2017-08-11 6 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」を検索すると、返された1つのヒットが返ってくれることを願っています。 –

+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; 
     } 
    } 
} 
関連する問題