构建各种Lucene Query

来源:互联网 发布:自助牛排知乎 编辑:程序博客网 时间:2024/05/25 23:29

出处: http://blog.sina.com.cn/s/blog_4efddaed0100bsa8.html

感谢您的文章

11.4.5  多关键字的搜索—PhraseQuery

除了普通的TermQuery外,Lucene还提供了一种Phrase查询的功能。用户在搜索引擎中进行搜索时,常常查找的并非是一个简单的单词,很有可能是几个不同的关键字。这些关键字之间要么是紧密相联,成为一个精确的短语,要么是可能在这几个关键字之间还插有其他无关的关键字。此时,用户希望将它们找出来。不过很显然,从评分的角度看,这些关键字之间拥有与查找内容无关短语所在的文档的分值一般会较低一些。

PhraseQuery正是Lucene所提供的满足上述需求的一种Query对象。它的add方法可以让用户往其内部添加关键字,在添加完毕后,用户还可以通过setSlop()方法来设定一个称之为“坡度”的变量来确定关键字之间是否允许、允许多少个无关词汇的存在。

下面以代码11.8为例对PhraseQuery进行介绍。

代码11.8  PhraseQueryTest.java

package ch11;

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

import org.apache.lucene.document.Document;

import org.apache.lucene.document.Field;

import org.apache.lucene.index.IndexWriter;

import org.apache.lucene.index.Term;

import org.apache.lucene.search.Hits;

import org.apache.lucene.search.IndexSearcher;

import org.apache.lucene.search.PhraseQuery;

import org.apache.lucene.search.PrefixQuery;

public class PhraseQueryTest {

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

         //生成Document对象

         Document doc1 = new Document();

         //添加“content”字段的内容

         doc1.add(Field.Text("content", "david mary smith robert"));

         //添加“title”字段的内容

         doc1.add(Field.Keyword("title", "doc1"));

         //生成索引书写器

         IndexWriter writer = new IndexWriter("c:\\index",

                 new StandardAnalyzer(), true);

         //设置为混合索引格式

         writer.setUseCompoundFile(true);

         //将文档添加到索引中

         writer.addDocument(doc1);

         //关闭索引

         writer.close();

         //生成索引搜索器

         IndexSearcher searcher = new IndexSearcher("c:\\index");

         //构造词条

         Term word1 = new Term("content", "david");

         Term word2 = new Term("content","mary");

         Term word3 = new Term("content","smith");

         Term word4 = new Term("content","robert");

         //用于保存检索结果

         Hits hits = null;

         //生成PhraseQuery对象,初始化为null

         PhraseQuery query = null;

         // 第一种情况,两个词本身紧密相连,先设置坡度为0,再设置坡度为2

         query = new PhraseQuery();

         query.add(word1);

         query.add(word2);

         //设置坡度

         query.setSlop(0);

         //开始检索,并返回检索结果

         hits = searcher.search(query);

         //输出检索结果的相关信息

         printResult(hits, "'david'与'mary'紧紧相隔的Document");

         //再次设置坡度

         query.setSlop(2);

         //开始第二次检索

         hits = searcher.search(query);

         //输出检索结果

         printResult(hits, "'david'与'mary'中相隔两个词的短语");

         // 第二种情况,两个词本身相隔两个词,先设置坡度为0,再设置坡度为2

         query = new PhraseQuery();

         query.add(word1);

         query.add(word4);

         //设置坡度

         query.setSlop(0);

         //开始第三次检索,并返回检索结果

         hits = searcher.search(query);

         //输出检索结果

         printResult(hits, "'david'与'robert'紧紧相隔的Document");

         //设置坡度

         query.setSlop(2);

         //开始第四次检索,并返回检索结果

         hits = searcher.search(query);

         //输出检索结果

