Lucene Query Parser

Lucene Query Parser


翻译这篇文章的初衷是希望能更系统的理解Lucene的用法,同时试试自己的翻译水平:)


原文:http://jakarta.apache.org/lucene/docs/queryparsersyntax.html


概述


虽然Lucene提供的API允许你创建你自己的Query(查询语句),但它同时也通过Query Parser(查询分析器)提供了丰富的查询语言。


这个页面提供的是Lucene的Query Parser的语法介绍:一个可通过用JavaCC把一个字符串解释成为Lucene的查询语句的规则。


在选择使用被提供的Query Parser前,请考虑一下几点:


1、如果你是通过编写程序生成一个查询语句,然后通过Query Parser分析,那么你需要认真的考虑是否该直接利用Query的API构造你的查询。换句话说,Query Parser是为那些人工输入的文本所设计的,而不是为了程序生成的文本。


2、未分词的字段最好直接加到Query中,而不要通过Query Parser。如果一个字段的值是由程序生成的,那么需要为这个字段生成一个Query Clause(查询子句)。Query Parser所用的Analyzer是为转换人工输入的文本为分词的。而程序生成的值,比如日期、关键字等,一般都由程序直接生成(?)。


3、在一个查询表单里,通常是文本的字段应该使用Query Parser。所有其他的,比如日期范围、关键字等等,最好是通过Query API直接加入到Query中。一个有有限个值的字段,比如通过下拉表单定义的那些,不应该被加到查询字串中(后面会分析到),而应该被添加为一个TermQuery子句。


分词


一个查询语句是有分词和操作符组成的。这里有两种类型的:单个的分词和短语。


一个单一分词就是一个简单的单词,比如”test”或”hello”。


一个短语就是一组被双引号包括的单词,比如”hello dolly”。


多个分词可以用布尔操作符组合起来形成一个更复杂的查询语句(下面会详细介绍)。


注意:用于建立索引的分析器(Analyzer)将被用于解释查询语句中的分词和短语。因此,合理的选择一个分析器是很重要的,当然这不会影响你在查询语句中使用的分词。


字段


Lucene支持字段数据。当执行一个搜索时,你可以指定一个字段,或者使用默认的字段。字段的名字和默认的字段是取决于实现细节的。


你可以搜索任何字段,做法是输入字段名称,结尾跟上一个冒号 “:” , 然后输入你想查找的分词。


举个例子,让我们假设Lucene的索引包含两个字段,标题和正文,正文是默认字段。如果你想标题为 “The Right Way” 并且正文包含文本 “don’t go this way”的记录的话,你可以输入:


  title:”The Right Way” AND text:go 


或者


  title:”Do it right” AND right


由于正文是默认字段,所以字段指示就没有必要了。


注意:字段的搜索值只能是紧跟在冒号后的一个分词,所以搜索字串:


 title:Do it right


只会找到标题含有 “Do” 的记录。它会在默认字段(这里是正文字段)里查找 “it” 和 “right”。


分词修饰语


Lucene支持对查询分词做修饰以提供一个更广的查询选项。
 
通配符搜索


ucene支持一个或多个字符的通配符搜索。


执行一个字符的通配符搜索可以用 “?”,比如你要搜索 “text” or “test” ,你可以用:


 te?t


执行多个字符的通配符搜索可以用 “*”,比如,搜索 “test” , “tests” 或 “tester” ,你可以用


 test*


你也可以把通配符放在分词中将进行搜索,比如


 te*t


注意:不可以使用 * 或 ? 作为搜索字串的第一个支付。


模糊搜索


Lucene支持基于Levenshtein Distance(一种字符串相似程度算法)或Edit Distance(一种字符串相似程度算法)算法的模糊查询。要执行模糊查询需要用到 “~” 符号,紧随单个分词。比如,用模糊查询搜索一个拼写上和 “roam” 类似的内容:


 roam~


这个搜索可以搜索到像 foam 和 roams 这样的分词。


 “jakarta apache”~10


 近似搜索


Lucene支持根据一个指定的近似程度查找。与模糊搜索类似,在短语的末尾使用 “~” 。


范围搜索


范围搜索可以搜索到那些列的值在被范围搜索所指定的上限和下限之间的记录。范围搜索可以包括或不包括上限和下限。如果不是日期类型的字段,会根据字典的排序来决定上下限。


 mod_date:[20020101 TO 20030101]


