【问题标题】:Lucene.NET query with multiple forward slashes带有多个正斜杠的 Lucene.NET 查询
【发布时间】:2017-02-21 22:51:17
【问题描述】:

多年来,我们一直在使用 Lucene.NET 根据用户输入的搜索词来搜索从文件中提取的文本。但是,我们最近遇到了一个客户报告的问题,即搜索具有多个正斜杠的术语时没有返回匹配项。

例如,SB/ABC/1234-123 的索引值和用户输入 SB/* 以匹配具有该前缀的所有文档。但是,不会根据该查询返回任何结果。奇怪的是搜索ABC/* 确实返回值为SB/ABC/1234-123 的文档,完全忽略了SB/ 组件。

最初报告的问题是正斜杠和通配符的组合(SB/* 不会返回与 SB/1234-123 的匹配项),但通过使用 QueryParserKeywordAnalyzer 以及除了以前的 QueryParser 只有一个 StandardAnalyzer

这是当前使用的代码(简化为可以重现问题的关键元素)。

var reader = IndexReader.Open(FSDirectory.Open(new DirectoryInfo(indexPath)), true);
var searcher = new IndexSearcher(reader);
var mainQuery = new BooleanQuery();

// The analyzer and parser for searching the index fields with full stop-words and tokenizers
var fieldAnalyzer = new StandardAnalyzer(LuceneVersion);
var fieldParser = new MultiFieldQueryParser(LuceneVersion, reader.GetFieldNames(IndexReader.FieldOption.ALL).ToArray(), fieldAnalyzer);

// The analyzer and parser for searching the index fields using no stop words or tokenizers
var fieldKeywordAnalyzer = new KeywordAnalyzer();
var fieldKeywordParser = new MultiFieldQueryParser(LuceneVersion, reader.GetFieldNames(IndexReader.FieldOption.ALL).ToArray(), fieldKeywordAnalyzer);

// Build and append the Standard and Keyword query clauses together for the whole field value query to pick up all relevant results
var fieldQuery = fieldParser.Parse(textCriteria);
var fieldKeywordQuery = fieldKeywordParser.Parse(textCriteria);

var fieldBooleanQuery = new BooleanQuery
{
    {fieldQuery, Occur.SHOULD},
    {fieldKeywordQuery, Occur.SHOULD}
};

mainQuery.Add(fieldBooleanQuery, Occur.MUST);

var hits = searcher.Search(mainQuery, reader.NumDocs());

在调用searcher.Search 时,在mainQuery 中实际解析的查询是+((Title:sb/abc/*) (Title:sb/abc/*))。在这种情况下,BooleanQuery 的两个子句恰好是相同的。通常用于处理 Lucene 索引的 Luke 工具似乎认为在使用 KeywordAnalyzer 时这是无效语法(暂时忽略标记化方面):

Cannot parse '+((Title:sb/abc/*) (Title:sb/abc/*))': '*' or '?' not allowed as first character in WildcardQuery.

我的假设是拥有两个正斜杠使其将其视为正则表达式。问题是我们如何让它正确匹配结果而不是将其视为正则表达式。转义搜索条件中的斜杠不会改变上面看到的解析查询或返回的结果。

我们当前的要求是它必须同时支持相同字段上的标记化/停用词搜索(用于文本短语等)以及完全匹配(我们存储大量不应标记化的发票号码等),并且都处理通配符。 SB/* 查询是在完全匹配值场景中进行通配符搜索的示例。

希望这是有道理的。如果需要,我可以添加更多说明。

编辑:我们的数据被组织成许多列,可以存储任何文本值。示例:一些客户将一个唯一/ID 值(发票号等)放入 doctype A 的字段 1。同一客户可以使用字段 1 作为 doctype B 的文本块(全名等)。文档的分类,从高层次上描述特定文档应该代表什么,例如发票、采购订单等。示例数据:

DocId  DocType  Field1    Field2    Field3, etc
1234   A        SB/2567   John Doe
5678   B        Jane Doe  90210  
3456   A        ABC/5678  Bobby Lee

【问题讨论】:

  • StandardAnalyzer 将您的示例发票编号 SB/ABC/1234-123 视为两个标记:[sb] [abc/1234-123]。这就是 'ABC/*' 匹配的原因。
  • 这就是为什么我们也添加了关键字分析器,以便它搜索标记化(标准)值和非标记化(关键字)。在这种情况下,它似乎忽略了 KeywordAnalyzer。
  • @DanielBrixen 抱歉,没有在我的回复中标记你,它不会让我编辑它。

标签: .net lucene lucene.net


【解决方案1】:

就像@Daniel Brixen 评论的那样,StandardAnalyzer 并没有像你希望的那样做。

您应该考虑滚动您自己的分析器。 Here's a .net fiddle 演示了 SlashAnalyzer(它可能以您想要的方式运行)以及演示 StandardAnalyzer 正在产生什么。

这是 Slash/Analyzer/Tokenizer 的亮点。

public sealed class SlashAnalyzer : Analyzer
{
    public override TokenStream TokenStream(System.String fieldName, System.IO.TextReader reader)
    {
        return new LowerCaseFilter(new SlashTokenizer(reader));
    }

    public override TokenStream ReusableTokenStream(System.String fieldName, System.IO.TextReader reader)
    {
        Tokenizer tokenizer = (Tokenizer)PreviousTokenStream;
        if (tokenizer == null)
        {
            tokenizer = new SlashTokenizer(reader);
            PreviousTokenStream = tokenizer;
        }
        else
            tokenizer.Reset(reader);
        return tokenizer;
    }
}

public class SlashTokenizer : CharTokenizer
{
    public SlashTokenizer(System.IO.TextReader @in) : base(@in)
    {
    }

    public SlashTokenizer(AttributeSource source, System.IO.TextReader @in) : base(source, @in)
    {
    }

    public SlashTokenizer(AttributeFactory factory, System.IO.TextReader @in) : base(factory, @in)
    {
    }

    protected override bool IsTokenChar(char c)
    {
        return c != '/';
    }
}

【讨论】:

  • 如果斜线是我们唯一要寻找的东西,这看起来会起作用,但从一个文档到另一个文档,它可能有斜线场景,或者只是可以使用标准或关键字的普通文本值。不过我会试试看会发生什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-16
  • 1970-01-01
相关资源
最近更新 更多