2011-11-23 11 views
5

Me gustaría buscar mi índice en dos campos llamados "a" y "b". Me dan búsquedas como Freud -- theories of psychology y me gustaría llevar a cabo la siguiente consulta:¿Cómo combino dos consultas de Lucene con OR?

(a="Freud" AND b="theories of psychology") OR (b="Freud" AND a="theories of psychology") 

¿Cómo se hace esto? Hasta ahora tengo Lucene la construcción de las dos mitades (firstHalf y secondHalf) utilizando MultiFieldQueryParser, entonces los he combinado con

BooleanQuery combined = new BooleanQuery(); 
combined.add(firstHalf, BooleanClause.Occur.SHOULD); 
combined.add(secondHalf, BooleanClause.Occur.SHOULD); 

Pero combined permite que los resultados serán devueltos en el que sólo "teorías" se encuentra y no "psicología" , donde definitivamente quiero ambos términos. Parece que Lucene está dividiendo las "teorías de la psicología" en tres palabras y combinándolas individualmente con O. ¿Cómo evito esto?

firstHalf parece:

Query firstHalf = MultiFieldQueryParser.parse(Version.LUCENE_33, 
     new String[]{"Freud", "theories of psychology"}, 
     new String[]{"a", "b"}, 
     new BooleanClause.Occur[]{BooleanClause.Occur.MUST, BooleanClause.Occur.MUST}, 
     analyzer); 

donde analyzer es sólo un objeto StandardAnalyzer.

+0

acaban de contar que Lucene no es compatible con la lógica booleana como esta, y que deberían = O!. En ese caso, _Lucene in Action_ 2nd Edition está equivocado en la página 95. :) Así que ahora sé _por qué_ mi consulta está quebrada, pero aún no sé cómo arreglarla. – dmn

+0

¿Estás seguro de que la consulta es correcta? La consulta 'teorías de la psicología' significa que al menos una de las tres palabras debe ocurrir en algún lugar, pero ninguna de ellas es una palabra requerida. –

+0

@KaiChan No, tuve que modificar esto para poner esa restricción. Creo que lo entendí. :) – dmn

Respuesta

2

El analizador estándar se convertirá en token. Por lo tanto, la consulta theories of psychology es equivalente a theories OR of OR psychology.

Si desea buscar la frase "teorías de la psicología" utilizar una PhraseQuery, o de lo contrario en cuenta que el valor por defecto QueryParser interpretará en el sentido de las frases una frase (es decir, cambiar el código para ser "\"theories of psychology\"").

Y sí, hay un sentido en el que Lucene no usa lógica booleana, pero es técnica y no es realmente relevante aquí.

3

Lo encontré yo mismo, pero ahora el código es significativamente más largo; si alguien conoce una solución más elegante, publíquela y con gusto la recompensaré. :) (Aunque Voy a estar haciendo esto en un método poco ... pero aquí está la versión completa de lo que está pasando ...)

QueryParser parser = new QueryParser(Version.LUCENE_33, "a", analyzer); 
parser.setDefaultOperator(QueryParser.AND_OPERATOR); 
Query a_0 = parser.parse("Freud"); 
parser = new QueryParser(Version.LUCENE_33, "b", analyzer); 
parser.setDefaultOperator(QueryParser.AND_OPERATOR); 
Query b_1 = parser.parse("theories of psychology"); 

BooleanQuery firstHalf = new BooleanQuery(); 
firstHalf.add(a_0, BooleanClause.Occur.MUST); 
firstHalf.add(b_1, BooleanClause.Occur.MUST); 

parser = new QueryParser(Version.LUCENE_33, "b", analyzer); 
parser.setDefaultOperator(QueryParser.AND_OPERATOR); 
Query b_0 = parser.parse("Freud"); 
parser = new QueryParser(Version.LUCENE_33, "a", analyzer); 
parser.setDefaultOperator(QueryParser.AND_OPERATOR); 
Query a_1 = parser.parse("theories of psychology"); 

BooleanQuery secondHalf = new BooleanQuery(); 
secondHalf.add(b_0, BooleanClause.Occur.MUST); 
secondHalf.add(a_1, BooleanClause.Occur.MUST); 

BooleanQuery combined = new BooleanQuery(); 
combined.add(firstHalf, BooleanClause.Occur.SHOULD); 
combined.add(secondHalf, BooleanClause.Occur.SHOULD); 

Resulta que SHOULD no funcionan de la manera que lo necesito aquí. Espero que alguien encuentre esto útil y no me estoy hablando solo en público;)

+1

Es mi experiencia reciente que al aprender Lucene, debe evitar QueryParser y tratar de unir todo desde los tipos de consulta subyacentes directos. Obtendrá una mejor comprensión de Lucene y no se distraerá con la sintaxis de la consulta. Como menciona Xodarap, lo que realmente quieres aquí es PhraseQuery.Lo que tiene ahora coincidirá con documentos con "teorías" "de" "psicología" en cualquier orden o posición, no con la frase exacta. (En realidad, StandardAnalyzer filtrará "de", por lo que incluso PhraseQuery no coincidirá con su frase exacta ... pero esa es una limitación de la búsqueda que solemos aceptar.) – Jegschemesch

2

Escribí a continuación la clase para generar consultas difusas encadenadas en las que se debe buscar un término en varios campos. La consulta combinada se puede recuperar llamando al método GetQuery().

public class QueryParam 
{ 
    public string[] Fields { get; set; } 
    public string Term { get; set; } 

    private QueryParam _andOperandSuffix; 
    private QueryParam _orOperandSuffix; 

    private readonly BooleanQuery _indexerQuery = new BooleanQuery();   

    public QueryParam(string term, params string[] fields) 
    { 
     Term = term; 
     Fields = fields; 
    } 

    public QueryParam And(QueryParam queryParam) 
    { 
     _andOperandSuffix = queryParam; 
     return this; 
    } 

    public QueryParam Or(QueryParam queryParam) 
    { 
     _orOperandSuffix = queryParam; 
     return this; 
    } 

    public BooleanQuery GetQuery() 
    {    
     foreach (var field in Fields) 
      _indexerQuery.Add(new FuzzyQuery(new Term(field, Term)), Occur.SHOULD); 

     if (_andOperandSuffix != null) 
      _indexerQuery.Add(_andOperandSuffix.GetQuery(),Occur.MUST); 

     if (_orOperandSuffix != null) 
      _indexerQuery.Add(_orOperandSuffix.GetQuery(), Occur.SHOULD); 

     return _indexerQuery; 
    } 

} 

Ejemplo:

var leftquery = new QueryParam("Freud", "a").And(new QueryParam("theories of psychology", "b")); 
var rightquery = new QueryParam("Freud", "b").And(new QueryParam("theories of psychology", "a")); 
var query = leftquery.Or(rightquery);    
Cuestiones relacionadas