2012-01-06 4 views
1

Luceneのクエリ私はこれらのようなLuceneのインデックス含むドキュメント持っ

_id  |   Name   |  Alternate Names  | Population 

123  Bosc de Planavilla    (some names here in   5000 
345  Planavilla      other languages)    20000 
456  Bosc de la Planassa           1000 
567  Bosc de Plana en Blanca          100000 

を私は何を使うべき最良のLuceneクエリのタイプだと、私は、次の必要な考慮それを構造化する方法:

「BoscのデPlanavilla近いイタリアン・レストラン」私はそのは、ドキュメントの名前と完全に一致が含まれているため、ID 123を持つ文書が返されることにしたい:
  1. ためのユーザークエリがいる場合 。

  2. "Planavillaに近いイタリア料理" クエリに完全一致が含まれており、人口が最も高いので、ID 345のドキュメントが必要です。

  3. クエリが「Boscのが」含まれており、3「Boscの」と、それは最高のポップを持っているので、「Boscの近くにイタリアン・レストラン」 ためのユーザークエリは、私は567をしたい場合

    。 ...

は、おそらく他の多くのユースケースがある...しかし、あなたは私が必要なものの感覚を得るこのフォーム私を行いますクエリのどのような

? 単語Nグラム(帯状疱疹)を生成し、帯状疱疹を使用してORedブールクエリを作成し、カスタムスコアリングを適用する必要がありますか?または定期的なフレーズのクエリが行いますか?私もDisjunctionMaxQueryを見ましたが、何を探しているのかわかりません。

考えてみればわかっているように、ユーザーがクエリで暗示している正確な場所を見つけることです。それでは、私はジオ検索を開始して、さらにその周りのクエリを追加することができます。

どのような方法が最適ですか?

ありがとうございます。

答えて

1

は、同様にソートするためのコードです。私は、人口の種類を一掃するのではなく、都市の大きさを考慮してカスタムスコアリングを追加するほうが理にかなっていると思う。また、これはFieldCacheを使用することに注意してください.FieldCacheはメモリ使用に関する最良の解決策ではないかもしれません。

public class ShingleFilterTests { 
    private Analyzer analyzer; 
    private IndexSearcher searcher; 
    private IndexReader reader; 
    private QueryParser qp; 
    private Sort sort; 

    public static Analyzer createAnalyzer(final int shingles) { 
     return new Analyzer() { 
      @Override 
      public TokenStream tokenStream(String fieldName, Reader reader) { 
       TokenStream tokenizer = new WhitespaceTokenizer(reader); 
       tokenizer = new StopFilter(false, tokenizer, ImmutableSet.of("de", "la", "en")); 
       if (shingles > 0) { 
        tokenizer = new ShingleFilter(tokenizer, shingles); 
       } 
       return tokenizer; 
      } 
     }; 
    } 

    public class PopulationComparatorSource extends FieldComparatorSource { 
     @Override 
     public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException { 
      return new PopulationComparator(fieldname, numHits); 
     } 

     private class PopulationComparator extends FieldComparator { 
      private final String fieldName; 
      private Integer[] values; 
      private int[] populations; 
      private int bottom; 

      public PopulationComparator(String fieldname, int numHits) { 
       values = new Integer[numHits]; 
       this.fieldName = fieldname; 
      } 

      @Override 
      public int compare(int slot1, int slot2) { 
       if (values[slot1] > values[slot2]) return -1; 
       if (values[slot1] < values[slot2]) return 1; 
       return 0; 
      } 

      @Override 
      public void setBottom(int slot) { 
       bottom = values[slot]; 
      } 

      @Override 
      public int compareBottom(int doc) throws IOException { 
       int value = populations[doc]; 
       if (bottom > value) return -1; 
       if (bottom < value) return 1; 
       return 0; 
      } 

      @Override 
      public void copy(int slot, int doc) throws IOException { 
       values[slot] = populations[doc]; 
      } 

      @Override 
      public void setNextReader(IndexReader reader, int docBase) throws IOException { 
       /* XXX uses field cache */ 
       populations = FieldCache.DEFAULT.getInts(reader, "population"); 
      } 

      @Override 
      public Comparable value(int slot) { 
       return values[slot]; 
      } 
     } 
    } 