         printResult(hits, "'david'与'robert'中相隔两个词的短语");

     }

     public static void printResult(Hits hits, String key) throws Exception

         {System.out.println("查找 \"" + key + "\" :");

         if (hits != null) {

             if (hits.length() == 0) {

                 System.out.println("没有找到任何结果");

                 System.out.println();

             } else {

                 System.out.print("找到");

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

                     //取得文档对象

                     Document d = hits.doc(i);

                     //取得“title”字段的内容

                     String dname = d.get("title");

                     //输出相关的信息

                     System.out.print(dname + "   ");

                 }

                 System.out.println();

                 System.out.println();

             }

         }

     }

}

在上述代码中创建了一个Document,这个Document的“content”域中含有4个关键字。接下来,代码创建了一个PhraseQuery对象,首先将前两个紧紧相连关键字放入其中,并设置它们的坡度值分别为0和2,接下来,又将第一个和最后一个关键字放入其中,同样设置它们的坡度值为0和2。

代码11.8的运行效果,如图11-13所示。

文本框:图11-13 PhraseQuery的测试效果从图11.8中可以看出,代码11.8共进行了4次检索测试,并且分两组分别对检索结果进行对比。

从代码11.8和图11-13中可以看出,对两个紧连的关键字来说无论将坡度设置为多少,Lucene总能找到它所在的文档,而对两个不紧连的关键字,如果坡度值小于它们之间无关词的数量,那么则无法找到。其实,当两个关键字之间的无关词数小于等于坡度值时,总是可以被找到。

11.4.6  使用短语缀搜索—PhrasePrefixQuery

PhrasePrefixQuery与Phrase有些类似。在PhraseQuery中,如果用户想查找短语“david robert”,又想查找短语“mary robert”。那么,他就只能构建两个PhraseQuery,然后再使用BooleanQuery将它们作为其中的子句,并使用“或”操作符来连接,这样就能达到需要的效果。PhrasePrefixQuery可以让用户很方便地实现这种需要。

接下来看看在代码11.9中是如何使用PhrasePrefixQuery来实现的。

代码11.9  PhrasePrefixQueryTest.java

package ch11;

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

import org.apache.lucene.document.Document;

import org.apache.lucene.document.Field;

import org.apache.lucene.index.IndexWriter;

import org.apache.lucene.index.Term;

import org.apache.lucene.search.Hits;

import org.apache.lucene.search.IndexSearcher;

import org.apache.lucene.search.PhrasePrefixQuery;

import org.apache.lucene.search.PhraseQuery;

import org.apache.lucene.search.RangeQuery;

public class PhrasePrefixQueryTest {

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

         //生成Document对象

         Document doc1 = new Document();

         //添加“content”字段的内容

         doc1.add(Field.Text("content", "david mary smith robert"));

         //添加“title”字段的内容

         doc1.add(Field.Keyword("title", "doc1"));

         //生成索引书写器对象

         IndexWriter writer = new IndexWriter("c:\\index",

                 new StandardAnalyzer(), true);

         //将文档添加到索引中

         writer.addDocument(doc1);

         //关闭索引书写器

         writer.close();

         //生成索引检索器

         IndexSearcher searcher = new IndexSearcher("c:\\index");

         //构造词条

         Term word1 = new Term("content", "david");

         Term word2 = new Term("content", "mary");

         Term word3 = new Term("content", "smith");

         Term word4 = new Term("content", "robert");

         //用于保存检索结果

         Hits hits = null;

         //生成PhrasePrefixQuery对象,初始化为null

         PhrasePrefixQuery query = null;

         query = new PhrasePrefixQuery();

         // 加入可能的所有不确定的词

         query.add(new Term[]{word1, word2});

         // 加入确定的词

         query.add(word4);

         //设置坡度

         query.setSlop(2);

         //开始检索,并返回检索结果

         hits = searcher.search(query);

         //输出检索结果的相关信息

