使用Lucene SpanQueries进行句子感知搜索

时间:2012-05-15 03:17:59

标签: search lucene sentence

是否可以使用Lucene SpanQuery查找所有出现“red”“green”和“blue”都出现在一个句子中的事件?

我的第一个(不完整/不正确)方法是编写一个分析器,将一个特殊的句子标记符号和一个句子的开头放在与该句子的第一个单词相同的位置,然后查询类似于下面的内容:

SpanQuery termsInSentence = new SpanNearQuery(
  SpanQuery[] {
    new SpanTermQuery( new Term (MY_SPECIAL_SENTENCE_TOKEN)),
    new SpanTermQuery( new Term ("red")),
    new SpanTermQuery( new Term ("green")),
    new SpanTermQuery( new Term ("blue")),
  },
  999999999999,
  false
);

SpanQuery nextSentence = new SpanTermQuery( new Term (MY_SPECIAL_SENTENCE_TOKEN));

SpanNotQuery notInNextSentence = new SpanNotQuery(termsInSentence,nextSentence);

问题当然是nextSentence不是下一个句子,它是任何句子标记,包括句子中的那个termsInSentence匹配。因此这不起作用。

我的下一个方法是创建分析器,将令牌放在句子之前(即之前第一个单词,而不是与第一个单词位于同一位置)。这个问题是我必须考虑由MY_SPECIAL_SENTENCE_TOKEN引起的额外偏移。更重要的是,当我使用天真模式来分割句子时(例如,在/\.\s+[A-Z0-9]/上拆分),这首先会特别糟糕,因为我在搜索时必须考虑所有(错误)句子标记为 U. S. S. Enterprise

那么......我该怎么做呢?

2 个答案:

答案 0 :(得分:1)

我会将每个句子索引为Lucene文档,包括一个标记该句子源文档的字段。根据您的源材料,句子/ LuceneDoc的开销可能是可以接受的。

答案 1 :(得分:0)

实际上,看起来你非常接近解决方案。我认为索引一个句末标志是一个很好的方法。问题是你的句末标志在你的SpanNearQuery中,这就是让你失望的原因。您要求它找到两个包含且包含MY_SPECIAL_SENTENCE_TOKEN的范围。查询自相矛盾,因此,当然,它不会找到任何匹配。你真正需要知道的是,三个术语(“红色”,“绿色”和“蓝色”)出现在一个不与MY_SPECIAL_SENTENCE_TOKEN重叠的范围内(也就是说,句子标记不会出现在这些术语之间)。

此外,Term ctors中缺少字段名称会有问题,但Lucene 应该抛出一个抱怨的异常,所以猜测这不是真正的问题。可能是当时编写的Lucene版本没有抱怨SpanNears中的字段不匹配,所以也许值得一提。

这似乎对我有用:

SpanQuery termsInSentence = new SpanNearQuery(
    new SpanQuery[] {
        new SpanTermQuery( new Term ("text", "red")),
        new SpanTermQuery( new Term ("text", "green")),
        new SpanTermQuery( new Term ("text", "blue")),
    },
    9999, 
    false
);

SpanQuery nextSentence = new SpanTermQuery( new Term ("text", MY_SPECIAL_SENTENCE_TOKEN));

SpanQuery notInNextSentence = new SpanNotQuery(termsInSentence,nextSentence);

至于在哪里分割句子,我会尝试使用java.text.Breakiterator而不是使用天真的正则表达式方法。它并不完美,但它做得非常好。