【问题标题】:How to deal with identifier fields in Lucene?如何处理 Lucene 中的标识符字段?
【发布时间】:2020-05-31 16:37:13
【问题描述】:

我偶然发现了一个类似于this other question 中描述的问题:我有一个名为“type”的字段,它是一个标识符,即它区分大小写,我想将它用于精确搜索,没有标记化,没有相似性搜索,只是简单地“找到'Sport:01'”。我可能会从“Sport*”中受益,但对我来说这并不是非常重要。

我无法让它工作:我认为存储它的正确字段类型是:StringField.TYPE_STOREDDOCS_AND_FREQS_AND_POSITIONSsetOmitNorms ( true )。但是,这种方式我无法使用标准分析器正确解析如下查询:+type:"RockMusic" +title: "a sample title",因为据我了解,分析器将输入转换为小写(即rockmusic)并且类型存储在它的原始混合大小写形式(因此,即使删除标题子句也无法解决)。

我想将不区分大小写的搜索标题与区分大小写的类型混合在一起,因为在某些情况下 type := BRAIN 是首字母缩写词,它与“大脑”不同。

那么,管理上述字段和搜索的最佳方式是什么?除了文本和字符串字段还有其他选择吗?

我使用的是 Lucene 6.6.0,但这是一个普遍问题,涉及多个(全部?)Lucene 版本。

一些显示细节的代码是here(见testIdMixedCaseID*)。 real use case 比较复杂,如果你想看一下,问题在于字段CC_FIELD,它可能是'BioProc',在这种情况下什么都找不到。

请注意,我需要使用普通的 Lucene,而不是 Solr 或 Elastic 搜索。

【问题讨论】:

  • 您可以将代码的相关部分添加到问题中吗?您使用的是哪个版本的 Lucene?
  • @andrewjames,我添加了一些细节,尽管问题很笼统,因此它们不是很相关。谢谢。

标签: java lucene


【解决方案1】:

以下注释基于 Lucene 8.x,而不是 Lucene 6.6 - 因此可能存在一些语法差异 - 但我同意您的观点,即任何此类差异应该与您的问题巧合。

这里有一些注释,我将重点关注您问题的以下方面:

但是,这样我无法使用标准分析器正确解析如下查询:+type:"RockMusic" +title:"a sample title"

我认为这有两个部分:

首先,使用"a sample title" 的查询示例 - 正如你所说 - 不能很好地与标准分析器的工作方式配合 - 出于你陈述的原因。

但是,其次,可以将您想要使用的两种类型的查询结合起来,我相信这可以满足您的需求:type 字段的完全匹配(例如RockMusic)和title 字段(a sample title)的更传统的标记化和不区分大小写的结果。

我会这样做:

这是一些简单的测试数据:

public static void buildIndex() throws IOException {
    final Directory dir = FSDirectory.open(Paths.get(INDEX_PATH));
    Analyzer analyzer = new StandardAnalyzer();
    IndexWriterConfig iwc = new IndexWriterConfig(analyzer);
    iwc.setOpenMode(OpenMode.CREATE);
    Document doc;

    try (IndexWriter writer = new IndexWriter(dir, iwc)) {
        doc = new Document();
        doc.add(new StringField("type", "RockMusic", Field.Store.YES));
        doc.add(new TextField("title", "a sample title", Field.Store.YES));
        writer.addDocument(doc);

        doc = new Document();
        doc.add(new StringField("type", "RockMusic", Field.Store.YES));
        doc.add(new TextField("title", "another different title", Field.Store.YES));
        writer.addDocument(doc);

        doc = new Document();
        doc.add(new StringField("type", "Rock Music", Field.Store.YES));
        doc.add(new TextField("title", "a sample title", Field.Store.YES));
        writer.addDocument(doc);

    }
}

这里是查询代码:

