【问题标题】:Lucene: Searching multiple fields with default operator = ANDLucene:使用默认运算符 = AND 搜索多个字段
【发布时间】:2012-12-17 00:33:20
【问题描述】:

为了允许用户使用 Lucene 3.5 跨多个字段进行搜索,我目前创建了 QueryParser 并将每个要搜索的字段添加到 DisjunctionMaxQuery。这在使用 OR 作为默认运算符时效果很好,但我现在想将默认运算符更改为 AND 以获得更准确(更少)的结果。

问题是,queryParser.setDefaultOperator(QueryParser.AND_OPERATOR) 遗漏了许多文档,因为所有术语都必须在至少 1 个字段中。

例如,考虑文档的以下数据:标题字段 =“编程语言”,正文字段 =“Java、C++、PHP”。如果用户要搜索 Java Programming,则此特定文档不会包含在结果中,因为标题和正文字段包含查询中的所有术语,尽管它们组合在一起。我希望为上述查询返回此文档,而不是为查询 HTML Programming

我考虑过一个包罗万象的领域,但我有一些问题。首先,用户经常在他们的查询中包含每个字段的术语(作者:bill),这对于一个包罗万象的字段是不可能的。此外,我使用 FastVectorHighlighter 突出显示某些字段,这需要对它们进行索引和存储。因此,通过添加一个包罗万象的字段,我将不得不对大多数相同的数据进行两次索引,这既费时又费空间。

有什么想法吗?

【问题讨论】:

  • 关于索引一个包罗万象的字段,您是否观察到引起关注的时间/空间命中?我的经验是在特定的存储字段中索引相同的数据,然后添加到通用的仅索引字段对性能或索引大小的影响非常小。
  • 另外,我想知道最终查询的结构是什么样的。特别是如何设置 dis-max 查询。很容易扼杀你获得有意义分数的能力。
  • @femtoRgon disjunctionMaxQuery 结构是这样的:'((title:java title:programming) | (body:java body:programming))~0.2' 你提出了一个很好的观点,即添加一个包罗万象的字段就时间/空间而言,可能影响不大。我确实考虑过但决定反对它,因为我还想保留按字段搜索的能力,例如作者:比尔。用户不仅使用此功能,而且我在幕后使用它。谢谢。

标签: java lucene


【解决方案1】:

我想我应该做更多的研究。原来MultiFieldQueryParser 提供了我正在寻找的确切功能。无论出于何种原因,我都为要搜索的每个字段创建了一个 QueryParser,如下所示:

String[] fields = {"title", "body", "subject", "author"};
QueryParser[] parsers = new QueryParser[fields.length];      
for(int i = 0; i < parsers.length; i++)
{
   parsers[i] = new QueryParser(Version.LUCENE_35, fields[i], analyzer);
   parsers[i].setDefaultOperator(QueryParser.AND_OPERATOR);
}

这将导致如下查询:

(+title:java +title:programming) | (+body:java +body:programming)

...这不是我想要的。现在我像这样创建一个 MultiFieldQueryParser:

MultiFieldQueryParser parser = new MultiFieldQueryParser(Version.LUCENE_35, new String[]{"title", "body", "subject"}, analyzer);
parser.setDefaultOperator(QueryParser.AND_OPERATOR);

这给了我正在寻找的查询:

+(title:java body:java) +(title:programming body:programming)

感谢 @seeta 和 @femtoRgon 的帮助!

【讨论】:

    【解决方案2】:

    也许您需要的是布尔查询的组合,这些查询可以捕获字段和术语的不同组合。在您给定的示例中,查询可能是 -

    (title:Java AND body:programming) OR (title:programming AND body:Java)。

    我不知道是否有一个现有的 Query 类可以为您自动生成它,但我认为这应该是在索引上运行的最终查询。

    【讨论】:

    • 我认为你走在正确的轨道上,尽管我认为像这样切换 AND 和 OR 会更好,对吧? (title:java OR body:java OR subject:java) AND (title:programming OR body:programming OR subject:programming) ...[处理附加条款]...我将在完成后发布我的实现以供其他人寻找一个可能的解决方案。谢谢你的答案。
    【解决方案3】:

    您希望能够使用相同的术语集搜索多个字段,然后是您评论中的问题:

    ((title:java title:programming) | (body:java body:programming))~0.2
    

    可能不是最好的实现。

    您实际上是从标题中获得分数,或者从正文中获得组合术语的分数。在标题中点击 java 并在正文中编程的情况将给出大约。与在体内对 java 的打击相等,对编程没有打击。

    我认为更好的结构化查询是:

    (title:java body:java)~0.2 (title:programming body:programming)~0.2
    

    这对我来说更有意义,因为您希望 dismax 查询限制同一术语(在不同字段中)的多个查询的得分增长,但我相信您确实希望不同术语的命中得分增长。

    如果这种查询结构可以让您获得更好的分数结果,则将结果限制在某个最低分数(返回的最高分数的百分比,而不是简单的硬编码值)可能足以防止太弱的结果被看见了。


    我仍然不会考虑为所有字段编制索引。这是我以前使用过的一种实现,同时索引特定字段和包罗万象的字段,从而允许通用查询和特定的单字段查询。对于未存储的术语,索引存储往往非常精简,如果您发现自己必须创建大而复杂的查询来弥补没有它的情况,它通常会提高性能。

    如果您真的想确保它占用最少的存储空间,您甚至可以关闭该字段的 TermVectors:

    new Field(name, value, Field.Store.NO, Field.Index.ANALYZED, Field.TermVector.NO);
    

    虽然我不知道这会产生多大的影响。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-08-09
      • 1970-01-01
      • 2021-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多