这个会搜索 mod_date 字段的值在 20020101 和 20030101 之间的,包括上下限。注意,范围搜索不仅仅是为日期字段准备的,你也可以在非日期的字段上使用它:


 title:{Aida TO Carmen}


这个会搜索 title 字段的值在 Aida 和 Carmen (根据字典的顺序) 的记录,不包括 Aida 和 Carmen 本身。


包含的范围查询是用方括号指定的 “[” 、 “]” ,而不包含的范围查询是有波形括号指定的 “{” 、 “}”。


提高一个分词的相似度


Lucene提供基于被搜索的分词的匹配文档的相似度这一概念。为了提高一个分词的相似度,可以使用 “^”和一个相似因子(一个数字)跟在需要搜索的分词后面。这个匹配因子越高,这个分词所获得相似度就越高。


提高匹配允许你通过提高分词的相似程度来控制一个文档的相似程度。比如,如果你在搜索


 jakarta apache


并且,你希望 “jakarta” 有更高的相似程度,就用让 “^” 和一个匹配因子紧随它。你将输入:


 jakarta^4 apache


这样会使包含分词 jakarta 的文档有更高的相似度(Lucene默认是按照相似度对搜索结果排序的,所以带来的直接影响就是该搜索结果文档排位靠前)。你也可以对短语分词提高匹配度,比如:


 “jakarta apache”^4 “jakarta lucene”


默认的情况下,匹配因子是1。虽然匹配因子必须是正数,当它可以小于1 (比如 0.2)


布尔操作符


布尔操作符允许分词通过逻辑操作符被组合。Lucene支持 AND, “+”, OR, NOT and “-” 作为布尔操作符(注意:布尔操作符必须全部大写)。


OR


OR操作符是默认的连接操作。这意味着如果没有布尔值在两个分词之间的话,OR操作符将被使用。OR操作符连接两个分词,并且搜索包含任意一个分词的文档。这等价于与集合概念中的并集。 “||” 可以替代 OR。


搜索包含 “jakarta apache” 或者 只是 “jakarta” 的文档使用查询语句:


“jakarta apache” jakarta


或者


 “jakarta apache” OR jakarta


AND


AND操作符会匹配那些包含所有分词的文档。这等价与集合概念中的交集。”&&” 可以替代 AND。


搜索既包含 “jakarta apache” 又包含 “jakarta” 的文档使用查询语句:


 “jakarta apache” AND “jakarta lucene”
 
+作为必需操作符,需要 “+” 后的分词必须存在于被搜索文档的某个字段中。


搜索必须含有 “jakarta” 同时可能含有 “lucene” 的文档使用查询语句:


 +jakarta apache


NOT


NOT操作符会排除包含NOT后的分词的文档。这等价于集合概念中的补集。”!” 可以替代 NOT。


搜索包含”jakarta apache” 但不包含 “jakarta lucene”的文档使用查询语句:


 “jakarta apache” NOT “jakarta lucene”


注意:NOT操作符不能只和一个分词一起使用,下面这个查询不会返回任何结果:


 NOT “jakarta apache”


“-“作为禁止操作符将不匹配包含”-“所跟随的分词的文档。


搜索包含”jakarta apache” 而不包含 “jakarta lucene” 使用查询语句:


 “jakarta apache” -“jakarta lucene”
 
分组


Lucene支付使用圆括号把子句分组形成子查询。如果你想控制一个查询语句的布尔逻辑,那么这个将非常有用。


查找包含 “jakarta” 或者 “apache” , 同时包含 “website” 使用查询语句:


 (jakarta OR apache) AND website


这个将会排除任何混淆,并确定被搜索的文档必须包括 website 这个分词并且包括 jakarta 或者 apache 中的一个。
 
字段分组


Lucene支持针对单一字段使用圆括号对多个子句分组。


要搜索一个标题既包含分词”return” , 又包含短语 “pink panther” 使用查询语句:


特殊字符转换


作为查询语法的一部分,Lucene支持特殊字符。当前特殊支付包括:


+ – && || ! ( ) { } [ ] ^ ” ~ * ? : \


要转化一个特殊字符,就在要转化的字符前加上 “\” , 例如,搜索 (1+1):2 使用查询语句:


 \(1\+1\)\:2


