Lucene总结(三):使用Lucene搜索索引

上一篇总结了一下Lucene是如何构建索引的,所以这一篇总结一下Lucene中的搜索功能。主要分为几个部分,对特定项的搜索查询表达式QueryParser的使用指定数字范围内搜索指定字符串开头搜索多条件查询

在这里构建索引使用的是上一篇的例子,就不赘述了。

一般Lucene查询索引的流程是这样的:

  • 创建一个 Directory 对象,用于指定索引库存放的位置;
  • 创建一个 indexReader 对象, 需要指定 Directory 对象, 用于读取索引库中的文件;
  • 创建一个 indexSearcher 对象, 需要指定 indexReader 对象;
  • 创建一个 Term 对象,指定查询的域和查询的关键词
  • 执行查询
  • 返回查询结果,遍历查询结果并输出;
  • 关闭 indexReader

特定项的搜索

public class testSearch {

    private Directory directory;
    private IndexReader reader;
    private IndexSearcher searcher;

    @Before
    public void before() throws Exception{
        directory = FSDirectory.open(Paths.get("D:\\resource"));
        reader = DirectoryReader.open(directory);//根据目录获取IndexReader
        searcher = new IndexSearcher(reader);//根据IndexReader获取IndexSearcher
    }

    @After
    public void after() throws Exception{
        reader.close();
    }
    //对特定项进行搜索
    @Test
    public void TermQuery() throws Exception{
        String searchField = "content";
        String q = "version";
        Term term = new Term(searchField, q);
        Query query = new TermQuery(term);
        TopDocs hits = searcher.search(query, 10);
        System.out.println("匹配" + q + "总共查询到" + hits.totalHits + "个文档");
        for(ScoreDoc scoreDoc : hits.scoreDocs){
            Document doc = searcher.doc(scoreDoc.doc);
            System.out.println(doc.get("author"));
        }
    }
}

结果如下:
这里写图片描述
首先创建了一个IndexSearcher 实例,在进行搜索之前指定了要搜索的域和单词,然后根据这个创建了一个Term对象term ,由上面的代码可以看出,我们要查域为content中含有version的文档,根据term 创建一个搜索。最后就可以搜索出包含这个字符串的文件的路径。

这就是针对特定项的搜索,因为如果我想搜索versions,按理说应该也能搜索到version,但是Lucene在建立索引的时候也是根据一个个单词来的,如果我只搜索单词的一部分,那么是搜不到的,所以这种针对特定项搜索其实用的不多。用的更频繁一点的是QueryParser。

查询表达式QueryParser的使用

首先看一下如何使用:

@Test
    public void testQueryParser() throws Exception{
        Analyzer analyzer = new StandardAnalyzer();
        String serarchField = "content";
        String q = "version~";
        QueryParser parser = new QueryParser(serarchField, analyzer);//查询解析器
        Query query = parser.parse(q);//通过解析要查询的String,获取查询对象
        TopDocs hits = searcher.search(query, 10);
        System.out.println("匹配" + q + "总共查询到" + hits.totalHits + "个文档");
        for(ScoreDoc scoreDoc : hits.scoreDocs){
            Document doc = searcher.doc(scoreDoc.doc);
            System.out.println(doc.get("author"));
        }
    }

结果如下(不知道为什么图片上传失败,那就直接复制结果了):

匹配version~总共查询到3个文档
C
A
B

其实就是由开始的Term换成了QueryParser ,这里要注意,QueryParser 的参数是要查询的域和分析器。使用QueryParser的好处在于初始化查询字符串q的时候,是有语法的。

如果我将q改成”version OR Unicode”,那么Lucene就会查询出所有包含particular或Unicode(不区分大小写)的文档,这里的OR也可以省略不写。同样的,如果我把OR改成AND,那么就是查询出所有包含version 且包含Unicode的文档。如果要类似于上面提到的模糊查询,可以将q定义为“version~”,这样,包含version和versions的都会出来。我上面的例子也就是这样的,关于这个更多的内容可以看一下官方文档。

指定数字范围内搜索