    @Before 
    public void setUp() throws Exception { 
     Directory dir = new RAMDirectory(); 
     analyzer = createAnalyzer(3); 

     IndexWriter writer = new IndexWriter(dir, analyzer, IndexWriter.MaxFieldLength.UNLIMITED); 
     ImmutableList<String> cities = ImmutableList.of("Bosc de Planavilla", "Planavilla", "Bosc de la Planassa", 
                   "Bosc de Plana en Blanca"); 
     ImmutableList<Integer> populations = ImmutableList.of(5000, 20000, 1000, 100000); 

     for (int id = 0; id < cities.size(); id++) { 
      Document doc = new Document(); 
      doc.add(new Field("id", String.valueOf(id), Field.Store.YES, Field.Index.NOT_ANALYZED)); 
      doc.add(new Field("city", cities.get(id), Field.Store.YES, Field.Index.ANALYZED)); 
      doc.add(new Field("population", String.valueOf(populations.get(id)), 
            Field.Store.YES, Field.Index.NOT_ANALYZED)); 
      writer.addDocument(doc); 
     } 
     writer.close(); 

     qp = new QueryParser(Version.LUCENE_30, "city", createAnalyzer(0)); 
     sort = new Sort(new SortField("population", new PopulationComparatorSource())); 
     searcher = new IndexSearcher(dir); 
     searcher.setDefaultFieldSortScoring(true, true); 
     reader = searcher.getIndexReader(); 
    } 

    @After 
    public void tearDown() throws Exception { 
     searcher.close(); 
    } 

    @Test 
    public void testShingleFilter() throws Exception { 
     System.out.println("shingle filter"); 

     printSearch("city:\"Bosc de Planavilla\""); 
     printSearch("city:Planavilla"); 
     printSearch("city:Bosc"); 
    } 

    private void printSearch(String query) throws ParseException, IOException { 
     Query q = qp.parse(query); 
     System.out.println("query " + q); 
     TopDocs hits = searcher.search(q, null, 4, sort); 
     System.out.println("results " + hits.totalHits); 
     int i = 1; 
     for (ScoreDoc dc : hits.scoreDocs) { 
      Document doc = reader.document(dc.doc); 
      System.out.println(i++ + ". " + dc + " \"" + doc.get("city") + "\" population: " + doc.get("population")); 
     } 
     System.out.println(); 
    } 
} 

これは、以下の結果得られます。

query city:"Bosc Planavilla" 
results 1 
1. doc=0 score=1.143841[5000] "Bosc de Planavilla" population: 5000 

query city:Planavilla 
results 2 
1. doc=1 score=1.287682[20000] "Planavilla" population: 20000 
2. doc=0 score=0.643841[5000] "Bosc de Planavilla" population: 5000 

query city:Bosc 
results 3 
1. doc=3 score=0.375[100000] "Bosc de Plana en Blanca" population: 100000 
2. doc=0 score=0.5[5000] "Bosc de Planavilla" population: 5000 
3. doc=2 score=0.5[1000] "Bosc de la Planassa" population: 1000 
+0

多くの感謝!あなたのアプローチは私が終わったアプローチと似ており、良い結果が得られます。しかし、それは完璧ではありません... 300万のドック指数では、私は応答時間が1秒(1台のマシン上で)になります。さらに、「Indian Bar Paris」を検索するときなどに、「Rich Bar Indian Reserve」を返すなど、奇妙なことがよくあります。可能であれば、スコアリングを使ってもう少し精緻化し、可能であればフィーチャタイプに応じてインデックスブーストを試みます。あなたの親切な助けをありがとう! – azpublic

+0

3百万のドキュメントの1秒間の音があまりにも多く聞こえる。どのようにソートしていますか?プロファイラを使用して、CPUがどこに行くかを調べることができます。私は約70ミリ秒で複雑なクエリとファセットとカスタムソートを持つ4000万のドキュメントインデックスを探しています。 – wesen

1

どのようにフィールドをトークン化しますか?それらを完全な文字列として保存しますか?また、どのようにクエリを解析しますか?

さて、私はこれでちょっと遊んでいます。私はla、en、deを削除するためにStopFilterを使用しています。私はその後、 "正確な一致"を行うために複数の組み合わせを取得するためにシングルフィルターを使用しました。たとえば、Bosc de Planavillaは[Bosc] [Bosc Planavilla]としてトークン化され、Bosc de Plana en Blancaは[Bosc] [Bosc Plana] [Plana Blanca] [Bosc Plana Blanca]にトークン化されます。これは、クエリの一部に対して「完全一致」ができるようにするためです。

