【问题标题】:Nesting searches in Lucene without duplicating keywords在 Lucene 中嵌套搜索而不重复关键字
【发布时间】:2011-11-04 15:26:00
【问题描述】:

我想使用以下逻辑在 Lucene(实际上是 Lucene.NET,但我可以根据需要从 Java 转换)中设置搜索:

  1. 搜索字符串为:A B C
  2. 在索引中的一个字段中搜索与 A、B 或 C 匹配的任何内容。(查询:(field1:A field1:B field1:C)
  3. 对于在第 2 步中不匹配的每个字词,在第二个字段中搜索它,同时保留第一次搜索的结果(查询:(+(field1:A) +(field2:B field2:C))
  4. 对于在步骤 3 中不匹配的每个字词,搜索第三个字段...
  5. 继续直到字段用完,或者搜索已用完所有术语。

目前,我的代码可以测试给定的搜索是否产生 NO 结果,并将所有产生结果的搜索结果组合在一起。但是在它对每个字段进行测试之前我没有办法阻止它(这不必要地限制了结果) - 它目前以如下查询结束:(+(field1:A field1:B field1:C) +(field3:A field3:B field3:C)) 当我希望它是(+(field1:A field1:C) +(field3:B)) 时。我不能只查看第一次搜索的结果并从搜索字符串中删除单词,因为分析器在解析搜索时会破坏单词,我无法解开它们以找出原始的它对应的搜索词。

有什么建议吗?


编辑: 好的,通常我更喜欢抽象地描述我的问题,但我认为其中的某些部分在这个过程中会丢失,所以我会更具体。

我正在为需要多层搜索逻辑的网站构建搜索引擎。我将追踪的一些示例搜索是:

  1. 耳机
  2. 怪兽耳机
  3. 白色怪兽耳机
  4. 白色 Foobar 耳机

索引包含具有七个字段的文档 - 与此示例相关的是:

  • “datattype”:一个字符串,表示该文档所代表的项目类型(产品、类别、品牌),因此我们知道如何显示它
  • “品牌”:相关的品牌(类别有多个品牌,产品和品牌各有一个)
  • “路径”:给定类别的路径(即“音频 > 耳机 > 入耳式”的“音频耳机入耳式”)
  • “关键词”:描述产品的各种不同之处。

总的来说,搜索的每一步的逻辑如下:

  1. 检查我们是否有匹配项。
  2. 如果是,请根据匹配项过滤结果,并在下一步中继续解析其余搜索词。
  3. 如果没有,请在下一步中解析搜索词。

每个步骤都类似于:

  • 搜索类别
  • 搜索品牌
  • 搜索关键字

以下是这三个示例搜索的结果:

  1. 耳机
    • 搜索类别:+path:headphones +datatype:Category
    • 有匹配项(耳机类别),原始查询中没有留下任何单词,因此我们将其返回。
  2. 怪物耳机
    • 搜索类别:`+(path:monster path:headphones) +datatype:Category
    • 找到了 path:headphonesdatatype:Category 的匹配项,导致“Monster”不匹配
    • 搜索品牌:+path:headphones +brand:monster
    • 找到了 path:headphonesbrand:monster 的匹配项,原始查询中没有留下任何单词,因此我们将 Monster 的所有耳机​​归还。
  3. 白色怪物耳机
    • 搜索类别:+(path:monster path:headphones path:white) +datatype:Category
    • 找到了 path:headphonesdatatype:Category 的匹配项,导致“White”和“Monster”不匹配
    • 搜索品牌:+path:headphones +(brand:monster +brand:white)
    • 找到了 path:headphonesbrand:monster 的匹配项,“白色”不匹配
    • 搜索关键字:+path:headphones +brand:monster +keywords:white
    • 有匹配项,原始查询中没有留下任何单词,因此我们返回它们。
  4. 白色 Foobar 耳机
    • 搜索类别:+(path:foobar path:headphones path:white) +datatype:Category
    • 找到了 path:headphonesdatatype:Category 的匹配项,“White”和“Foobar”不匹配
    • 搜索品牌:+path:headphones +(brand:foobar +brand:white)
    • 没有找到,所以我们继续。
    • 搜索关键字:+path:headphones +(keywords:white keywords:foobar)
    • 找到了 path:headphoneskeywords:white 的匹配项,导致“Foobar”不匹配
    • ...(继续搜索其他字段,包括产品描述)...
    • 仍有未匹配的搜索词(“Foobar”),返回“未找到结果”

我的问题是双重的:

  1. 希望在一切都匹配后继续匹配(只有产品有描述,所以一旦达到该步骤,我们将永远不会返回不是产品的东西)。我可以使用来自here 的denis 的GetHitTerms 来管理这个问题,但我最终会在所有后续字段中搜索第一个匹配的术语,直到所有内容都匹配(即在示例#2 中,我将拥有+path:headphones +(brand:headphones brand:monster))。李>
  2. 尽管上面有我的示例,但我在路径字段上的实际搜索查询看起来像+path:headphon +datatype:Taxonomy,因为我正在修改它以进行搜索。所以我不能把匹配的词从原始查询中删除(因为“headphon”!=“headphones”)。

希望这能让我更清楚我在寻找什么。

【问题讨论】:

  • 你看过这个吗? cwiki.apache.org/LUCENENET/simple-faceted-search.html可能有你可以使用的想法。
  • @denis:乍一看,这确实是我需要的。我需要更深入地研究它才能确定,但​​它看起来确实很有希望。
  • @denis:好消息和坏消息。它似乎确实是我需要的,但我的数据集太大了,无法处理。即使我提高了 MAX_FACETS 常量。我有 455 个品牌词、251 个路径词和 10094 个关键字词,仅来自上面的字段。如果它涵盖了除描述之外的所有方面,它的权重将接近 4 万亿个方面。
  • 我敢打赌,您还没有阅读该页面中的References to adding faceted to Lucene.Net section。还有其他利用 Collector 类进行分面搜索的技巧。见mail-archives.apache.org/mod_mbox/lucene-lucene-net-dev/…
  • @denis:我现在已经阅读了,但我不明白如何处理结果,或者如何告诉它我希望多个字段成为我的搜索的一部分。

标签: java lucene lucene.net


【解决方案1】:

我不了解您的用例,但您听起来像是在询问 BooleanQuery API。您可以通过调用getClauses 来获取查询的子句。

一个简单的例子:

BooleanQuery bq = new BooleanQuery();
bq.add(new TermQuery(new Term("field1","a")), BooleanClause.Occur.SHOULD)
bq.add(new TermQuery(new Term("field1","b")), BooleanClause.Occur.SHOULD)

BooleanClause[] clauses = bq.getClauses();

编辑:也许您只是要求搜索算法。在伪代码中:

generate_query (qs_that_matched, qs_that_didnt_match, level):
   new_query = qs_that_matched AND level:qs_that_didnt_match
   qs_still_unmatched = ...
   qs_which_just_matched = ...
   if qs_still_unmatched != null:
      return generate_query(qs_that_matched AND qs_which_just_matched, qs_still_unmatched, level+1)
   else:
      return qs_that_matched AND qs_which_just_matched

【讨论】:

  • 这只是第一步。我想运行bq,看到只有field1:a 匹配,然后用b 做其他事情。具体来说,我想试试 (+field1:a +field2:b) 看看它是否同时匹配 a 和 b。这说明清楚了吗?
  • @Bobson:我已经编辑了我的答案以提供搜索算法,因为也许这就是你想要的?
  • 这更接近我的需要。我一直在考虑这些方面的事情,但我无法弄清楚如何像这样优雅地做到这一点(我在考虑迭代而不是递归)。但是, ... 部分让我很困惑。根据我之前的问题 (stackoverflow.com/questions/7896183/…),我知道如何 get 匹配的术语,但不知道如何获取生成我刚刚匹配(或未能匹配)的术语的查询字符串部分)。这不仅仅是字符串匹配,因为它们会被分析器破坏。
  • @Bobson:如果我的伪代码是正确的,那么您将永远不会有嵌套查询。因此,省略号可以填写为匹配/不匹配项的连词/并集。
  • 我编辑了问题以提供更具体的示例。这是否有助于澄清我在寻找什么?
【解决方案2】:

最后,我构建了一个 QueryTree 类并将查询存储在树结构中。它存储对接受查询的函数的引用、要注入该查询的术语列表、是否应该与或或这些术语,以及子项列表(表示匹配项的唯一组合)。

要执行下一级搜索,我只需在树中最深的节点上调用Evaluate(Func<string, QueryParser.Operator, Query> newQuery),并引用一个函数,该函数采用术语和运算符并为该组逻辑返回正确的查询。然后,Evaluate 函数针对已传递给它的不匹配术语列表和所有祖先查询的结果集(通过与父项进行与,与父项进行与等等)测试该新查询。然后,它使用GetHitTerms 为每组匹配项创建子项,并将不匹配的项提供给子项。对每个级别的搜索重复。


我怀疑有更好的方法来做到这一点 - 我什至没有研究 Xodarap 提到的 Bobo,而且我从来没有真正得到分面搜索(根据丹尼斯)的工作。但是,它正在工作,这意味着是时候转移到网站的其他方面了。

【讨论】:

  • 如果有人想要,我可以发布有问题的代码,但我不确定它是否会添加任何内容,而且它会使答案显着延长。
猜你喜欢
  • 2015-10-15
  • 2016-12-16
  • 2011-01-07
  • 1970-01-01
  • 1970-01-01
  • 2011-12-02
  • 1970-01-01
  • 2016-08-16
  • 1970-01-01
相关资源
最近更新 更多