原文:http://jakarta.apache.org/lucene/docs/queryparsersyntax.html


lucene简单实例<二>

 写文章的时候,感觉比较难写的就是标题,有时候不知道起什么名字好,反正这里写的都是关于lucene的一些简单的实例,就随便起啦.

Lucene 其实很简单的,它最主要就是做两件事:建立索引和进行搜索
来看一些在lucene中使用的术语,这里并不打算作详细的介绍,只是点一下而已—-因为这一个世界有一种好东西,叫搜索。

IndexWriter:lucene中最重要的的类之一,它主要是用来将文档加入索引,同时控制索引过程中的一些参数使用。

Analyzer:分析器,主要用于分析搜索引擎遇到的各种文本。常用的有StandardAnalyzer分析器,StopAnalyzer分析器,WhitespaceAnalyzer分析器等。

Directory:索引存放的位置;lucene提供了两种索引存放的位置,一种是磁盘,一种是内存。一般情况将索引放在磁盘上;相应地lucene提供了FSDirectory和RAMDirectory两个类。

Document:文档;Document相当于一个要进行索引的单元,任何可以想要被索引的文件都必须转化为Document对象才能进行索引。

Field:字段。

IndexSearcher:是lucene中最基本的检索工具,所有的检索都会用到IndexSearcher工具;

Query:查询,lucene中支持模糊查询,语义查询,短语查询,组合查询等等,如有TermQuery,BooleanQuery,RangeQuery,WildcardQuery等一些类。

QueryParser: 是一个解析用户输入的工具,可以通过扫描用户输入的字符串,生成Query对象。

Hits:在搜索完成之后,需要把搜索结果返回并显示给用户,只有这样才算是完成搜索的目的。在lucene中,搜索的结果的集合是用Hits类的实例来表示的。

上面作了一大堆名词解释,下面就看几个简单的实例吧:
1、简单的的StandardAnalyzer测试例子



Java代码


  1. package lighter.javaeye.com;  

  2.   

  3. import java.io.IOException;  

  4. import java.io.StringReader;  

  5.   

  6. import org.apache.lucene.analysis.Analyzer;  

  7. import org.apache.lucene.analysis.Token;  

  8. import org.apache.lucene.analysis.TokenStream;  

  9. import org.apache.lucene.analysis.standard.StandardAnalyzer;  

  10.   

  11. public class StandardAnalyzerTest   

  12. {  

  13.     //构造函数,  

  14.     public StandardAnalyzerTest()  

  15.     {  

  16.     }  

  17.     public static void main(String[] args)   

  18.     {  

  19.         //生成一个StandardAnalyzer对象  

  20.         Analyzer aAnalyzer = new StandardAnalyzer();  

  21.         //测试字符串  

  22.         StringReader sr = new StringReader(“lighter javaeye com is the are on”);  

  23.         //生成TokenStream对象  

  24.         TokenStream ts = aAnalyzer.tokenStream(“name”, sr);   

  25.         try {  

  26.             int i=0;  

  27.             Token t = ts.next();  

  28.             while(t!=null)  

  29.             {  

  30.                 //辅助输出时显示行号  

  31.                 i++;  

  32.                 //输出处理后的字符  

  33.                 System.out.println(“第”+i+“行:”+t.termText());  

  34.                 //取得下一个字符  

  35.                 t=ts.next();  

  36.             }  

  37.         } catch (IOException e) {  

  38.             e.printStackTrace();  

  39.         }  

  40.     }  

  41. }  

显示结果:

引用

第1行:lighter
第2行:javaeye
第3行:com

提示一下:
StandardAnalyzer是lucene中内置的”标准分析器”,可以做如下功能:
1、对原有句子按照空格进行了分词
2、所有的大写字母都可以能转换为小写的字母
3、可以去掉一些没有用处的单词,例如”is”,”the”,”are”等单词,也删除了所有的标点
查看一下结果与”new StringReader(“lighter javaeye com is the are on”)”作一个比较就清楚明了。
这里不对其API进行解释了,具体见lucene的官方文档。需要注意一点,这里的代码使用的是lucene2的API,与1.43版有一些明显的差别。

2、看另一个实例,简单地建立索引,进行搜索



