尽管lucene、solr如此的频繁、如此的普遍被使用。仍然有许多人、需要开发者所见即所得的方式,将查询用起来。一方面为了迎合传统sql用户的习惯,另一方面提升通用搜索产品的可用性,也丰富solr的功能接口。有必要实现
类sql的 solr查询实现。
社区总是无比的强大,总是只要你能想到的一定有人想到,只要你想到的一定有人已经尝试在实现,甚至已经有实现的初级版本,这个开头就给予了屌丝很好的指引。类sql的开源项目 solrflux就是这么一个开源项目。目前看,貌似没多少人关注这个。从注释和用例看,作者是日本友好人士。http://code.google.com/p/solrflux/
1. solrflux 用例体验
public class TestDemo {
public static void main(String[] args) throws IOException
{
//ConsoleReader conReader = new ConsoleReader();
String filedemo="testSql2Solr.txt"; // 改为从文件读取sql 语法
BufferedReader fileReader = new BufferedReader( new InputStreamReader(new FileInputStream(filedemo)));
EvalContext ctx = new EvalContext();
StringBuilder builder = new StringBuilder();
CharSequenceReader reader = new CharSequenceReader(builder);
final String NORMAL_PROMPT = "SQL>";
final String CONTD_PROMPT = "... ";
String prompt = NORMAL_PROMPT;
String targetUrl="http://localhost:8080/terminator-search/search4****-0";
String name ="conn"; //这个name对应testSql2Solr.txt 中的 信息,然后关联到targetUrl
UseStatement use = new UseStatement();
use.setUrl( targetUrl );
use.setName( name );
try {
use.execute(ctx);
} catch (Exception e) {
e.printStackTrace();
}
while (true) {
String line = fileReader.readLine();
//System.out.println("--"+line);
if ( line == null ) {
System.out.println("line is null so exit");
break;
}
// String line ="SELECT * FROM conn WHERE id="104614938"";
builder.append(line + "\n");
reader.reset();
try {
if ( tryEvaluate( reader, ctx ) ) {
prompt = NORMAL_PROMPT;
} else {
prompt = CONTD_PROMPT;
continue;
}
} catch (Exception e) {
e.printStackTrace();
prompt = NORMAL_PROMPT;
}finally{
}
builder.setLength(0);
builder.trimToSize();
}
fileReader.close();
}
private static boolean tryEvaluate(Reader reader, EvalContext ctx)
throws SyntaxException, EvalException, IOException
{
//String expr= null;
//ANTLRStringStream in = new ANTLRStringStream(expr);
ANTLRReaderStream stream = new ANTLRReaderStream( reader );
RecognizerSharedState state = new RecognizerSharedState();
SolrqlLexer lexer = new SolrqlLexer(stream, state);//词法分析器
CommonTokenStream tokenStream = new CommonTokenStream(lexer);//获取lexer构造的记号流
SolrqlParser parser = new SolrqlParser(tokenStream);//语法分析器
try {
stmt_list_return ret = parser.stmt_list();
if (ret != null) {
evaluate((CommonTree)ret.getTree(), ctx);
}
} catch (MismatchedTokenException e) {
if (e.getUnexpectedType() == SolrqlParser.EOF) {
// immature input. continue.
return false;
} else {
throw new SyntaxException(
lexer.getErrorMessage(e, SolrqlParser.tokenNames), e);
}
} catch (RecognitionException e) {
throw new SyntaxException(
lexer.getErrorMessage(e, SolrqlParser.tokenNames), e);
}
return true;
}
private static void evaluate(CommonTree t, EvalContext ctx)
throws EvalException, SyntaxException
{
printTree(t, 0);
List statements = new StatementListBuilder().build(t);
for (Statement stmt: statements) {
Object ret = stmt.execute(ctx);
if ( ret == null ) {
System.out.println("OK.");
continue;
}
if ( ret instanceof UpdateResponse ) {
printUpdateResponse((UpdateResponse)ret);
} else if ( ret instanceof SelectStatement.Result ) {
printSelectResult((SelectStatement.Result)ret);
} else {
System.out.println("Result: " + ret.toString());
}
}
}
private static void printSelectResult(SelectStatement.Result res)
{
int i=1;
for (SolrDocument doc: res.getDocuments()) {
System.out.print(Integer.toString(i) + ": ");
int j=0;
for (Map.Entry e: doc.entrySet()) {
if (j > 0) {
System.out.print(", ");
}
System.out.print(e.getKey() + "=" + e.getValue().toString());
++j;
}
System.out.println();
++i;
}
System.out.println();
System.out.println("Query=" + res.getQuery());
System.out.println(
"Found " + res.getResponse().getResults().getNumFound() +
" documents, Offset=" + res.getResponse().getResults().getStart()
);
System.out.println(
"Status=" + res.getResponse().getStatus() +
", Elapsed Time=" + res.getResponse().getElapsedTime()
);
}
private static void printUpdateResponse(UpdateResponse res)
{
System.out.println(
"Status=" + res.getStatus() +
", Elapsed Time=" + res.getElapsedTime()
);
}
private static void printTree(Tree t, int indent)
{
if (t != null) {
StringBuffer sb = new StringBuffer(indent);
for (int i = 0; i < indent; ++i) {
sb = sb.append(" ");
}
if (!t.isNil()) {
System.out.println(sb.toString() + t.toString());
++indent;
}
for (int i = 0; i < t.getChildCount(); ++i) {
printTree(t.getChild(i), indent);
}
}
}
}
-------------------------testSql2Solr.txt--------------
SELECT * FROM conn ;
SELECT * FROM conn WHERE id="1581";
SELECT * FROM conn WHERE id!="1581";
SELECT id,nick FROM conn WHERE 0<2000;
SELECT * FROM conn NWHERE "se_isv_type:4 AND isv_id:[* TO 2000]";
SELECT SUBSTRING(description, 0, 3) AS first_three_letters From conn WHERE description="sdf";
SELECT * FROM conn WHERE id!="1581" ORDER BY score DESC;
SELECT * FROM conn WHERE id!="1581" ORDER BY score DESC limit 10 offset 10;
SELECT SUBSTRING(description, 0, 4) AS first_fouth_letters From conn WHERE description="sdfs";
---------------------------------------------------------------
说明,其中conn 对应name,id|nick|isv_id| description 对应schema中的域
limit ~ rows
offset ~ start
solrflux 文法-语法-抽象语法树参考 grammar/Solral.g,
可以实现学习下 antlr 基本原理
http://www.ibm.com/developerworks/cn/java/j-lo-antlr/index.html?ca=drs-
然后 下载 antlrwork 对句法、文法等编辑、测试
http://openjdk.java.net/projects/compiler-grammar/antlrworks/index.html