         printResult(hits, "存在短语'david robert'或'mary robert'的文档");

     }

     public static void printResult(Hits hits, String key) throws Exception

         {System.out.println("查找 \"" + key + "\" :");

         if (hits != null) {

             if (hits.length() == 0) {

                 System.out.println("没有找到任何结果");

                 System.out.println();

             } else {

                 System.out.print("找到");

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

                 //获取文档对象

                     Document d = hits.doc(i);

                     //取得“title”字段内容

                     String dname = d.get("title");

                     System.out.print(dname + "   ");

                 }

                 System.out.println();

                 System.out.println();

             }

         }

     }

}

在上述代码中,首先构建了一个Document,它的“content”字段中包含4个关键字。接下来,构建了一个PhrasePrefixQuery的对象,调用它的add(Term [])方法设定出现在短语中的第一个关键词。由于这个方法的参数类型为一个Term型的数组,所以,它可以设置多个Term,即出现在短语中的第一个词就在这个数组中进行选择。然后,再使用add(Term)方法设置出现在短语中的后一个词。代码的运行结果如图11-14所示。

图11-14  PhrasePrefixQuery的测试结果

从图11-14中可以看出,使用PhrasePrefixQuery可以非常容易的实现相关短语的检索功能。

11.4.7  相近词语的搜索—FuzzyQuery

FuzzyQuery是一种模糊查询,它可以简单地识别两个相近的词语。下面以11.10为例进行详细介绍。

代码11.10  FuzzyQueryTest.java

package ch11;

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

import org.apache.lucene.document.Document;

import org.apache.lucene.document.Field;

import org.apache.lucene.index.IndexWriter;

import org.apache.lucene.index.Term;

import org.apache.lucene.search.FuzzyQuery;

import org.apache.lucene.search.Hits;

import org.apache.lucene.search.IndexSearcher;

public class FuzzyQueryTest {

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

         //生成Document对象

         Document doc1 = new Document();

         //添加“content”字段的内容

         doc1.add(Field.Text("content", "david"));

         //添加“title”字段的内容

         doc1.add(Field.Keyword("title", "doc1"));

         Document doc2 = new Document();

         doc2.add(Field.Text("content", "sdavid"));

         doc2.add(Field.Keyword("title", "doc2"));

         Document doc3 = new Document();

         doc3.add(Field.Text("content", "davie"));

         doc3.add(Field.Keyword("title", "doc3"));

         //生成索引书写器

         IndexWriter writer = new IndexWriter("c:\\index",

                 new StandardAnalyzer(), true);

         //将文档添加到索引中

         writer.addDocument(doc1);

         writer.addDocument(doc2);

         writer.addDocument(doc3);

         //关闭索引写器

         writer.close();

         //生成索引搜索器

         IndexSearcher searcher = new IndexSearcher("c:\\index");

         Term word1 = new Term("content", "david");

         //用于保存检索结果

         Hits hits = null;

         //生成FuzzyQuery对象,初始化为null

         FuzzyQuery query = null;

         query = new FuzzyQuery(word1);

         //开始检索,并返回检索结果

         hits = searcher.search(query);

         //输出检索结果的相关信息

         printResult(hits,"与'david'相似的词");

     }

     public static void printResult(Hits hits, String key) throws Exception

         {System.out.println("查找 \"" + key + "\" :");

         if (hits != null) {

             if (hits.length() == 0) {

                 System.out.println("没有找到任何结果");

                 System.out.println();

             } else {

                 System.out.print("找到");

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

                     //取得文档对象

                     Document d = hits.doc(i);

                     //取得“title”字段的内容

                     String dname = d.get("title");

                     System.out.print(dname + "   ");

                 }

                 System.out.println();

                 System.out.println();

             }

         }

     }

}

在上述代码中,首先构建了3个Document,这3个Document的“content”字段中都有一个与“david”较为相似的关键字(其中第一个就是david)。然后使用FuzzyQuery来对其进行检索。运行效果如图11-15所示。