这个比较简单,直接给代码了:

@Test
    public void numericRangeQueryTest() throws Exception{
        NumericRangeQuery<Integer> query = NumericRangeQuery.newIntRange("id", 1, 2, true,true);
        TopDocs hits = searcher.search(query, 10);
        System.out.println("总共查询到" + hits.totalHits + "个文档");
        for (ScoreDoc score : hits.scoreDocs) {
            Document doc = searcher.doc(score.doc);
            System.out.print(doc.get("title"));
            System.out.print(doc.get("author") + " ");
            System.out.println(doc.get("position"));
        }
    }

总共查询到2个文档
Java is a good language.A a
Java is a cross platform language.B b

这里使用的是ABCD文章的那篇,但是记得把id域改为IntFiled,因为之前的id域是字符串。
NumericRangeQuery.newIntRange中参数分别是查询的域,开始,结束,是否包含开始值,是否包含结束值。

指定字符串开头搜索

这个也比较简单,我使用了indexTest1的例子:

@Test
    public void preFixQueryTest() throws Exception{
        PrefixQuery query = new PrefixQuery(new Term("date","y"));
        TopDocs hits = searcher.search(query, 10);
        System.out.println("总共查询到" + hits.totalHits + "个文档");
        for (ScoreDoc score : hits.scoreDocs) {
            Document doc = searcher.doc(score.doc);
            System.out.println(doc.get("id"));
            System.out.println(doc.get("date"));
            System.out.println(doc.get("desc"));
        }
    }

结果如下:

总共查询到1个文档
1
yesterday
It was a rainy day yesterday

这个和之前的给数字范围查找类似。指定字符串开头搜索的话需要创建一个PrefixQuery对象,将要搜索的字段和开头的字符串传进去,然后再搜索。上面是搜索,date域中以y开头的文档。

多条件查询

又称组合查询。
就是将多个查询条件组合到一起进行查询。这里我使用上面的指定字符串开头搜索和指定数字范围内搜索进行组合:

@Test
    public void booleanQueryTest() throws Exception{
        NumericRangeQuery<Integer> query1 = NumericRangeQuery.newIntRange("id",1,2,true,true);
        PrefixQuery query2 = new PrefixQuery(new Term("title", "java"));

        BooleanQuery.Builder builder = new BooleanQuery.Builder();

        builder.add(query1, BooleanClause.Occur.MUST);//表示这个条件必须要有
        builder.add(query2, BooleanClause.Occur.MUST);

        TopDocs hits = searcher.search(builder.build(), 10);
        System.out.println("总共查询到" + hits.totalHits + "个文档");
        for (ScoreDoc score : hits.scoreDocs) {
            Document doc = searcher.doc(score.doc);
            System.out.println(doc.get("id"));
            System.out.println(doc.get("author"));
            System.out.println(doc.get("content"));
        }
    }

结果如下:
这里写图片描述
这里提一下,为什么content域取出来的值为null,因为在上一篇构建索引的时候,content域是使用了Field.Store.NO的也就是它没有存储在document中,所以是null。关于field可以看我的第一篇Lucene文章。
这里我把content改成存储了的position,就有如下结果:

总共查询到2个文档
1
A
a
2
B
b

组合查询使用的是BooleanQuery,然后组合的条件还是之前的条件,这些条件中原来该使用什么类初始化还是使用那些类初始化,只是往BooleanQuery中加就行了。当查询条件多的时候,就可以采用这种组合的查询方式来查询。这里可以稍微注意一下:

builder.add(query1, BooleanClause.Occur.MUST);//表示这个条件必须要有
builder.add(query2, BooleanClause.Occur.MUST);

BooleanClause.Occur.MUST表示必须符合这个条件,还有其他的如BooleanClause.Occur.SHOULD表示这个条件可以有,还有其他的就不多说了。

以上就是使用Lucene搜索索引的总结。


希望对你有帮助,如有疑问或见解,欢迎提出,一起进步。

猜你喜欢

转载自blog.csdn.net/RebelHero/article/details/80202415