2010-01-07 19 views
0

un document donné { 'foo', 'bar', 'baz'}, je veux correspondre à l'aide SpanNearQuery avec les jetons { 'baz', 'supplémentaires'}Lucene SpanNearQuery correspondance partielle

Mais ce ne parvient.

Comment puis-je contourner ce problème?

test de l'échantillon (en utilisant lucene 2.9.1) avec les résultats suivants:

  • givenSingleMatch - PASS
  • givenTwoMatches - PASS
  • givenThreeMatches - PASS
  • givenSingleMatch_andExtraTerm - FAIL

...

SpanNearQuery vous permet de trouver des termes situés à une certaine distance les uns des autres.
import org.apache.lucene.analysis.standard.StandardAnalyzer; 
import org.apache.lucene.document.Document; 
import org.apache.lucene.document.Field; 
import org.apache.lucene.index.IndexReader; 
import org.apache.lucene.index.IndexWriter; 
import org.apache.lucene.index.Term; 
import org.apache.lucene.search.IndexSearcher; 
import org.apache.lucene.search.TopDocs; 
import org.apache.lucene.search.spans.SpanNearQuery; 
import org.apache.lucene.search.spans.SpanQuery; 
import org.apache.lucene.search.spans.SpanTermQuery; 
import org.apache.lucene.store.RAMDirectory; 
import org.apache.lucene.util.Version; 
import org.junit.After; 
import org.junit.Assert; 
import org.junit.Before; 
import org.junit.Test; 

import java.io.IOException; 

public class SpanNearQueryTest { 

    private RAMDirectory directory = null; 

    private static final String BAZ = "baz"; 
    private static final String BAR = "bar"; 
    private static final String FOO = "foo"; 
    private static final String TERM_FIELD = "text"; 

    @Before 
    public void given() throws IOException { 
     directory = new RAMDirectory(); 
     IndexWriter writer = new IndexWriter(
       directory, 
       new StandardAnalyzer(Version.LUCENE_29), 
       IndexWriter.MaxFieldLength.UNLIMITED); 

     Document doc = new Document(); 
     doc.add(new Field(TERM_FIELD, FOO, Field.Store.NO, Field.Index.ANALYZED)); 
     doc.add(new Field(TERM_FIELD, BAR, Field.Store.NO, Field.Index.ANALYZED)); 
     doc.add(new Field(TERM_FIELD, BAZ, Field.Store.NO, Field.Index.ANALYZED)); 

     writer.addDocument(doc); 
     writer.commit(); 
     writer.optimize(); 
     writer.close(); 
    } 

    @After 
    public void cleanup() { 
     directory.close(); 
    } 

    @Test 
    public void givenSingleMatch() throws IOException { 

     SpanNearQuery spanNearQuery = new SpanNearQuery(
       new SpanQuery[] { 
         new SpanTermQuery(new Term(TERM_FIELD, FOO)) 
       }, Integer.MAX_VALUE, false); 

     TopDocs topDocs = new IndexSearcher(IndexReader.open(directory)).search(spanNearQuery, 100); 

     Assert.assertEquals("Should have made a match.", 1, topDocs.scoreDocs.length); 
    } 

    @Test 
    public void givenTwoMatches() throws IOException { 

     SpanNearQuery spanNearQuery = new SpanNearQuery(
       new SpanQuery[] { 
         new SpanTermQuery(new Term(TERM_FIELD, FOO)), 
         new SpanTermQuery(new Term(TERM_FIELD, BAR)) 
       }, Integer.MAX_VALUE, false); 

     TopDocs topDocs = new IndexSearcher(IndexReader.open(directory)).search(spanNearQuery, 100); 

     Assert.assertEquals("Should have made a match.", 1, topDocs.scoreDocs.length); 
    } 

    @Test 
    public void givenThreeMatches() throws IOException { 

     SpanNearQuery spanNearQuery = new SpanNearQuery(
       new SpanQuery[] { 
         new SpanTermQuery(new Term(TERM_FIELD, FOO)), 
         new SpanTermQuery(new Term(TERM_FIELD, BAR)), 
         new SpanTermQuery(new Term(TERM_FIELD, BAZ)) 
       }, Integer.MAX_VALUE, false); 

     TopDocs topDocs = new IndexSearcher(IndexReader.open(directory)).search(spanNearQuery, 100); 

     Assert.assertEquals("Should have made a match.", 1, topDocs.scoreDocs.length); 
    } 

    @Test 
    public void givenSingleMatch_andExtraTerm() throws IOException { 

     SpanNearQuery spanNearQuery = new SpanNearQuery(
       new SpanQuery[] { 
         new SpanTermQuery(new Term(TERM_FIELD, BAZ)), 
         new SpanTermQuery(new Term(TERM_FIELD, "EXTRA")) 
       }, 
       Integer.MAX_VALUE, false); 

     TopDocs topDocs = new IndexSearcher(IndexReader.open(directory)).search(spanNearQuery, 100); 

     Assert.assertEquals("Should have made a match.", 1, topDocs.scoreDocs.length); 
    } 
} 
+0

