2016-06-13 4 views
2

Я хранимый следующие документы в моем индексе Lucene:Lucene.net - не может сделать многословный поиск

{ 
"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" 
} 

и теперь я хочу Seach по всем полям, чтобы проверить, если данное значение существует:

search term: "John data check" 

который sould мне вернуть документы с 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 !! 

ответ

3

Запрос, который вы создали, в основном требует соответствия всех трех слов.

Вы обертываете каждое слово в BooleanQuery с помощью пункта SHOULD. Это эквивалентно непосредственному использованию внутреннего запроса (вы просто добавляете косвенность, которая не изменяет поведение запроса). Логический запрос имеет только одно предложение, которое должно соответствовать логическому запросу.

Затем вы обертываете каждый из них в другой логический запрос, на этот раз с предложением MUST для каждого. Это означает, что каждое предложение должно соответствовать запросу для соответствия.

Для BooleanQuery, чтобы соответствовать все MUST положения должны быть удовлетворены, и если их нет, то как минимум MinimumNumberShouldMatchSHOULD положений должны быть удовлетворены. Оставьте это свойство по умолчанию, так как задокументированное поведение:

По умолчанию для соответствия не требуются необязательные оговорки (если нет необходимых условий).

Эффективно, ваш запрос (при условии, что нет 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")); 
    } 
} 
+0

, но когда я изменил код, как вы предложили, есть также возвращены те документы, которые не соответствуют критериям поиска – Tony

+0

Ммм он не должен иметь, не могли бы вы сказать, что такое 'mainQuery .ToString() 'печатает? –

+0

{((имя: Joh дополнительная информация: Joh) (имя: dat extraData: dat) (имя: chec extraData: chec)) ~ 1} – Tony

0

Ну, в моем случае я сохранил массив строк с тем же самым fie имя Л.Д., я должен был получить все значения поля из результата 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; 
Смежные вопросы