Lucene学习笔记(四)

来源:互联网 发布:手机外汇看盘软件 编辑:程序博客网 时间:2024/06/08 09:56

 

** Lucene技术拓展: Lucene与数据库结合的建议:(引用自下边的2.2标题中的内容)

     比较好的一种方式是让Lucene和数据库结合使用,在索引中存入一些关键性的数据,

     如数据库表中数据的ID字段、路径字段或者简单文本。而真正的数据提取则从数据库中得到

     (就是根据据搜索出来的Document文档中保存的表数据的类似ID字段的数据到数据库中取出真正的数据),

     这样既可以发挥Lucene的作用,有分担了服务器性能的压力。

 

 四、Lucene搜索:
                           大纲:
                                   1. Lucene的搜索流程
                                   2. Lucene搜索与结果的表示
                                   3. Lucene的评分机制
                                   4. 构建Lucene的各种Query
                                   5. Lucene的QueryParser类
      
  1. Lucene的搜索流程
  
   1.1 初始化Lucene的检索工具类----IndexSearch类:是Lucene中最基本的检索工具,所有的检索都会用到IndexSearch检索工具,
   但是使用它之前还要做一些准备工作,即对IndexReader进行初始化(需要传入一个保存索引文件的目录参数到构造方法,具体见后面)。
   
   1.2 构建Query:就是值对于需要查询的Field字段采用什么样的方式进行查询(可以任务是创建一个具体的查询条件对象),
   如模糊查询、语义查询、短语查询、范围查询、组合查询等。正是因为Query的存在,Lucene才有了丰富的查询语言。
   
    1.2.1 使用:在使用Query之前,首先要创建一个Query的实例对象,
    Lucene支持直接创建Query对象,也可以通过QueryParser.parse()方法返回一个Query对象。
   
   1.3 搜索并处理返回结果:构建完成Query后,就可以将它作为IndexSearch.search()方法的参数进行查询了,将会返回结果集。
   具体的操作见下边的小节。
   
  2. 搜索与结果:下边介绍如何搜索和处理结果集。
  
   2.1 检索工具----IndexSearch类(搜索器):该类继承子Search基类。该类的构造方法可以自己看Javadoc文档。
   我们会发现IndexSearch提供的四种构造方法的最终目的就是用一个IndexReader对象作为一个索引目录的读取器,
   还可以要传入一个boolean值,用来判断是否在关闭IndexSearch的时候也同时关闭传入的IndexReader对象。
   初始化工作完成之后,还要创建一个Query对象才能真正的进行搜索,这里我们只关注IndexSearch对象的搜索功能,
   至于Query我们后边标题中会讲到。
   
   例子:
    /*
    构建5个Document文档,每个含有两个Field字段
    其中title字段表示文档名称
    其中name字段表示文字数据
    */
    Document doc1 = new Document();
    doc1.add(Field.Keyword("title", doc1));
    doc1.add(Field.Text("name", "word1 word2 word3"));
    
    Document doc2 = new Document();
    doc2.add(Field.Keyword("title", doc2));
    doc2.add(Field.Text("name", "word4 word5 word6"));
    
    Document doc3 = new Document();
    doc3.add(Field.Keyword("title", doc3));
    doc3.add(Field.Text("name", "word1 word4"));
    
    Document doc4 = new Document();
    doc4.add(Field.Keyword("title", doc4));
    doc4.add(Field.Text("name", "word2 word5"));
    
    Document doc5 = new Document();
    doc5.add(Field.Keyword("title", doc5));
    doc5.add(Field.Text("name", "word3 word6"));
    
    //生成一个索引写入器
    IndexWriter writer = new IndexWriter("C://lucene_index", new StandardAnalyzer(), true);
    //依次将创建的Document文档
    writer.addDocument(doc1);
    writer.addDocument(doc2);
    writer.addDocument(doc3);
    writer.addDocument(doc4);
    writer.addDocument(doc5);
    writer.close();
    
    //生成Query对象
    Query query = null;
    //生成结果集
    Hits hits = null;
    //创建搜索关键字
    String key1 = "word1";
    String key2 = "word2";
    String key3 = "word3";
    String key4 = "word4";
    String key5 = "word5";
    String key6 = "word6";
    //初始化IndexSearch搜索器对象
    IndexSearch search = new IndexSearch("C://lucene_index");
    //为Query赋值:传递搜索关键字、要查询的字段、分析器
    query = QueryParser.parse(key1, "name", new StandardAnalyzer());
    //得到结果集
    hits = search.search(query);
    //打印结果
    printResult(hits, key1);
    
    query = QueryParser.parse(key2, "name", new StandardAnalyzer());
    hits = search.search(query);
    printResult(hits, key2);
    
    query = QueryParser.parse(key3, "name", new StandardAnalyzer());
    hits = search.search(query);
    printResult(hits, key3);
    
    query = QueryParser.parse(key4, "name", new StandardAnalyzer());
    hits = search.search(query);
    printResult(hits, key4);
    
    query = QueryParser.parse(key5, "name", new StandardAnalyzer());
    hits = search.search(query);
    printResult(hits, key5);
    
    query = QueryParser.parse(key6, "name", new StandardAnalyzer());
    hits = search.search(query);
    printResult(hits, key6);
    
    //关闭IndexSearch对象
    search.close();
    
    //打印结果
    public void printResult(Hits hits, String key){
     System.out.println("查找关键字: " + key);
     if(hits != null && hits.length() != 0){
      System.out.println("找到文档:");
      for(int i = 0; i < hits.length(); i++){
       //得到结果集中的文档对象
       Document doc = hits.doc(i);
       String docName = doc.get("title");
       System.out.println(docName);
      }
     }else{
      System.out.println("没有任何搜索结果!");
     }
     //加两个换行
     System.out.println();
     System.out.println();
    }
    
   ** 注意:search.close()方法其实关闭的并不是IndexSearch对象,
   而关闭的是IndexSearch内部封装的IndexReader流对象。
   
   ** 注意:IndexSearch类中有很多search()方法的重载,其中可以传入一些辅助参数,可以对查询结果进行处理,
   主要是为了对搜索结果的排序、过滤等功能而设置的,这些具体的参数讲解将会在下一章高级部分讲解。
   
   2.2 检索结果----Hits类:搜索完成之后,就要把搜索结果返回给用户进行显示。
   在Lucene中的结果集是用Hits类的对象来表示的。我们可以查看Javadoc来查看Hits提供的方法。
   
   ** 注意:如果一个结果集含有100000条记录,而hits对象一次性就把结果集返回,那么此时hits结果集中的内容会有所不同。
   即它不会将所有的结果都返回,而是采用一种类似Hibernate懒加载的懒惰(Lazy)的方式来返回结果,当用户要访问某个文档结果时,
   hits对象在内部会对Lucene的索引再一次进行检索,返回一个对应的最新的索引文档结果。《具体原理可以查看源代码》
   
   ** 建议:比较好的一种方式是让Lucene和数据库结合使用,在索引中存入一些关键性的数据,
   如数据库表中数据的ID字段、路径字段或者简单文本。而真正的数据提取则从数据库中得到
   (就是根据据搜索出来的Document文档中保存的表数据的类似ID字段的数据到数据库中取出真正的数据),
   这样既可以发挥Lucene的作用,有分担了服务器性能的压力。
   
  3. Lucene的评分机制:我们可以通过Hits的score(int)方法得到对应位置的Document的得分,下边我们就来了解一下得分的意义吧!
  
   3.1 理解评分的概念:通常情况下,当用户输入一个关键字,搜索引擎接收到关键字信息,然后即可分析最后进行搜索。
   这里主要要说的就是得到的搜索结果,如果结果数据比较多,那么结果需要按照一定的顺序返回给用户。
   因此Lucene引入了评分机制,对检索结果中的每个Document文档都按照某种Lucene中规定好的标准进行评估打分,
   然后按照分值的高低来对结果进行排序并返回给用户。
   
   ** 注意:对于一个商用的搜索引擎来说,评分机制是其收入来源的重要部分。例如某公司向搜索引擎缴纳一定的费用,
   要求该搜索引擎将搜索结果中有关它公司的部分分值加大 ,以便在用户搜索有关信息的时候能使该公司的结果处于更靠前的位置。
   
   3.2 Lucene的评分算法:下边介绍Lucene到底是怎么确定各个Document评分值!
   Document文档的得分是在用户进行检索时(或说成得到搜索结果的同时)实时计算出来的。而不是在建立索引时确定的Document的得分,
   如果是建立索引时确定的得分,那么不管输入什么关键字进行搜索,那么建立索引时得分最高的永远会排在第一位,所以明显不合理。
   因此,所有Document文档的得分应当和用户输入的关键有关系,
   根据关键字和Document文档中查找到的匹配上的信息进行实时的运算得到的结果。
   所谓的得分可以认为成搜索关键字在Document文档中出现的频率,可认为出现频率越高得分越多。
   《有一个得分计算公式,可以到Lucene书的260页查看,不太重要,知道上边的原理就行了》
   
   3.3 改变Document文档的得分:我们可以手动干预Document文档的得分。通过Document.setBoost()方法来修改得分。
   其实就是改变Document的boost属性值,让原文档得分乘以boost属性,所以最后得分会跟Document.setBoost()设置的值十分相近,
   所以就可以认为最终值就是setBoost()设置的值。
   
   例如: 
    //创建一个手动指定分值的Document
    Document doc = new Document();
    doc.add(Field.Text("name", "word"));
    //设置得分值,因为接收的是float类型的所以要加"f"标识符
    doc.setBoost(0.1f);
    
    //创建索引写入器,然后保存索引到指定的磁盘目录
    ......
    //进行搜索
    ......
    
    ** 结果:从结果可以看到分值的显示
    
   ** 注意:上面这种手动设置评分值的方式仍然不是一种理想的方式,
   因为在建立索引时还需要人为的指定每个Document文档的boost值。《有关排序的更高级话题,后面会讲到》
   
  4. 构建各种Query查询对象:搜索流程中第二步就是构建一个Query对象。
  当用户输入一个关键字后,Lucene不能直接使用关键字字符串进行搜索,
  而是要将字符串分析和处理之后将关键字转换成Lucene指定的类型(Query)后才能真正的进行搜索,
  要转换成的格式就是Query类型的对象,使用Query对象的好处不单单提高了检索的效率,同时能检索出更加有效的结果。
  
  ** 注意:Query是一个抽象类,所以我们需要学习一下它的子类,下边共介绍了八个子类的使用方法。
  
   4.1 按分词搜索----TermQuery类:使用TermQuery类搜索可以认为是"分词搜索"。
   在搜索引擎中最基本的搜索就是在索引中搜索某一分词
   (就是建立索引是把Document文档中的Field字段中保存的数据进行分词后产生的词条数据信息),
   TermQuery就是可以完成这样功能的一个类。
   
    4.1.1 分词Term概念:前边我们已经提到了这个概念,这里重复叙述一下。
    在Lucene中分词是最基本的搜索单位。从本质上来讲一个分词其实就是一个名/值对。
    "名"是Field字段名,
    "值"是对应"名"的字段中所有分词(创建索引的时候字段中的数据将会被分词器进行分词)的某一个。
 
    4.1.2 构造TermQuery对象:首先要构造Term对象,然后将该对象作为构造TermQuery对象的参数。
    如:
     Term term = new Term("name", "word");
     Query query = new TermQuery(term);
    这样如果使用该query对象进行搜索的话,那么所有的以"name"为字段名,
    并且字段中包含"word"分词的Document文档,都将会作为查询结果返回给用户。
    
    ** 注意:我们之前搜索的时候都要传第一个和创建索引时所用的分词器对象相同的对象,但是这里没有用到分词器,
    这是因为之前我们通过QueryParser.parse("word", "name", new StandardAnalyzer());创建Query时,传入的就是搜索关键字,
    而不是把它作为分词传递过来进行搜索,所以要先对该关键字进行一下分词。
    但是现在我们构造Term对象的时候传入的"值"是用来作为分词进行查询的,虽然传入的不一定是分词,
    但是我们在代码中的意思就是把它作为了分词,所以不需要再用分词器进行分词处理了。
    
   4.2 "与或"搜索----BooleanQuery类:它也是实际开发过程中经常使用的一个Query。
   其实它是一个组合的Query类,一般不是单独使用,
   换句话说就是在使用时可以把各种Query的子类对象添加到该BooleanQuery对象中并标明添加到
   该BooleanQuery对象中的这些Query条件之间的逻辑关系,就相当于sql中的and、or这样的连接条件的逻辑关系表达式。
   
   ** 注意:BooleanQuery是可以嵌套的,一个BooleanQuery可以成为另一个BooleanQuery的条件子句。
   例子:
    //创建三个Document
    Document doc1 = new Document();
    doc1.add(Field.Text("name", "word1 word2 word3"));
    doc1.add(Field.Keyword("title", "doc1"));
    
    Document doc2 = new Document();
    doc2.add(Field.Text("name", "word1 word4 word5"));
    doc2.add(Field.Keyword("title", "doc2"));
    
    Document doc3 = new Document();
    doc3.add(Field.Text("name", "word1 word2 word6"));
    doc3.add(Field.Keyword("title", "doc3"));
    //创建索引写入器
    IndexWriter writer = new IndexWriter("C://lucene_index", new StandardAnalyzer(), true);
    //添加Document文档并创建索引
    writer.addDocument(doc1);
    writer.addDocument(doc2);
    writer.addDocument(doc3);
    writer.close();
    
    //开始搜索,创建搜索器
    IndexSearch search = new IndexSearch("C://lucene_index");
    //创建两个Query对象
    Query query1 = new TermQuery(new Term("name", "word1"));
    Query query2 = new TermQuery(new Term("name", "word2"));
    //创建复合条件Query对象:BooleanQuery对象,产生"与"关系效果
    Query query = new BooleanQuery();
    query.add(query1, true, false);
    query.add(query2, true, false);
    //得到结果
    Hits hits = search.search(query);
    //打印结果,使用上边的打印方法
    ......
   
    ** 结果:会查到"name"字段中同时包含分词"word1"和"word2"的Document文档对象。
   
   ** 注意:在调用BooleanQuery.add(Query, boolean, boolean)方法时除了第一个参数外还有两个参数。
   第二个参数意思是当前加入的查询子句Query对象是否必须满足。
   第三个参数意思是当前加入的查询子句Query对象是否不需要满足。
   
   这样的话那么可能产生的关系组合方式有四种:
            <1>true和false:表明当前加入的子句是必须要满足的
            <2>false和true:表明当前加入的子句是不可以被满足的
            <3>false和false:表明当前加入的子句是可选的
            <4>true和true:这种组合是错误的,执行的时候会有错。
   上边代码中表示的是<1>,即两个query子查询都要满足,即"and"的关系
   如果换成:
     query.add(query1, false, false);
     query.add(query2, false, false);
   那就是<3>,表示"or"的关系
   
   ** 注意:由于BooleanQuery这样的查询是可以嵌套的,索引可以表示多种条件下的组合查询。但是如果子句比较多的话,
   无疑又会导致查找效率降低。
   为此,Lucene给出了一个默认的限制,就是BooleanQuery的子句数目不能超过1024个
   
   4.3 在某一范围内搜索----RangeQuery类:有的时候用户会需要一种在一个范围内查找某个文档,
   比如查找某一时间段内的所有文档,为此Lucene提供了RangeQuery类来实现类似这样的功能。
   
   ** 原理:RangeQuery表示在某范围内的搜索条件,实现从一个开始分词到一个结束分词的搜索功能,
   即构造RangeQuery对象的时候要指定一个开始分词和一个结束分词,还可以指定是否包含开始、结束分词在内一起查找。
   
   例子:
    //创建Document文档对象
    Document doc1 = new Document();
    doc1.add(Field.Text("time","200001"));
    Document doc2 = new Document();
    doc2.add(Field.Text("time","200002"));
    Document doc3 = new Document();
    doc3.add(Field.Text("time","200003"));
    Document doc4 = new Document();
    doc4.add(Field.Text("time","200004"));
    Document doc5 = new Document();
    doc5.add(Field.Text("time","200005"));
    //创建索引写入器
    IndexWriter writer = new IndexWriter("C://lucene_index", new StandardAnalyzer(), true);
    
    //设置使用复合索引格式
    writer.setUseCompoundFile(true);
    
    //添加Document文档并创建索引
    writer.addDocument(doc1);
    writer.addDocument(doc2);
    writer.addDocument(doc3);
    writer.addDocument(doc4);
    writer.addDocument(doc5);
    writer.close();
    
    //开始搜索,创建搜索器
    IndexSearch search = new IndexSearch("C://lucene_index");
    //准备搜索范围:即创建两个Term分词对象
    Term beginTime = new Term("time", "200001");
    Term endTime = new Term("time", "200005");
    /*
    创建RangeQuery对象:
    之所以没有使用Query是因为我们下边要调用RangeQuery中特有的方法,并指定不包含边界值在内进行搜索
    */
    RangeQuery query = new RangeQuery(beginTime, endTime, false);
    //得到结果
    Hits hits = search.search(query);
    //打印结果
    ......
    
    ** 注意:这里调用了writer.setUseCompoundFile()方法设置了使用复合索引创建索引文件。
   
   4.4 使用前缀搜索----PrefixQuery类:通常情况下定义一个Term,该分词包含要查找的字段名和对应字段中分词的前缀,
   用这个Term对象构造一个PrefixQuery对象,就可以使用前缀搜索了。
   
   例子:
    //创建四个文档Document,其中每个中都保存Field.Text("name", "David/Darwen/Smith/Smart");
    //创建写入器,同上
    ......
    //创建搜索器,然后构造Term对象
    Term pre1 = new Term("name", "Da");//大写D和小写a
    Term pre2 = new Term("name", "da");//纯小写
    Term pre3 = new Term("name", "sm");//纯小写
    //分别构造PrefixQuery对象
    PrefixQuery query = null;
    query = new PrefixQuery(pre1);
    hits = search.search(query);
    //打印:但是结果却没有找到,这是因为标准的分词器对数据进行分词的时候会把所有大写字母转换成小写所以"Da"找不到记录
    
    //查询第二个"da"前缀,因为正好标准分词器分词时将数据改成小写所以"da"才能找到结果
    query = new PrefixQuery(pre1);
    hits = search.search(query);
    //查找第三个前缀"sm",打印后可以找到对应的结果
    query = new PrefixQuery(pre1);
    hits = search.search(query);
    //打印
    ......
   
   ** 注意:上面通过前缀"Da"不能找到结果,而通过"da"却能找到结果,
   这是因为在创建索引的时候标准分词器对文档数据分析切词的时候,将所有的大写字母都会转变成小写字母,
   所以根据大写字母的前缀,不能找到对应的分词;而相反如果换成小写的前缀"da"正好能找到对应的分词。
   
   4.5 多关键字的搜索----PhraseQuery类:用搜索引擎搜索时,常常查找的并非是一个简单的单词,很有可能是几个不同的关键字。
   这些关键字要么是紧密相联,构成一个精确的短语;要么可能这些关键字之间还有些未知的无关紧要的内容。
   此时用户希望能够通过查找找到对应的内容,但是我们知道多个关键字之间存在了一些其他未知内容,
   当我们用普通方法搜索的时候,我们会知道,被搜索的索引文件中虽然有我们搜索的这些关键字,
   但是对于索引文件中的内容他们是不连贯的,显然这将导致搜索结果的得分较低,所以达不到我们的要求。
   Lucene为此提供了PhraseQuery类来解决上面的问题。
   
   ** 功能:它的add方法让用户往其内部添加关键字,
   在添加完毕后,用户还可以通过setSlop()方法来设定一个称之为"坡度"的变量来确定关键字之间
   是否允许和允许多少个无关词汇的存在。
   
   例子:
    Document doc1 = new Document()
    doc1.add(Field.Text("content", "david mary smith robert"))
    //创建索引写入器,设为复合索引格式,最后创建索引,之后创建搜索器
    ......
    //构建关键字词条
    Term word1 = new Term("content", "david");
    Term word2 = new Term("content", "smith");
    //创建Query
    PhraseQuery query = new PhraseQuery();
    query.add(word1);
    query.add(word2);
    //设置坡度:坡度值大于等于实际分词之间拥有的分词个数就行,也就是坡度值必须大于等于无关紧要的数据被分词后的个数
    query.setSlop(1);//即大于等于1的数值都可以让搜索结果找到,但是如果小于1的话就找不到了。
    //结果集
    Hits hits = search.search(query);
    //打印
    ......
   
   ** 注意:坡度值可以大于等于无关紧要的数据被分词后的个数,但是不能小于这个个数。
   如"david mary smith robert",查找的时候传入的分词是"david"和"smith",那么坡度可以">=1"但是不能"<1"
   
   4.6 使用短语缀搜索----PhrasePrefixQuery类:PhrasePrefixQuery与PhraseQuery类似。
   在PhraseQuery中,如果用户想查找及存在短语"david robert"又存在"mary robert"的文档的话,
   那么只能使用两个PhraseQuery对象,一个用来查找存在关键字"david robert"的文档,另一个用来查找存在关键字"mary robert"的文档,
   最后使用BooleanQuery创建两个query的关系为"或",然后再进行查找。
   为此Lucene提供了PhrasePrefixQuery类可以很方便的为我们完成上面的需求。
   
   例子:
    //创建文档,并保存到索引,同上
    ......
    //创建搜索器,并且创建分词Term对象
    Term word1 = new Term("content", "david");
    Term word2 = new Term("content", "mary");
    Term word3 = new Term("content", "robert");
    //创建Query对象
    PhrasePrefixQuery query = new PhrasePrefixQuery();
    //添加短语中可能出现的第一个分词
    query.add(new Term[]{word1, word2});
    //加入短语中出现的最后一个分词
    query.add(word3);
    //设置坡度,这里的坡度设置原则和上边设定PhraseQuery的坡度的原则相同
    query.setSlop(2);
    //开始搜索,返回结果集
    Hits hits = search.search(query);
    //打印
    ......
    
   ** 注意:上边查找的目的是查找存在"david robert"或"mary robert"短语的Document文档。
   其中我们使用了add(Term[]);这里接收的参数是一个Term数组表示这是要搜索的分词句中第一个出现的分词,可以设置多个。
   而add(Term)方法的意思就是设置要搜索的分词句中最后一个出现的分词。然后还是通过设置坡度来说明无关紧要的分词的个数。
   
   4.7 相近词语的搜索----FuzzyQuery类:是一种模糊查询,它可以简单的识别两个相近的词语。=
   
   例子:
     Document doc1 = new Document();
     doc1.add(Field.Text("name", "david"));
    
     Document doc2 = new Document();
     doc2.add(Field.Text("name", "sdavid"));
    
     Document doc3 = new Document();
     doc3.add(Field.Text("name", "davie"));
     //创建索引写入器,然后保存索引,在创建搜索器,同上
     ......
     //创建用户模糊查询的分词Term对象
     Term word = new Term("name", "david");
     //创建Query对象:该FuzzyQuery没有提供add方法,只能通过构造函数传入搜索分词对象
     FuzzyQuery query = new FuzzyQuery(word);
     //搜索
     Hits hits = search.search(query);
     //打印
     ......
   
   ** 注意:搜索结果是三个Document文档都搜索到了,这就是模糊查询的效果,
   可以把个包含要搜索的分词相似的分词对应的Document文档对象查找到。
    
   4.8 使用通配符搜索----WildcardQuery类:可以使用通配符进行查询。
   Lucene中通配符供提供两个:"?","*".
   "?"表示一个字符
   "*"表示0个至多个字符,即任何多个字符
   
   例子:
    /*
    创建三个Document文档,分别加入一个Field字段:名为"content",
    值分别为"whatever/whoever/however/everest"中的一个。
    然后创建索引,并创建搜索器
    */
    ......
    //构造通配符式的分词Term对象
    Term word1 = new Term("content", "*ever"); //查询将找到含有"whatever"、"whoever"和"however"分词的Document
    Term word1 = new Term("content", "wh?ever"); //查询将找到含有"whoever"分词的Document
    Term word1 = new Term("content", "h??ever"); //查询将找到含有"however"分词的Document
    Term word1 = new Term("content", "ever*"); //查询将找到含有"everest'分词的Document
    //创建Query对象
    WildcardQuery query = new WildcardQuery(word1); //这里只做了一个演示
    //搜索
    Hits hits = search.search(query);
    //打印
    ......
    
  5. 查询字符串的解析器----QueryParser类:对于搜索引擎(如百度和Google)来讲,
  很多情况下只需要用户在输入框内输入所需查询的内容,然后单击"搜索"就可以了。
  对此因为用户输入的不一定就是索引中生成好的一个分词,所以在Lucene中,这项工作交由QueryParser类来完成对用户输入信息的分析,
  然后创建一个合适的Query对象或一个Query组。虽然Lucene提供的API允许使用者创建各种各样的Query子类对象,
  但它同时也允许通过QueryParser分析器类生成各种各样的Query子对象,这使的Lucene的查询功能更加灵活和强大。
  
   5.1 QueryParser的简单用法:它实际上就是一个解析用户输入的工具,可以通过扫描用户输入的字符串,生成Query对象。
   
   例子:Query query = QueryParser.parse(keywords,fieldName,new StandarcAnalyzer());
   
   ** 注意:我们要传入用户输入的关键字,还要告诉QueryParser默认将到指定的fieldName字段内查找关键字被切词后的分词。
   当然这里说是指定QueryParser默认要查询的fieldName,那么就还可以指定fielName,
   当然,我们可以在输入搜索关键字的时候通过Lucene特有的方法指定fieldName,
   即:"content:david",如果输入的是这样形式的搜索关键字,
   那么生成的Query对象中绑定的搜索字段将不是QueryParser.parse(keywords,fieldName,new StandarcAnalyzer());中指定的fieldName,
   而是keywords中指定的代表字段名称的字段,即"content:david",也就是到字段content中搜索。
   
   ** 用户输入的关键字和QueryParser理解的含义:
   
    5.1.1 输入"David"------------- QueryParser解析成:在默认的字段中检索"David"被切词后的分词
    5.1.2 输入"content: David"------------- QueryParser解析成:在"content"字段中检索"David"被切词后的分词
    5.1.3 输入"David Mary"或输入"David OR Mary"---- QueryParser解析成:在默认的字段中
    检索"David"和"Mary"被切词后的分词,它们是"或"的关系
    5.1.4 输入"+David +Mary"或输入"David AND Mary"---- QueryParser解析成:在默认的字段中
    检索"David"和"Mary"被切词后的分词,它们是"与"的关系
    5.1.5 输入"content:David -title:Manager"或输入"content:David AND NOT title:Manager"---- QueryParser解析成:
    在"content"字段中检索包括关键字"David"被切词后的分词,
    但是在"title"字段中不能包含关键字"Manager"被切词后的分词的Document文档
    5.1.6 输入"(David OR Mary) AND Robert"---- QueryParser解析成:在默认字段中检索包含"David"或"Mary"被切词后的分词,
    但是一定要包含"Robert"被切词后的分词的Document文档
    5.1.7 输入"Davi*"------------QueryParser解析成:在默认字段中检索前缀为"David"被切词后的分词作为分词前缀的Document文档
    5.1.8 输入"content: "David is a manager"" ---- QueryParser解析成:在"content"字段中包含短语"David is a manager"被切词后的分词短语的Document文档
    
   ** 注意:在使用QueryParser对用户输入的关键字进行扫描时,还要传一个分词器对象(后面详讲)。
   不过,当对用户输入的关键字进行分词时,分词器应当与建立索引时使用的分词器是一样的,
   这样才保证搜索关键字的分词原则和建立索引时的分词原则是一样的,这样才能保证分词成功。
    
   5.2 QueryParser的 "与" 和 "或" :这里所说的与和或主要针对用户输入的搜索关键字的形式为"David Mary"的时候,
   QueryParser解析成Query的时候把这种情况是按照把这两个关键词的分词作为了"或"的关系,也就是生成的Query对象查询的时候,
   查找的目标是对应的字段中出现分词"david"或"mary"(注意这里被分词器分析切词后都是小写),而不是"与"的关系。
   
   ** 要解决的问题:就是我们想让QueryParser把用户输入的"David Mary"这种形式按照"与"的关系进行处理,怎么实现呢?
   
   ** 解决方案:以下两行代码可以参看Javadoc文档做进一步理解。
      QueryParser parser = new QueryParser(fieldName, new StandardAnalyzer());
      parser.setOperator(QueryParser.DEFAULT_OPERATOR_AND);
      
      ** 说明:加上这两行代码就是将默认的用"或"关系处理改成了用"与"的关系进行处理,
      这样设置之后,在进行对用户输入的"David Mary"这种形式的搜索关键字,
      那么不会再按照"或"的关系进行处理,而是按照"与"的关系处理。
   

原创粉丝点击