在lucene BooleanQuery中使用空格匹配整个句子

时间:2017-05-18 16:37:16

标签: solr lucene lucene.net

我有一个搜索字符串,

Tulip INN Riyadhh
 Tulip INN Riyadhh LUXURY
 Suites of Tulip INN RIYAHdhh

如果我提及

,我需要搜索字词
 *Tulip INN Riyadhh*

它必须返回上面的所有三个,我有限制,我必须实现这个没有QueryParser或Analyzer,它必须只有BooleanQuery / WildCardQuery /等....

此致 拉加

2 个答案:

答案 0 :(得分:3)

这里需要的是PhraseQuery。让我解释一下。

我不知道你正在使用哪种分析仪,但我认为你有一个非常简单的分析器,它只是将文本转换为小写。不要告诉我你没有使用anlayzer,因为Lucene必须完成任何工作,至少在索引阶段 - 这就是定义标记器和令牌过滤器链的原因。 / p>

以下是此示例中字符串的标记方式:

  • tulip inn ryiadhh
  • tulip inn ryiadhh luxury
  • suites of tulip inn ryiadhh

请注意这些都包含令牌序列tulip inn ryiadhh。一系列令牌就是PhraseQuery正在寻找的东西。

在Lucene.Net构建中,这样的查询看起来像这样(未经测试):

var query = new PhraseQuery();
query.Add(new Term("propertyName", "tulip"));
query.Add(new Term("propertyName", "inn"));
query.Add(new Term("propertyName", "ryiadhh"));

请注意,这些术语需要与分析器生成的术语相匹配(在此示例中,它们全部为小写)。 QueryParser通过分析器运行部分查询,为您完成这项工作,但如果您不使用解析器,则必须自己完成。

现在,为什么不会WildcardQueryRegexQuery在这种情况下工作?这些查询始终与单个术语匹配,但您需要匹配有序的术语序列。例如,WildcardQuery一词Riyadhh*会找到所有以Riyadhh 开头的词。

包含BooleanQuery TermQuery条款集合的MUST将匹配任何恰好包含这3个字词的文本 - 不完全符合您的要求。

答案 1 :(得分:1)

Lucas有正确的想法,但有一个更专业的Create Table Temp1 (Load_Dt Date, ID Number, Name Varchar2(10)); 可用于根据索引中已有的数据构建查询,以获得前缀匹配demonstrated in this unit testMultiPhraseQuery的文档内容为:

  

MultiPhraseQueryMultiPhraseQuery的通用版本,添加了方法PhraseQuery。要使用此类,要搜索短语" Microsoft app *"首先在术语" Microsoft"上使用Add(Term[]),然后找到所有包含" app"使用Add(Term)作为前缀,并使用IndexReader.GetTerms(Term)将其添加到查询中。

正如Lucas指出的那样,MultiPhraseQuery.Add(Term[] terms) *something是进行后缀匹配的方法,前提是您了解其性能影响。

然后可以将它们与WildCardQuery组合以获得您想要的结果。

BooleanQuery

WriteIndex

using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.Search;
using Lucene.Net.Store;
using Lucene.Net.Util;
using System;
using System.Collections.Generic;

namespace LuceneSQLLikeSearch
{
    class Program
    {
        static void Main(string[] args)
        {
            // Prepare...
            var dir = new RAMDirectory();
            var writer = new IndexWriter(dir, 
                new IndexWriterConfig(LuceneVersion.LUCENE_48, 
                new StandardAnalyzer(LuceneVersion.LUCENE_48)));

            WriteIndex(writer);

            // Search...
            var reader = writer.GetReader(false);

            // Get all terms that end with tulip
            var wildCardQuery = new WildcardQuery(new Term("field", "*tulip"));
            var multiPhraseQuery = new MultiPhraseQuery();

            multiPhraseQuery.Add(new Term("field", "inn"));

            // Get all terms that start with riyadhh
            multiPhraseQuery.Add(GetPrefixTerms(reader, "field", "riyadhh"));

            var query = new BooleanQuery();
            query.Add(wildCardQuery, Occur.SHOULD);
            query.Add(multiPhraseQuery, Occur.SHOULD);

            var result = ExecuteSearch(writer, query);

            foreach (var item in result)
            {
                Console.WriteLine("Match: {0} - Score: {1:0.0########}", 
                    item.Value, item.Score);
            }

            Console.ReadKey();
        }
    }
}

GetPrefixTerms

在这里,我们扫描索引以查找以传入前缀开头的所有术语。然后将这些条款添加到public static void WriteIndex(IndexWriter writer) { Document document; document = new Document(); document.Add(new TextField("field", "Tulip INN Riyadhh", Field.Store.YES)); writer.AddDocument(document); document = new Document(); document.Add(new TextField("field", "Tulip INN Riyadhh LUXURY", Field.Store.YES)); writer.AddDocument(document); document = new Document(); document.Add(new TextField("field", "Suites of Tulip INN RIYAHdhh", Field.Store.YES)); writer.AddDocument(document); document = new Document(); document.Add(new TextField("field", "Suites of Tulip INN RIYAHdhhll", Field.Store.YES)); writer.AddDocument(document); document = new Document(); document.Add(new TextField("field", "myTulip INN Riyadhh LUXURY", Field.Store.YES)); writer.AddDocument(document); document = new Document(); document.Add(new TextField("field", "some bogus data that should not match", Field.Store.YES)); writer.AddDocument(document); writer.Commit(); }

MultiPhraseQuery

ExecuteSearch

public static Term[] GetPrefixTerms(IndexReader reader, string field, string prefix)
{
    var result = new List<Term>();
    TermsEnum te = MultiFields.GetFields(reader).GetTerms(field).GetIterator(null);
    te.SeekCeil(new BytesRef(prefix));
    do
    {
        string s = te.Term.Utf8ToString();
        if (s.StartsWith(prefix, StringComparison.Ordinal))
        {
            result.Add(new Term(field, s));
        }
        else
        {
            break;
        }
    } while (te.Next() != null);

    return result.ToArray();
}

信息搜索结果

public static IList<SearchResult> ExecuteSearch(IndexWriter writer, Query query)
{
    var result = new List<SearchResult>();
    var searcherManager = new SearcherManager(writer, true, null);
    // Execute the search with a fresh indexSearcher
    searcherManager.MaybeRefreshBlocking();

    var searcher = searcherManager.Acquire();
    try
    {
        var topDocs = searcher.Search(query, 10);
        foreach (var scoreDoc in topDocs.ScoreDocs)
        {
            var doc = searcher.Doc(scoreDoc.Doc);
            result.Add(new SearchResult
            {
                Value = doc.GetField("field")?.GetStringValue(),
                // Results are automatically sorted by relevance
                Score = scoreDoc.Score,
            });
        }
    }
    catch (Exception e)
    {
        Console.WriteLine(e.ToString());
    }
    finally
    {
        searcherManager.Release(searcher);
        searcher = null; // Don't use searcher after this point!
    }

    return result;
}

如果这看起来很麻烦,请注意public class SearchResult { public string Value { get; set; } public float Score { get; set; } } 可以模仿&#34; SQL LIKE&#34;查询。正如here所指出的那样,QueryParser上的AllowLeadingWildCard可以选择轻松构建正确的查询序列。目前还不清楚为什么你有一个你不能使用它的约束,因为它绝对是完成工作的最简单方法。