Java代码


  1. package lighter.javaeye.com;  

  2. import org.apache.lucene.analysis.standard.StandardAnalyzer;  

  3. import org.apache.lucene.document.Document;  

  4. import org.apache.lucene.document.Field;  

  5. import org.apache.lucene.index.IndexWriter;  

  6. import org.apache.lucene.queryParser.QueryParser;  

  7. import org.apache.lucene.search.Hits;  

  8. import org.apache.lucene.search.IndexSearcher;  

  9. import org.apache.lucene.search.Query;  

  10. import org.apache.lucene.store.FSDirectory;  

  11.   

  12. public class FSDirectoryTest {  

  13.   

  14.     //建立索引的路径  

  15.     public static final String path = “c:\\index2”;  

  16.   

  17.     public static void main(String[] args) throws Exception {  

  18.         Document doc1 = new Document();  

  19.         doc1.add( new Field(“name”“lighter javaeye com”,Field.Store.YES,Field.Index.TOKENIZED));  

  20.   

  21.         Document doc2 = new Document();  

  22.         doc2.add(new Field(“name”“lighter blog”,Field.Store.YES,Field.Index.TOKENIZED));  

  23.   

  24.         IndexWriter writer = new IndexWriter(FSDirectory.getDirectory(path, true), new StandardAnalyzer(), true);  

  25.         writer.setMaxFieldLength(3);  

  26.         writer.addDocument(doc1);  

  27.         writer.setMaxFieldLength(3);  

  28.         writer.addDocument(doc2);  

  29.         writer.close();  

  30.   

  31.         IndexSearcher searcher = new IndexSearcher(path);  

  32.         Hits hits = null;  

  33.         Query query = null;  

  34.         QueryParser qp = new QueryParser(“name”,new StandardAnalyzer());  

  35.           

  36.         query = qp.parse(“lighter”);  

  37.         hits = searcher.search(query);  

  38.         System.out.println(“查找\”lighter\” 共” + hits.length() + “个结果”);  

  39.   

  40.         query = qp.parse(“javaeye”);  

  41.         hits = searcher.search(query);  

  42.         System.out.println(“查找\”javaeye\” 共” + hits.length() + “个结果”);  

  43.   

  44.     }  

  45.   

  46. }  

运行结果:



Java代码


  1. 查找“lighter” 共2个结果  

  2. 查找“javaeye” 共1个结果  

lucene的简单实例<一>

 说明一下,这一篇文章的用到的lucene,是用2.0版本的,主要在查询的时候2.0版本的lucene与以前的版本有了一些区别.
其实这一些代码都是早几个月写的,自己很懒,所以到今天才写到自己的博客上,高深的文章自己写不了,只能记录下一些简单的记录与点滴,其中的代码算是自娱自乐的,希望高手不要把重构之类的砸下来…

1、在windows系统下的的C盘,建一个名叫s的文件夹,在该文件夹里面随便建三个txt文件,随便起名啦,就叫”1.txt”,”2.txt”和”3.txt”啦
其中1.txt的内容如下:



Java代码


  1. 中华人民共和国  

  2. 全国人民  

  3. 2006年  

而”2.txt”和”3.txt”的内容也可以随便写几写,这里懒写,就复制一个和1.txt文件的内容一样吧

2、下载lucene包,放在classpath路径中
建立索引:



