2017-08-11 179 views
1

我開始使用Lucene.Net 4.8演示項目(https://github.com/synhershko/LuceneNetDemo)。我的目標是能夠使用查詢解析器(QueryParser或MultiFieldQueryParser)來搜索文本以及數字。那可能嗎?我找到的所有例子都是使用範圍(NumericRangeQuery),或者是建立我自己的查詢解析器的建議。我無法確定是否可以通過現有的查詢解析器創建範圍?可以Lucene.Net(3.0.3或4.8.0)QueryParser搜索數字嗎?

using System; 
using Lucene.Net.Store; 
using Lucene.Net.Documents; 
using Lucene.Net.Index; 
using Lucene.Net.Util; 
using Lucene.Net.QueryParsers.Classic; 
using Lucene.Net.Search; 
using Lucene.Net.Analysis.Standard; 

/* 
Package Manager: 
Install-Package Lucene.Net -Version 4.8.0-beta00004 -Pre 
Install-Package Lucene.Net.Analysis.Common -Version 4.8.0-beta00004 -Pre 
Install-Package Lucene.Net.QueryParser -Version 4.8.0-beta00004 -Pre 
*/ 

namespace LuceneNetNumbers 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      LuceneVersion MatchVersion = LuceneVersion.LUCENE_48; 

      using (var oDirectory = new RAMDirectory()) 
      { 
       var oAnalyzer = new StandardAnalyzer(MatchVersion); 
       var oQueryParser = new MultiFieldQueryParser(MatchVersion, new[] { "name", "height", "age" }, oAnalyzer); 
       var oIndexWriterConfig = new IndexWriterConfig(MatchVersion, oAnalyzer); 
       var oIndexWriter = new IndexWriter(oDirectory, oIndexWriterConfig); 
       var oSearcherManager = new SearcherManager(oIndexWriter, true, null); 

       var oAdd = new Action<string, double, int>((sName, nAge, nHeight) => 
       { 
        var oDocument = new Document 
        { 
         new TextField("name", sName, Field.Store.YES), 
         new Int32Field("height", nHeight, Field.Store.YES), 
         new DoubleField("age", nAge, Field.Store.YES), 
        }; 

        oIndexWriter.UpdateDocument(new Term("name", sName), oDocument); 
       }); 

       oAdd("John Doe", 24.45, 56); 
       oAdd("John Smith", 44.44, 64); 
       oAdd("Mike Smith", 56.65, 70); 

       oIndexWriter.Flush(true, true); 
       oIndexWriter.Commit(); 

       // 

       var oSearch = new Action<string>((sQueryString) => 
       { 
        var oQuery = oQueryParser.Parse(sQueryString); 
        oSearcherManager.MaybeRefreshBlocking(); 
        var oSearcher = oSearcherManager.Acquire(); 

        try 
        { 
         var oTopDocs = oSearcher.Search(oQuery, 10); 
         var nTotalHits = oTopDocs.TotalHits; 
         Console.WriteLine("Total Hits: {0}", nTotalHits); 

         foreach (var oResult in oTopDocs.ScoreDocs) 
         { 
          var oDocument = oSearcher.Doc(oResult.Doc); 

          var nScore = oResult.Score; 
          var sName = oDocument.GetField("name")?.GetStringValue(); 
          var nAge = oDocument.GetField("age")?.GetNumericValue(); 
          var nHeight = oDocument.GetField("height")?.GetNumericValue(); 

          Console.WriteLine("{0:0.00}, {1,15}, {2,8}, {3,8}", nScore, sName, nAge, nHeight); 
         } 
        } 
        catch (Exception e) 
        { 
         Console.WriteLine(e.ToString()); 
        } 
        finally 
        { 
         oSearcherManager.Release(oSearcher); 
         oSearcher = null; 
        } 
       }); 

       oSearch("john"); 
       oSearch("height:64"); 

       /* 
       Output: 
       Total Hits: 2 
       0.20,  John Doe, 24.45,  56 
       0.20,  John Smith, 44.44,  64 
       Total Hits: 0 
       */ 
      } 
     } 
    } 
} 
+0

