【monner的一点小感想】
个人最近研究Lucene时候,发现其搜索语句的词法语法搞到挺复杂的。
其实搜索引擎作为普通大众的工具,更多的应该在其搜索的准确性和速度方面做功夫,
大多数人都是输入个关键字就完成了搜索,很少有人会采用极度复杂的搜索语句的。
lucene项目目前基本上已经成为了开源搜索引擎的事实上底层索引标准!而且其索引生成算法和索引文件(准确的说是
索引数据存储)的模式基本已经比较固定,且都是采用的理论上的所谓最佳算法,我看在目前基本框架不变的基础上,想带来全新的改观基本很难!也难怪lucene的版本虽然在不断更新,但是核心包的性能却是一直大同小异了。
提个问题:如果由你来主导lucene项目的进一步开发,你会从什么方面对lucene进行切入和改进呢?!
目前我个人看来,只所以lucene作为Apache的顶级项目,下面又陆陆续续分支裂变了很多子项目如nutch/solr..等等
我们就可以看出其中道理来,lucene核心并不是我们研究的主要方向,而且说真的其核心接口已经十分固定并很人性化,
就引入哪么几个对象就可以轻易实现调用lucene为我服务!我们更应该从外围入手,如何开发更精致灵巧的实用项目,充分发挥lucene-core这个基础包的利用价值!!!
比如参考nutch,做个小型网站搜索引擎
比如参考regain,做个个人桌面文档资料搜索
。。。。
其实类似的需求在如今数据暴涨爆炸的年代已经非常必要了!可是我发现如今软件界并没有认真开发出几个实用好用耐用,用了离开不了的个人文档数据管理软件(当然,所谓数据其实可以更广义一点,就是一切需要你用脑筋去记忆,花心思去整理去管理的电子数据)。
谁能给我推荐几个好用的个人电子数据管理软件???
【monner补充下相关知识】
如果您学过编译原理,那么应该不会对YACC和Lex之类的词法分析工具陌生
Lex 即 Lexical Analyzar。
Yacc即 Yet Another Compiler Compiler
lucene采用类似的JAVACC实现,下面摘录如下:
常见常规表达式语法和细节整理如下:
字符 | 含义 |
A-Z, 0-9, a-z | 构成了部分模式的字符和数字。 |
. | 匹配任意字符,除了 \n。 |
- | 用来指定范围。例如:A-Z 指从 A 到 Z 之间的所有字符。 |
[ ] | 一个字符集合。匹配括号内的 任意 字符。如果第一个字符是 ^ 那么它表示否定模式。例如: [abC] 匹配 a, b, 和 C中的任何一个。 |
* | 匹配 0个或者多个上述的模式。 |
+ | 匹配 1个或者多个上述模式。 |
? | 匹配 0个或1个上述模式。 |
$ | 作为模式的最后一个字符匹配一行的结尾。 |
{ } | 指出一个模式可能出现的次数。 例如: A{1,3} 表示 A 可能出现1次或3次。 |
\ | 用来转义元字符。同样用来覆盖字符在此表中定义的特殊意义,只取字符的本意。 |
^ | 否定。 |
| | 表达式间的逻辑或。 |
"<一些符号>" | 字符的字面含义。元字符具有。 |
/ | 向前匹配。如果在匹配的模版中的“/”后跟有后续表达式,只匹配模版中“/”前面的部分。如:如果输入 A01,那么在模版 A0/1 中的 A0 是匹配的。 |
( ) | 将一系列常规表达式分组。 |
常规表达式 | 含义 |
joke[rs] | 匹配 jokes 或 joker。 |
A{1,2}shis+ | 匹配 AAshis, Ashis, AAshi, Ashi。 |
(A[b-e])+ | 匹配在 A 出现位置后跟随的从 b 到 e 的所有字符中的 0 个或 1个。 |
Lex或者YACC 中的标记声明类似 C 中的变量名。每个标记都有一个相关的表达式
标记 | 相关表达式 | 含义 |
数字(number) | ([0-9])+ | 1个或多个数字 |
字符(chars) | [A-Za-z] | 任意字符 |
空格(blank) | " " | 一个空格 |
字(word) | (chars)+ | 1个或多个 chars |
变量(variable) | (字符)+(数字)*(字符)*(数字)* |
lucene 的Token部分描述采用的是通用的 巴科斯范式(BNF, Backus Naur Form)
如果您想对词法分析有所深入的话,强烈建议您学习下BNF!!!
备忘: Lucene中Query语法树的整理
========================================================================
【摘录原文章】
Lucene的查询解析代码为QueryParser.jj,语法为JavaCC实现的LL():
以下是Token部分:
_ESCAPED_CHAR:: = " \\ " [ " \\ " , " + " , " - " , " ! " , " ( " , " ) " , " : " , " ^ " , " [ " , " ] " , " \ "" , " { " , " } " , " ~ " , " * " , " ? " ] > //特殊字符,
_TERM_START_CHAR :: = ( ~ [ " " , " \t " , " \n " , " \r " , " + " , " - " , " ! " , " ( " , " ) " , " : " , " ^ " , " [ " , " ] " , " \ "" , " { " , " } " , " ~ " , " * " , " ? " ] //TERM的起始字符,除了列出的其它字符都可以
_TERM_CHAR:: = ( < _TERM_START_CHAR > | < _ESCAPED_CHAR > | " - " | " + " ) > // TERM可使用字符
_WHITESPACE:: = ( " " | " \t " | " \n " | " \r " ) // 空格和回车,
< DEFAULT > TOKEN:
AND:: = ( " AND " | " && " )
OR:: = ( " OR " | " || " )
NOT:: = ( " NOT " | " ! " )
PLUS:: = " + "
MINUS:: = " - "
LPAREN:: = " ( "
RPAREN:: = " ) "
COLON:: = " : "
STAR:: = " * "
CARAT:: = " ^ " // 后接Boost,原文<CARAT: "^" > : Boost,后面Boost说明什么没明白
QUOTED:: = " \ "" (~[ " \ "" ] | " \\\ "" )+ " \ "" // 表示用"包起来的字符串,字符"开始,中间由不是"的符号或者连着的这两个符号\"组成,字符"结束,
TERM:: =< _TERM_START_CHAR > ( < _TERM_CHAR > ) *
FUZZY_SLOP:: = " ~ " ( ( < _NUM_CHAR > ) + ( " . " ( < _NUM_CHAR > ) + ) ? ) ? // 字符~开始,而后是数字.Lucene支持模糊查询,例如"roam~"或"roam~0.8", The value is between 0 and 1,算法为the Levenshtein Distance, or Edit Distance algorithm
PREFIXTERM:: = ( < _TERM_START_CHAR > | " * " ) ( < _TERM_CHAR > ) * " * " > // 模糊查找,表示以某某开头的查询, 字符表示为"something*",前缀允许模糊符号*,中间可有字符也可没有, 结尾必须是*
WILDTERM:: = ( < _TERM_START_CHAR > | [ " * " , " ? " ]) ( < _TERM_CHAR > | ( [ " * " , " ? " ] )) * > // 类似上面,但同时支持?字符,结尾可以是字符也可以是* ?。使用[]表示or关系时,不需要使用|,只要,号分割即可
RANGEIN_START:: = " [ " // 在RangeQuery中,[或{表示了是否包含边界条件本身, 用字符表示为"[begin TO end]" 或者"{begin TO end}",后接RangeIn
RANGEEX_START:: = " { " // 同上,后接RangeEx
< Boost > TOKEN:
NUMBER:: = ( < _NUM_CHAR > ) + ( " . " ( < _NUM_CHAR > ) + ) ? // 后接DEFAULT, 整数或小数
< RangeIn > TOKEN:
RANGEIN_TO:: = " TO "
RANGEIN_END:: = " ] " // 后接DEFAULT, RangIn的结束
RANGEIN_QUOTED:: = " \ "" (~[ " \ "" ] | " \\\ "" )+ " \ "" // 同上述QUOTED,表示用"包起来的字符串,
RANGEIN_GOOP:: = ( ~ [ " " , " ] " ]) + // 1个或多个不是空格和]的符号,这样就能提取出[]中的内容
< RangeEx > TOKEN :
RANGEEX_TO:: = " TO " >
RANGEEX_END:: = " } " // 后接DEFAULT, RangeEx的结束
RANGEEX_QUOTED:: = " \ "" (~[ " \ "" ] | " \\\ "" )+ " \ "" // 同上述QUOTED,表示用"包起来的字符串,
RANGEEX_GOOP:: = ( ~ [ " " , " } " ]) + // 1个或多个不是空格和]的符号,这样就能提取出[]中的内容
< DEFAULT, RangeIn, RangeEx > SKIP : {
< < _WHITESPACE >>
} // 所有空格和回车被忽略
以下为解析部分
Modifiers:: = [ < PLUS > { ret = MOD_REQ; } | < MINUS > { ret = MOD_NOT; } | < NOT > { ret = MOD_NOT; } ] // + - !符号
Query:: = Modifiers Clause (Conjunction Modifiers Clause) *
Clause:: = [( < TERM > < COLON >|< STAR > < COLON > )] // btw:代码中LOOKAHEAD[2]表示使用LL(2)
(Term |< LPAREN > Query < RPAREN > ( < CARAT > < NUMBER > ) ? ) // 子句. ???????这儿语法有点,仿佛允许 *:(*:dog)这样的语法,很奇怪
Term:: = (
( < TERM >|< STAR >|< PREFIXTERM >|< WILDTERM >|< NUMBER > ) [ < FUZZY_SLOP > ] [ < CARAT >< NUMBER > [ < FUZZY_SLOP > ]}
| ( < RANGEIN_START > ( < RANGEIN_GOOP >|< RANGEIN_QUOTED > ) [ < RANGEIN_TO > ] ( < RANGEIN_GOOP >|< RANGEIN_QUOTED > < RANGEIN_END > ) [ < CARAT > boost =< NUMBER > ] // 这儿看出range必须同时有两端,不能只有有一端
| ( < RANGEEX_START > < RANGEEX_GOOP >|< RANGEEX_QUOTED > [ < RANGEEX_TO > ] < RANGEEX_GOOP >|< RANGEEX_QUOTED > < RANGEEX_END > )[ < CARAT > boost =< NUMBER > ] // 在RangeQuery中,[或{表示了是否包含边界条件本身, 用字符表示为"[begin TO end]" 或者"{begin TO end}",后接RangeIn
| < QUOTED > [ < FUZZY_SLOP > ] [ < CARAT > boost =< NUMBER > ] // 被""包含的内容