Remarque: Tous les jetons se trouvent dans un seul champ. Merci danben d'avoir signalé cette information manquante. –

Répondre

5

Exemple (à partir http://www.lucidimagination.com/blog/2009/07/18/the-spanquery/):

que nous voulons trouver Lucene dans les 5 positions de doug, avec doug suivant Lucene (questions d'ordre) - vous pouvez utiliser la SpanQuery suivante:

new SpanNearQuery(new SpanQuery[] { 
    new SpanTermQuery(new Term(FIELD, "lucene")), 
    new SpanTermQuery(new Term(FIELD, "doug"))}, 
    5, 
    true); 

alt text http://www.lucidimagination.com/blog/wp-content/uploads/2009/07/spanquery-dia1.png

Dans ce texte exemple, Lucene est dans les 3 de Doug

Mais pour votre exemple, le seul match que je peux voir est que à la fois votre requête et le document cible ont « cd » (et je fais l'hypothèse que tous ces termes sont dans un seul champ). Dans ce cas, vous n'avez pas besoin d'utiliser un type de requête spécial. En utilisant les mécanismes standard, vous obtiendrez une pondération différente de zéro en fonction du fait qu'ils contiennent tous les deux le même terme dans le même champ.

Edit 3 - en réponse à la dernière remarque, la réponse est que vous ne pouvez pas utiliser SpanNearQuery pour faire autre chose que ce qu'il est destiné à être, ce qui est de savoir si plusieurs termes dans un document se produire dans un certain nombre de places l'une de l'autre. Je ne peux pas dire quel est votre cas d'utilisation spécifique/résultats attendus (n'hésitez pas à le poster), mais dans le dernier cas si vous voulez seulement savoir si un ou plusieurs de ("BAZ", "EXTRA") est en le document, un BooleanQuery fonctionnera très bien.

Éditer 4 - Maintenant que vous avez posté votre cas d'utilisation, je comprends ce que vous voulez faire. Voici comment vous pouvez le faire: utiliser un BooleanQuery comme mentionné ci-dessus pour combiner les termes individuels que vous voulez ainsi que le SpanNearQuery, et donner un coup de pouce sur le SpanNearQuery.

Ainsi, la requête sous forme de texte ressemblerait à ceci:

BAZ OR EXTRA OR "BAZ EXTRA"~100^5 

(à titre d'exemple - ce sélectionnne tous les documents contenant soit « baz » ou « EXTRA », mais attribuer un score plus élevé aux documents où les termes "BAZ" et "EXTRA" se trouvent à 100 endroits l'un de l'autre, ajustez la position et augmentez comme bon vous semble Cet exemple est tiré du livre de recettes Solr, donc il peut ne pas analyser dans Lucene, ou donner des résultats indésirables. dans la section suivante je vous montre comment construire ceci en utilisant l'API)

Par programme, vous construiriez ceci comme suit:

Query top = new BooleanQuery(); 

// Construct the terms since they will be used more than once 
Term bazTerm = new Term("Field", "BAZ"); 
Term extraTerm = new Term("Field", "EXTRA"); 

// Add each term as "should" since we want a partial match 
top.add(new TermQuery(bazTerm), BooleanClause.Occur.SHOULD); 
top.add(new TermQuery(extraTerm), BooleanClause.Occur.SHOULD); 

// Construct the SpanNearQuery, with slop 100 - a document will get a boost only 
// if BAZ and EXTRA occur within 100 places of each other. The final parameter means 
// that BAZ must occur before EXTRA. 
SpanNearQuery spanQuery = new SpanNearQuery(
           new SpanQuery[] { new SpanTermQuery(bazTerm), 
               new SpanTermQuery(extraTerm) }, 
           100, true); 

// Give it a boost of 5 since it is more important that the words are together 
spanQuery.setBoost(5f); 

// Add it as "should" since we want a match even when we don't have proximity 
top.add(spanQuery, BooleanClause.Occur.SHOULD); 

Espérons que ça aide! À l'avenir, essayez de commencer en publiant exactement les résultats que vous attendez - même si cela vous semble évident, ce n'est peut-être pas le cas pour le lecteur, et être explicite peut éviter d'avoir à faire des allers-retours autant de fois.

+0

L'image en ligne expliquant la distance est une bonne idée. – Brian

+0

C'est ce que j'ai initialement supposé aussi. Cependant, le document en question ne sera pas renvoyé de ma recherche. –

+0

Peut-être que vous pourriez poster un code indiquant comment vous cherchez? – danben