如果你只是搜索'64',我希望它會返回你一擊。 –

+0

不幸的是,它返回零點。我嘗試了許多變體(oSearch(「64」),oSearch(「'64'」),oSearch(「\」64 \「」)),但似乎查詢解析器不適用於數字。 –

回答

0

它有事情做與方式Lucene.Net節省數值(編碼形式):

new Int32Field("height", nHeight, Field.Store.YES) 

你可以使用一個NumericRangeQuery

var oQuery = NumericRangeQuery.NewInt32Range("height", 64, 64, true, true); 

使用你的號碼」重新嘗試搜索minmax值。

另一種選擇是使用TermQuery和轉換你的電話號碼爲BytesRef

BytesRef bytes = new BytesRef(NumericUtils.BUF_SIZE_INT32); 
NumericUtils.Int32ToPrefixCoded(64, 0, bytes); 
Term term = new Term("height", bytes); 
var oQuery = new TermQuery(term); 

當然,你將無法解析您的查詢字符串形式,但你總是可以創建自己的解析器在這裏你結合方面:

BytesRef bytes = new BytesRef(NumericUtils.BUF_SIZE_INT32); 
NumericUtils.Int32ToPrefixCoded(64, 0, bytes); 
Term term = new Term("height", bytes); 
// var oQuery = new TermQuery(term); 

var oQuery = new BooleanQuery 
{ 
    { new TermQuery(new Term("name", "John")), Occur.SHOULD }, 
    { new TermQuery(term), Occur.SHOULD } 
}; 

你可以看到查詢是如何翻譯成字符串

enter image description here

+0

感謝您的回答。雖然這是一個如何搜索數字的好例子,但它不使用查詢分析器。 –

0

雖然我並不完全滿意,我已經想出,它確實滿意,我使用查詢分析器的要求,搜索數字...和文字用原來的例子所暗示的。我將繼續研究如何在字符串和數字中使用StandardQueryParser.SetMultiFields和StandardQueryParser.NumericConfigMap,並在此處編輯/發佈任何發現。

using Lucene.Net.Analysis.Standard; 
using Lucene.Net.Documents; 
using Lucene.Net.Index; 
using Lucene.Net.QueryParsers.Flexible.Standard; 
using Lucene.Net.QueryParsers.Flexible.Standard.Config; 
using Lucene.Net.Search; 
using Lucene.Net.Store; 
using Lucene.Net.Support; 
using Lucene.Net.Util; 
using System; 
using System.Globalization; 

/* 
Package Manager: 
Install-Package Lucene.Net -Version 4.8.0-beta00004 -Pre 
Install-Package Lucene.Net.Analysis.Common -Version 4.8.0-beta00004 -Pre 
Install-Package Lucene.Net.QueryParser -Version 4.8.0-beta00004 -Pre 
*/ 