Java代码


  1. package lighter.javaeye.com;  

  2.   

  3. import java.io.BufferedReader;  

  4. import java.io.File;  

  5. import java.io.FileInputStream;  

  6. import java.io.IOException;  

  7. import java.io.InputStreamReader;  

  8. import java.util.Date;  

  9.   

  10. import org.apache.lucene.analysis.Analyzer;  

  11. import org.apache.lucene.analysis.standard.StandardAnalyzer;  

  12. import org.apache.lucene.document.Document;  

  13. import org.apache.lucene.document.Field;  

  14. import org.apache.lucene.index.IndexWriter;  

  15.   

  16. /** 

  17.  * author lighter date 2006-8-7 

  18.  */  

  19. public class TextFileIndexer {  

  20.     public static void main(String[] args) throws Exception {  

  21.         /* 指明要索引文件夹的位置,这里是C盘的S文件夹下 */  

  22.         File fileDir = new File(“c:\\s”);  

  23.   

  24.         /* 这里放索引文件的位置 */  

  25.         File indexDir = new File(“c:\\index”);  

  26.         Analyzer luceneAnalyzer = new StandardAnalyzer();  

  27.         IndexWriter indexWriter = new IndexWriter(indexDir, luceneAnalyzer,  

  28.                 true);  

  29.         File[] textFiles = fileDir.listFiles();  

  30.         long startTime = new Date().getTime();  

  31.           

  32.         //增加document到索引去  

  33.         for (int i = 0; i < textFiles.length; i++) {  

  34.             if (textFiles[i].isFile()  

  35.                     && textFiles[i].getName().endsWith(“.txt”)) {  

  36.                 System.out.println(“File “ + textFiles[i].getCanonicalPath()  

  37.                         + “正在被索引….”);  

  38.                 String temp = FileReaderAll(textFiles[i].getCanonicalPath(),  

  39.                         “GBK”);  

  40.                 System.out.println(temp);  

  41.                 Document document = new Document();  

  42.                 Field FieldPath = new Field(“path”, textFiles[i].getPath(),  

  43.                         Field.Store.YES, Field.Index.NO);  

  44.                 Field FieldBody = new Field(“body”, temp, Field.Store.YES,  

  45.                         Field.Index.TOKENIZED,  

  46.                         Field.TermVector.WITH_POSITIONS_OFFSETS);  

  47.                 document.add(FieldPath);  

  48.                 document.add(FieldBody);  

  49.                 indexWriter.addDocument(document);  

  50.             }  

  51.         }  

  52.         //optimize()方法是对索引进行优化  

  53.         indexWriter.optimize();  

  54.         indexWriter.close();  

  55.           

  56.         //测试一下索引的时间  

  57.         long endTime = new Date().getTime();  

  58.         System.out  

  59.                 .println(“这花费了”  

  60.                         + (endTime – startTime)  

  61.                         + ” 毫秒来把文档增加到索引里面去!”  

  62.                         + fileDir.getPath());  

  63.     }  

  64.   

  65.     public static String FileReaderAll(String FileName, String charset)  

  66.             throws IOException {  

  67.         BufferedReader reader = new BufferedReader(new InputStreamReader(  

  68.                 new FileInputStream(FileName), charset));  

  69.         String line = new String();  

  70.         String temp = new String();  

  71.           

  72.         while ((line = reader.readLine()) != null) {  

  73.             temp += line;  

  74.         }  

  75.         reader.close();  

  76.         return temp;  

  77.     }  

  78. }  


索引的结果:



Java代码


  1. File C:\s\1.txt正在被索引….  

  2. 中华人民共和国全国人民2006年  

  3. File C:\s\2.txt正在被索引….  

  4. 中华人民共和国全国人民2006年  

  5. File C:\s\3.txt正在被索引….  

  6. 中华人民共和国全国人民2006年  

  7. 这花费了297 毫秒来把文档增加到索引里面去!c:\s  


3、建立了索引之后,查询啦….



Java代码


  1. package lighter.javaeye.com;  

  2.   

  3. import java.io.IOException;  

  4.   

  5. import org.apache.lucene.analysis.Analyzer;  

  6. import org.apache.lucene.analysis.standard.StandardAnalyzer;  

  7. import org.apache.lucene.queryParser.ParseException;  

  8. import org.apache.lucene.queryParser.QueryParser;  

  9. import org.apache.lucene.search.Hits;  

  10. import org.apache.lucene.search.IndexSearcher;  

  11. import org.apache.lucene.search.Query;  

  12.   

  13. public class TestQuery {  

  14.     public static void main(String[] args) throws IOException, ParseException {  

  15.         Hits hits = null;  

  16.         String queryString = “中华”;  

  17.         Query query = null;  

  18.         IndexSearcher searcher = new IndexSearcher(“c:\\index”);  

  19.   

  20.         Analyzer analyzer = new StandardAnalyzer();  

  21.         try {  

  22.             QueryParser qp = new QueryParser(“body”, analyzer);  

  23.             query = qp.parse(queryString);  

  24.         } catch (ParseException e) {  

  25.         }  

  26.         if (searcher != null) {  

  27.             hits = searcher.search(query);  

  28.             if (hits.length() > 0) {  

  29.                 System.out.println(“找到:” + hits.length() + ” 个结果!”);  

  30.             }  

  31.         }  

  32.     }  

  33.   

  34. }  


其运行结果:

引用

找到:3 个结果!

具体的API的用法,这里就不说了,具体的做法参考lucene的官方文档吧…