public static void doSearch() throws QueryNodeException, ParseException, IOException {

    IndexReader reader = DirectoryReader.open(FSDirectory.open(Paths.get(INDEX_PATH)));
    IndexSearcher searcher = new IndexSearcher(reader);

    TermQuery typeQuery = new TermQuery(new Term("type", "RockMusic"));

    Analyzer analyzer = new StandardAnalyzer();
    QueryParser parser = new QueryParser("title", analyzer);
    Query titleQuery = parser.parse("A Sample Title");

    Query query = new BooleanQuery.Builder()
            .add(typeQuery, BooleanClause.Occur.MUST)
            .add(titleQuery, BooleanClause.Occur.MUST)
            .build();

    System.out.println("Query: " + query.toString());
    System.out.println();

    TopDocs results = searcher.search(query, 100);
    ScoreDoc[] hits = results.scoreDocs;
    for (ScoreDoc hit : hits) {
        System.out.println("doc = " + hit.doc + "; score = " + hit.score);
        Document doc = searcher.doc(hit.doc);
        System.out.println("Type = " + doc.get("type")
                + "; Title = " + doc.get("title"));
        System.out.println();
    }
}

上述查询的输出如下:

Query: +type:RockMusic +(title:a title:sample title:title)

doc = 0; score = 0.7016101
Type = RockMusic; Title = a sample title

doc = 1; score = 0.2743341
Type = RockMusic; Title = another different title

如您所见,此查询与从您的问题中提取的查询略有不同。

但是找到的文档列表显示 (a) 根本没有找到 Rock Music 文档(很好 - 因为 Rock MusicRockMusic 的“类型”搜索词不匹配); (b) 在搜索 A Sample Title 时,标题 a sample title 的匹配分数远高于 another different title 文档。

补充说明:

此查询通过将 StringField 精确搜索与更传统的 TextField 标记化搜索相结合来工作 - 后一种搜索由 StandardAnalyzer 处理(匹配数据最初的索引方式)。

我假设分数排名对您有用 - 但对于标题搜索,我认为这是合理的。

这种方法也适用于您的BRAINbrain 示例,用于StringField 数据。

(我还假设,对于用户界面,用户可以从下拉列表中选择“RockMusic”类型值,然后在输入字段中输入“A Sample Title”搜索 - 但这已经开始了 -主题,我想)。

您显然可以根据需要增强分析器以包含停用词等。

当然,我的示例涉及硬编码数据 - 但推广这种处理动态提供的搜索词的方法并不需要太多。

希望这是有道理的 - 并且我正确理解了这个问题。

【讨论】:

  • @anrewjames 非常感谢您的深入分析。我最终得到了类似的代码,添加了一个 PerFieldAnalyzerWrapper,它能够为“type”等字段选择 KeywordAnalyzer,并使用标准分析器作为默认值。我不得不努力意识到使用我在搜索过程中索引的相同分析器需要一个查询解析器,但现在它正在工作,稍后会发布我的答案。再次感谢!
  • 我没有注意到PerFieldAnalyzerWrapper - 对于这种情况,这是一个非常有用的类。
【解决方案2】:

我要自己回答……

我通过制作a number of tests of my own 发现了@andrewjames 在his excellent analysis 中的概述。从本质上讲,像“类型”这样的字段在标准分析器中表现不佳,最好使用 KeywordAnalyzer 之类的分析器对其进行索引和搜索,在实践中,它会按原样存储原始值并相应地搜索它。

大多数真实案例都像我的示例一样,即需要精确匹配的混合 ID 类字段,以及像“标题”或“描述”这样的字段,它们使用每个令牌搜索、基于单词的评分来最好地服务于用户搜索, 停用词消除等。

正因为如此,PerFieldAnalyzerWrapper(另请参见上面链接的我的示例代码)提供了很大帮助,即包装分析器,它能够根据字段名称调度特定于分析字段的分析器。

要补充一点的是,当没有解析器(例如,使用新的TermQuery ( new Term ( fname, fval ))构建查询时,我仍然不清楚使用哪个分析器,所以现在我使用QueryParser

【讨论】:

    猜你喜欢
    • 2012-06-13
    • 2013-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-19
    • 1970-01-01
    相关资源
    最近更新 更多