Lucene.net - 无法进行多字搜索

时间:2016-06-13 15:34:42

标签: c# lucene

我已将以下文件存储在我的lucene索引中:

{
"id" : 1,
"name": "John Smith"
"description": "worker"
"additionalData": "faster data"
"attributes": "is_hired=not"
},
{
"id" : 2,
"name": "Alan Smith"
"description": "hired"
"additionalData": "faster drive"
"attributes": "is_hired=not"
},
{
"id" : 3,
"name": "Mike Std"
"description": "hired"
"additionalData": "faster check"
"attributes": "is_hired=not"
}

现在我想搜索所有字段以检查给定值是否存在:

search term: "John data check"

我可以用ID 1 and 3返回文件。但它没有,为什么?

var analyzer = new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30);

BooleanQuery mainQuery = new BooleanQuery();
mainQuery.MinimumNumberShouldMatch = 1;

var cols = new string[] {
                         "name",
                         "additionalData"
                        };

 string[] words = searchData.text.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);

 var queryParser = new MultiFieldQueryParser(Lucene.Net.Util.Version.LUCENE_30, cols, analyzer);

 foreach (var word in words)
 {
    BooleanQuery innerQuery = new BooleanQuery();
    innerQuery.MinimumNumberShouldMatch = 1;

    innerQuery.Add(queryParser.Parse(word), Occur.SHOULD);

    mainQuery.Add(innerQuery, Occur.MUST);
 }

 TopDocs hits = searcher.Search(mainQuery, null, int.MaxValue, Sort.RELEVANCE);

 //hits.TotalHits is 0 !!

2 个答案:

答案 0 :(得分:3)

您构建的查询基本上需要所有三个单词匹配。

您使用BooleanQuery子句将每个单词包装在SHOULD中。这相当于直接使用内部查询(您只是添加一个不会改变查询行为的间接)。布尔查询只有一个子句,它应匹配要匹配的布尔查询。

然后,将这些中的每一个包装在另一个布尔查询中,这次使用MUST子句。这意味着每个子句必须匹配查询才能匹配。

要匹配BooleanQuery,必须满足所有MUST条款,如果没有,则必须满足至少MinimumNumberShouldMatch SHOULD条款。将该属性保留为其默认值,因为记录的行为是:

  

默认情况下,匹配不需要任何可选子句(除非没有必需的子句)。

实际上,您的查询是(假设为了简单起见没有MultiFieldQueryParser):

+(john) +(data) +(check)

或者,以树形式:

BooleanQuery
    MUST: BooleanQuery
        SHOULD: TermQuery: john
    MUST: BooleanQuery
        SHOULD: TermQuery: data
    MUST: BooleanQuery
        SHOULD: TermQuery: check

可以简化为:

BooleanQuery
    MUST: TermQuery: john
    MUST: TermQuery: data
    MUST: TermQuery: check

但您想要的查询是:

BooleanQuery
    SHOULD: TermQuery: john
    SHOULD: TermQuery: data
    SHOULD: TermQuery: check

所以,移除mainQuery.MinimumNumberShouldMatch = 1;行,然后用以下内容替换您的foreach正文,它应该完成工作:

mainQuery.Add(queryParser.Parse(word), Occur.SHOULD);

好的,这是一个完整的例子,对我有用:

var analyzer = new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30);

var directory = new RAMDirectory();

using (var writer = new IndexWriter(directory, analyzer, true, IndexWriter.MaxFieldLength.UNLIMITED))
{
    var doc = new Document();
    doc.Add(new Field("id", "1", Field.Store.YES, Field.Index.NOT_ANALYZED));
    doc.Add(new Field("name", "John Smith", Field.Store.NO, Field.Index.ANALYZED));
    doc.Add(new Field("additionalData", "faster data", Field.Store.NO, Field.Index.ANALYZED));
    writer.AddDocument(doc);

    doc = new Document();
    doc.Add(new Field("id", "2", Field.Store.YES, Field.Index.NOT_ANALYZED));
    doc.Add(new Field("name", "Alan Smith", Field.Store.NO, Field.Index.ANALYZED));
    doc.Add(new Field("additionalData", "faster drive", Field.Store.NO, Field.Index.ANALYZED));
    writer.AddDocument(doc);

    doc = new Document();
    doc.Add(new Field("id", "3", Field.Store.YES, Field.Index.NOT_ANALYZED));
    doc.Add(new Field("name", "Mike Std", Field.Store.NO, Field.Index.ANALYZED));
    doc.Add(new Field("additionalData", "faster check", Field.Store.NO, Field.Index.ANALYZED));
    writer.AddDocument(doc);
}

var words = new[] {"John", "data", "check"};
var parser = new MultiFieldQueryParser(Lucene.Net.Util.Version.LUCENE_30, new[] {"name", "additionalData"}, analyzer);


var mainQuery = new BooleanQuery();
foreach (var word in words)
    mainQuery.Add(parser.Parse(word), Occur.SHOULD); // Should probably use parser.Parse(QueryParser.Escape(word)) instead

using (var searcher = new IndexSearcher(directory))
{
    var results = searcher.Search(mainQuery, null, int.MaxValue, Sort.RELEVANCE);
    var idFieldSelector = new MapFieldSelector("id");

    foreach (var scoreDoc in results.ScoreDocs)
    {
        var doc = searcher.Doc(scoreDoc.Doc, idFieldSelector);
        Console.WriteLine("Found: {0}", doc.Get("id"));
    }
}

答案 1 :(得分:0)

嗯,在我的情况下,我存储了一个具有相同字段名称的字符串数组,我必须从结果Document中检索所有字段值,因为Document.Get("field_name")仅返回第一列值有很多相同方式的领域

var multi_fields = doc.GetFields("field_name");
var field_values = multi_fields.Select(x => x.StringValue).ToArray();

另外,我必须启用WildCard搜索,因为如果我不输入完整的单词,它会失败,例如Jo代替John

 string[] words = "Jo data check".Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries).Select(x => string.Format("*{0}*", x)).ToArray();

 var queryParser = new MultiFieldQueryParser(Lucene.Net.Util.Version.LUCENE_30, cols, analyzer);
 parser.AllowLeadingWildcard = true;