(四)Lucene——搜索和相关度排序

1. 搜索

1.1 创建查询对象的方式

  • 通过Query子类来创建查询对象

Query子类常用的有TermQuery、NumericRangeQuery、BooleanQuery

特点:不能输入lucene的查询语法不需要指定分词器

  • 通过QueryParser来创建查询对象(常用)

QueryParser、MultiFieldQueryParser

特点:可以输入lucene的查询语法可以指定分词器

1.2 通过Query子类来创建查询对象

1.2.1 TermQuery(精确的词项查询)

@Test
    public void termQuery() {
        // 创建TermQuery对象
        Query query = new TermQuery(new Term("description", "java"));
        doSearch(query);
    }
private void doSearch(Query query) {
        // 创建IndexSearcher
        // 指定索引库的地址
        try {
            File indexFile = new File("D:\DBIndex\");
            Directory directory = FSDirectory.open(indexFile);
            IndexReader reader = DirectoryReader.open(directory);
            IndexSearcher searcher = new IndexSearcher(reader);
            // 通过searcher来搜索索引库
            // 第二个参数:指定需要显示的顶部记录的N条
            TopDocs topDocs = searcher.search(query, 10);

            // 根据查询条件匹配出的记录总数
            int count = topDocs.totalHits;
            System.out.println("匹配出的记录总数:" + count);
            // 根据查询条件匹配出的记录
            ScoreDoc[] scoreDocs = topDocs.scoreDocs;

            for (ScoreDoc scoreDoc : scoreDocs) {
                // 获取文档的ID
                int docId = scoreDoc.doc;

                // 通过ID获取文档
                Document doc = searcher.doc(docId);
                System.out.println("商品ID:" + doc.get("id"));
                System.out.println("商品名称:" + doc.get("name"));
                System.out.println("商品价格:" + doc.get("price"));
                System.out.println("商品图片地址:" + doc.get("pic"));
                System.out.println("==========================");
                // System.out.println("商品描述:" + doc.get("description"));
            }
            // 关闭资源
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

1.2.2  NumericRangeQuery(数字范围查询)

@Test
    public void numericRangeQuery() {
        // 创建NumericRangeQuery对象
        // 参数:域的名称、最小值、最大值、是否包含最小值、是否包含最大值
        Query query = NumericRangeQuery.newFloatRange("price", 55f, 60f, true, false);
        doSearch(query);
    }

1.2.3 BooleanQuery(组合查询)

@Test
    public void booleanQuery() {
        // 创建BooleanQuery
        BooleanQuery query = new BooleanQuery();
        // 创建TermQuery对象
        Query q1 = new TermQuery(new Term("description", "lucene"));
        // 创建NumericRangeQuery对象
        // 参数:域的名称、最小值、最大值、是否包含最小值、是否包含最大值
        Query q2 = NumericRangeQuery.newFloatRange("price", 55f, 60f, true, false);

        // 组合关系代表的意思如下:
        // 1、MUST和MUST表示“与”的关系,即“交集”。
        // 2、MUST和MUST_NOT前者包含后者不包含。
        // 3、MUST_NOT和MUST_NOT没意义
        // 4、SHOULD与MUST表示MUST,SHOULD失去意义;
        // 5、SHOUlD与MUST_NOT相当于MUST与MUST_NOT。
        // 6、SHOULD与SHOULD表示“或”的概念。

        query.add(q1, Occur.MUST_NOT);
        query.add(q2, Occur.MUST_NOT);

        doSearch(query);
    }

1.3 通过QueryParser来创建查询对象

1.3.1 QueryParser

通过QueryParser来创建query对象,可以指定分词器,搜索时的分词器和创建该索引的分词器一定要一致。还可以输入查询语句。

@Test
    public void indexSearch() throws Exception {
        // 创建query对象
        // 使用QueryParser搜索时,需要指定分词器,搜索时的分词器要和索引时的分词器一致
        // 第一个参数:默认搜索的域的名称
        QueryParser parser = new QueryParser("description", new StandardAnalyzer());

        // 通过queryparser来创建query对象
        // 参数:输入的lucene的查询语句(关键字一定要大写)
        Query query = parser.parse("description:java AND lucene");

        doSearch(query);
    }

1.3.2 MultiFieldQueryParser(多域查询)

@Test
    public void multiFieldQueryParser() throws Exception {
        // 创建 MultiFieldQueryParser
        // 默认搜索的多个域的域名
        String[] fields = { "name", "description" };
        Analyzer analyzer = new StandardAnalyzer();
        Map<String, Float> boosts = new HashMap<String, Float>();
        boosts.put("name", 200f);
        MultiFieldQueryParser parser = new MultiFieldQueryParser(fields, analyzer, boosts);

        // Query query = parser.parse("name:lucene OR description:lucene");
        Query query = parser.parse("java");
        System.out.println(query);

        doSearch(query);
    }

1.3.3 查询语法

(1)基础的查询语法,关键词查询

域名+“:”+搜索的关键字

例如:content:java

(2)范围查询

域名+:+[最小值 TO 最大值]

例如:size:[1 TO 1000]

注意:QueryParser不支持对数字范围的搜索,它支持字符串范围。数字范围搜索建议使用NumericRangeQuery

(3)组合条件查询

Occur.MUST 查询条件必须满足,相当于and

+(加号)

Occur.SHOULD 查询条件可选,相当于or

空(不用符号)

Occur.MUST_NOT 查询条件不能满足,相当于not非

-(减号)

 

 

 

 

 

(3.1)+条件1 +条件2:两个条件之间是并且的关系and

例如:+filename:apache +content:apache

(3.2)+条件1 条件2:必须满足第一个条件,忽略第二个条件

例如:+filename:apache content:apache

(3.3)条件1 条件2:两个条件满足其一即可。

例如:filename:apache content:apache

(3.4)-条件1 条件2:必须不满足条件1,要满足条件2

例如:-filename:apache content:apache

(4)组合查询(3)的第二种写法

  • 条件1 AND 条件2
  • 条件1 OR 条件2
  • 条件1 NOT 条件2

 1.4 TopDocs

Lucene搜索结果可通过TopDocs遍历,TopDocs类提供了少量的属性,如下:

方法或属性

说明

totalHits

匹配搜索条件的总记录数

scoreDocs

顶部匹配记录

注意:

Search方法需要指定匹配记录数量nindexSearcher.search(query, n)

TopDocs.totalHits:是匹配索引库中所有记录的数量

TopDocs.scoreDocs:匹配相关度高的前边记录数组,scoreDocs的长度小于等于search方法指定的参数n

2. 相关度排序

2.1 什么是相关度排序

相关度排序就是查询关键字与查询结果的匹配相关度。匹配越高的越靠前。Lucene是通过打分来进行相关度排序的。

2.1.1 打分分两步:

step1:根据词计算词的权重

step2:根据词的权重进行打分

2.1.2 词的权重

词指的就是term。也就是说一个term对一个文档的重要性就叫词的权重

影响词的权重的方式有两种:

  • Tf  ——词在同一个文档中出现的频率

     Tf越高说明词的权重越高

  • Df  ——词在多个文档中出现的频率

     Df越高说明词的权重越低

以上是自然打分的规则。

2.2 设置boost值影响打分

Boost:加权值默认是1.0f

设置加权值可以在创建索引时(如下代码)设置也可以在查询时(见1.3.2 MultiFieldQueryParser(多域查询)设置

for (Book book : list) {
            document = new Document();
            // store:如果是yes,则说明存储到文档域中
            // 图书ID
            // 不分词、索引、存储 StringField
            Field id = new StringField("id", book.getId().toString(), Store.YES);
            // 图书名称
            // 分词、索引、存储 TextField
            Field name = new TextField("name", book.getName(), Store.YES);
            ············
// 设置boost值 if (book.getId() == 4) description.setBoost(100f); // 将field域设置到Document对象中 document.add(id);        ············· }

Boost值是设置到Field域上的

原文地址:https://www.cnblogs.com/zjfjava/p/7638996.html