私は、ユーザーが渡した正確な文字列を照会しますが、そこにもいくつかの適応があります。あなたが探していたものと結果が良く合うように、私は単純なケースに行きました。私は今、人口あたりの仕分けに探しています

public class ShingleFilterTests { 
    private Analyzer analyzer; 
    private IndexSearcher searcher; 
    private IndexReader reader; 

    public static Analyzer createAnalyzer(final int shingles) { 
     return new Analyzer() { 
      @Override 
      public TokenStream tokenStream(String fieldName, Reader reader) { 
       TokenStream tokenizer = new WhitespaceTokenizer(reader); 
       tokenizer = new StopFilter(false, tokenizer, ImmutableSet.of("de", "la", "en")); 
       if (shingles > 0) { 
        tokenizer = new ShingleFilter(tokenizer, shingles); 
       } 
       return tokenizer; 
      } 
     }; 
    } 

    @Before 
    public void setUp() throws Exception { 
     Directory dir = new RAMDirectory(); 
     analyzer = createAnalyzer(3); 

     IndexWriter writer = new IndexWriter(dir, analyzer, IndexWriter.MaxFieldLength.UNLIMITED); 
     ImmutableList<String> cities = ImmutableList.of("Bosc de Planavilla", "Planavilla", "Bosc de la Planassa", 
                   "Bosc de Plana en Blanca"); 
     ImmutableList<Integer> populations = ImmutableList.of(5000, 20000, 1000, 100000); 

     for (int id = 0; id < cities.size(); id++) { 
      Document doc = new Document(); 
      doc.add(new Field("id", String.valueOf(id), Field.Store.YES, Field.Index.NOT_ANALYZED)); 
      doc.add(new Field("city", cities.get(id), Field.Store.YES, Field.Index.ANALYZED)); 
      doc.add(new Field("population", String.valueOf(populations.get(id)), 
            Field.Store.YES, Field.Index.NOT_ANALYZED)); 
      writer.addDocument(doc); 
     } 
     writer.close(); 

     searcher = new IndexSearcher(dir); 
     reader = searcher.getIndexReader(); 
    } 

    @After 
    public void tearDown() throws Exception { 
     searcher.close(); 
    } 

    @Test 
    public void testShingleFilter() throws Exception { 
     System.out.println("shingle filter"); 

     QueryParser qp = new QueryParser(Version.LUCENE_30, "city", createAnalyzer(0)); 

     printSearch(qp, "city:\"Bosc de Planavilla\""); 
     printSearch(qp, "city:Planavilla"); 
     printSearch(qp, "city:Bosc"); 
    } 

    private void printSearch(QueryParser qp, String query) throws ParseException, IOException { 
     Query q = qp.parse(query); 

     System.out.println("query " + q); 
     TopDocs hits = searcher.search(q, 4); 
     System.out.println("results " + hits.totalHits); 
     int i = 1; 
     for (ScoreDoc dc : hits.scoreDocs) { 
      Document doc = reader.document(dc.doc); 
      System.out.println(i++ + ". " + dc + " \"" + doc.get("city") + "\" population: " + doc.get("population")); 
     } 
     System.out.println(); 
    } 
} 

:ここ

は、コード、私が使用しています(Luceneの3.0.3)です。

これはアウト出力します。ここでは

query city:"Bosc Planavilla" 
results 1 
1. doc=0 score=1.143841 "Bosc de Planavilla" population: 5000 

query city:Planavilla 
results 2 
1. doc=1 score=1.287682 "Planavilla" population: 20000 
2. doc=0 score=0.643841 "Bosc de Planavilla" population: 5000 

query city:Bosc 
results 3 
1. doc=0 score=0.5 "Bosc de Planavilla" population: 5000 
2. doc=2 score=0.5 "Bosc de la Planassa" population: 1000 
3. doc=3 score=0.375 "Bosc de Plana en Blanca" population: 100000 
+0

をご返信wesenあちこちありがとうございます。実際には、標準トークンフィルタ、小文字トークンフィルタ、および停止トークンフィルタを使用して、名前トークンを標準トークン化ツールを使用して索引付けします。しかし、これは簡単に変更することができます。私の質問は、実際にはどのようにインデックスを作成し、クエリをクエリする必要がありますか? – azpublic