namespace LuceneNetNumbers 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      LuceneVersion MatchVersion = LuceneVersion.LUCENE_48; 

      using (var oDirectory = new RAMDirectory()) 
      { 
       var oAnalyzer = new StandardAnalyzer(MatchVersion); 

       //########## 
       //########## 

       //List of changes... 

       //1. Remove this. 
       //var oQueryParser = new MultiFieldQueryParser(MatchVersion, new[] { "name", "height", "age" }, oAnalyzer); 

       //2. Add the following 6 lines of code. 
       var oQueryParser = new StandardQueryParser(oAnalyzer); 

       var oNumericConfigMap = new HashMap<string, NumericConfig>(); 
       oNumericConfigMap.Put("height", new NumericConfig(8, new NumberFormatIgnoreExceptions(CultureInfo.CurrentCulture), NumericType.INT32)); 
       oNumericConfigMap.Put("age", new NumericConfig(8, new NumberFormatIgnoreExceptions(CultureInfo.CurrentCulture), NumericType.DOUBLE)); 
       oQueryParser.NumericConfigMap = oNumericConfigMap; 

       oQueryParser.SetMultiFields(new[] { "name", "height", "age" }); 

       //3. Add null as second parameter to StandardQueryParser.Parse below to utilize StandardQueryParser.SetMultiFields 

       //4. Create NumberFormatIgnoreExceptions. I was not able to find another way (yet) to get 
       //StandardQueryParser.SetMultiFields and StandardQueryParser.NumericConfigMap to work with 
       //both text and number fields. I feel like this is a bit of a hack, but it does satisfiy my 
       //requirement of using a query parser to search for numbers (and text... implied by example). 

       //########## 
       //########## 

       var oIndexWriterConfig = new IndexWriterConfig(MatchVersion, oAnalyzer); 
       var oIndexWriter = new IndexWriter(oDirectory, oIndexWriterConfig); 
       var oSearcherManager = new SearcherManager(oIndexWriter, true, null); 

       var oAdd = new Action<string, double, int>((sName, nAge, nHeight) => 
       { 
        var oDocument = new Document 
        { 
         new TextField("name", sName, Field.Store.YES), 
         new Int32Field("height", nHeight, Field.Store.YES), 
         new DoubleField("age", nAge, Field.Store.YES), 
        }; 

        oIndexWriter.UpdateDocument(new Term("name", sName), oDocument); 
       }); 

       oAdd("John Doe", 24.45, 56); 
       oAdd("John Smith", 44.44, 64); 
       oAdd("Mike Smith", 56.65, 70); 

       oIndexWriter.Flush(true, true); 
       oIndexWriter.Commit(); 

       // 

       var oSearch = new Action<string>((sQueryString) => 
       { 
        var oQuery = oQueryParser.Parse(sQueryString, null); 
        oSearcherManager.MaybeRefreshBlocking(); 
        var oSearcher = oSearcherManager.Acquire(); 

        try 
        { 
         var oTopDocs = oSearcher.Search(oQuery, 10); 
         var nTotalHits = oTopDocs.TotalHits; 
         Console.WriteLine("Total Hits: {0}", nTotalHits); 

         foreach (var oResult in oTopDocs.ScoreDocs) 
         { 
          var oDocument = oSearcher.Doc(oResult.Doc); 

          var nScore = oResult.Score; 
          var sName = oDocument.GetField("name")?.GetStringValue(); 
          var nAge = oDocument.GetField("age")?.GetNumericValue(); 
          var nHeight = oDocument.GetField("height")?.GetNumericValue(); 

          Console.WriteLine("{0:0.00}, {1,15}, {2,8}, {3,8}", nScore, sName, nAge, nHeight); 
         } 
        } 
        catch (Exception e) 
        { 
         Console.WriteLine(e.ToString()); 
        } 
        finally 
        { 
         oSearcherManager.Release(oSearcher); 
         oSearcher = null; 
        } 
       }); 

       oSearch("john"); 
       oSearch("height:64"); 
       oSearch("age:[44.45 TO 56.66]"); 
       oSearch("height:[70 TO *]"); 

       /* 
       Output: 
       Total Hits: 2 
       0.12,  John Doe, 24.45,  56 
       0.12,  John Smith, 44.44,  64 
       Total Hits: 1 
       1.00,  John Smith, 44.44,  64 
       Total Hits: 1 
       1.00,  Mike Smith, 56.65,  70 
       Total Hits: 1 
       1.00,  Mike Smith, 56.65,  70 
       */ 
      } 
     } 
    } 

    class NumberFormatIgnoreExceptions : NumberFormat 
    { 
     public NumberFormatIgnoreExceptions(CultureInfo locale) : base(locale) 
     { 
     } 

     public override object Parse(string source) 
     { 
      var oValue = default(object); 

      try { oValue = base.Parse(source); } catch { } 

      return oValue; 
     } 
    } 
}