文本框:图11-15 FuzzyQuery.java从图11-15中可以看出,使用FuzzyQuery可以检索到索引中所有包含与“david”相近词语的文档。

11.4.8  使用通配符搜索—WildcardQuery

Lucene也提供了通配符的查询,这就是WildcardQuery。下面以代码11.11为例进行介绍。

代码11.11  WildcardQueryTest.java

package ch11;

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

import org.apache.lucene.document.Document;

import org.apache.lucene.document.Field;

import org.apache.lucene.index.IndexWriter;

import org.apache.lucene.index.Term;

import org.apache.lucene.search.Hits;

import org.apache.lucene.search.IndexSearcher;

import org.apache.lucene.search.WildcardQuery;

public class WildcardQueryTest {

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

         //生成Document对象,下同

         Document doc1 = new Document();

         //添加“content”字段的内容,下同

         doc1.add(Field.Text("content", "whatever"));

         //添加“title”字段的内容,下同

         doc1.add(Field.Keyword("title", "doc1"));

         Document doc2 = new Document();

         doc2.add(Field.Text("content", "whoever"));

         doc2.add(Field.Keyword("title", "doc2"));

         Document doc3 = new Document();

         doc3.add(Field.Text("content", "however"));

         doc3.add(Field.Keyword("title", "doc3"));

         Document doc4 = new Document();

         doc4.add(Field.Text("content", "everest"));

         doc4.add(Field.Keyword("title", "doc4"));

         //生成索引书写器

         IndexWriter writer = new IndexWriter("c:\\index",

                 new StandardAnalyzer(), true);

         //将文档对象添加到索引中

         writer.addDocument(doc1);

         writer.addDocument(doc2);

         writer.addDocument(doc3);

         writer.addDocument(doc4);

         //关闭索引书写器

         writer.close();

         //生成索引书写器

         IndexSearcher searcher = new IndexSearcher("c:\\index");

         //构造词条

         Term word1 = new Term("content", "*ever");

         Term word2 = new Term("content", "wh?ever");

         Term word3 = new Term("content", "h??ever");

         Term word4 = new Term("content", "ever*");

         //生成WildcardQuery对象,初始化为null

         WildcardQuery query = null;

         //用于保存检索结果

         Hits hits = null;

         query = new WildcardQuery(word1);

         //开始第一次检索,并返回检索结果

         hits = searcher.search(query);

         //输出检索结果的相关信息

         printResult(hits, "*ever");

         query = new WildcardQuery(word2);

         //开始第二次检索,并返回检索结果

         hits = searcher.search(query);

         //输出检索结果的相关信息

         printResult(hits, "wh?ever");

         query = new WildcardQuery(word3);

         //开始第三次检索,并返回检索结果

         hits = searcher.search(query);

         //输出检索结果的相关信息

         printResult(hits, "h??ever");

         query = new WildcardQuery(word4);

         //开始第四次检索,并返回检索结果

         hits = searcher.search(query);

         //输出检索结果的相关信息

         printResult(hits, "ever*");

     }

     public static void printResult(Hits hits, String key) throws Exception

         {System.out.println("查找 \"" + key + "\" :");

         if (hits != null) {

             if (hits.length() == 0) {

                 System.out.println("没有找到任何结果");

                 System.out.println();

             } else {

                 System.out.print("找到");

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

                     //取得文档对象

                     Document d = hits.doc(i);

                     //取得“title”字段的内容

                     String dname = d.get("title");

                     System.out.print(dname + "   ");

                 }

                 System.out.println();

                 System.out.println();

             }

         }

     }

}

代码11.11的运行结果如图11-16所示。

文本框:图11-16 WildcardQuery的测试结果由上述代码可以看出,通配符“?”代表1个字符,而“*”则代表0至多个字符。不过通配符检索和上面的FuzzyQuery由于需要对字段关键字进行字符串匹配,所以,在搜索的性能上面会受到一些影响。

0 0