2012-01-30 18 views
5

Estoy tratando de hacer un directorio de teléfono/local de búsqueda de negocios utilizando Apache Lucene.Lucene: frases de varias palabras como términos de búsqueda

Tengo campos para el nombre de la calle, nombre comercial, número de teléfono, etc. El problema que tengo es que cuando trato de buscar por la calle donde el nombre de la calle tiene varias palabras (por ejemplo, 'la media luna'), no los resultados son devueltos Pero si trato de buscar con solo una palabra, por ejemplo, 'media luna', obtengo todos los resultados que deseo.

estoy indexación de los datos con la siguiente:

String LocationOfDirectory = "C:\\dir\\index"; 

StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_34); 
Directory Index = new SimpleFSDirectory(LocationOfDirectory); 

IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE.34, analyzer); 
IndexWriter w = new IndexWriter(index, config); 


Document doc = new Document(); 
doc.add(new Field("Street", "the crescent", Field.Store.YES, Field.Index.Analyzed); 

w.add(doc); 
w.close(); 

Mis búsquedas trabajo como este:

int numberOfHits = 200; 
String LocationOfDirectory = "C:\\dir\\index"; 
TopScoreDocCollector collector = TopScoreDocCollector.create(numberOfHits, true); 
Directory directory = new SimpleFSDirectory(new File(LocationOfDirectory)); 
IndexSearcher searcher = new IndexSearcher(IndexReader.open(directory); 

WildcardQuery q = new WildcardQuery(new Term("Street", "the crescent"); 

searcher.search(q, collector); 
ScoreDoc[] hits = collector.topDocs().scoreDocs; 

He tratado de intercambio de la consulta comodín para una consulta frase, primero con la totalidad cadena y luego dividir la cadena en espacio en blanco y envolverlos en una BooleanQuery como esta:

String term = "the crescent"; 
BooleanQuery b = new BooleanQuery(); 
PhraseQuery p = new PhraseQuery(); 
String[] tokens = term.split(" "); 
for(int i = 0 ; i < tokens.length ; ++i) 
{ 
    p.add(new Term("Street", tokens[i])); 
} 
b.add(p, BooleanClause.Occur.MUST); 

Sin embargo, esto no funcionó. Intenté usar un KeywordAnalyzer en lugar de un StandardAnalyzer, pero luego todos los otros tipos de búsqueda dejaron de funcionar también. He intentado reemplazar espacios con otros caracteres (+ y @) y convertir consultas de este formulario, pero todavía no funciona. Creo que no funciona porque + y @ son caracteres especiales que no están indexados, pero parece que no puedo encontrar una lista en la que los personajes sean así.

Estoy empezando a enojarme un poco, ¿alguien sabe lo que estoy haciendo mal?

Gracias, Rik

+0

carácter especial se puede encontrar aquí: http://lucene.apache.org/core/3_5_0/queryparsersynta x.html # N10180. – Oliver

Respuesta

5

me encontré con que mi intento de generar una consulta sin utilizar un QueryParser no funcionaba, así que dejé de tratar de crear mis propias consultas y utilizado una vez QueryParser. Todas las recomendaciones que vi en línea mostraron que debe usar el mismo Analizador en QueryParser que usa durante la indexación, entonces usé un StandardAnalyzer para construir QueryParser.

Esto funciona en este ejemplo porque el StandardAnalyzer elimina la palabra "the" de la calle "the crescent" durante la indexación, y por lo tanto no podemos buscarla porque no está en el índice.

Sin embargo, si elegimos buscar "Grove Road", tenemos un problema con la funcionalidad lista para usar, es decir, que la consulta devolverá todos los resultados que contengan "Grove" O "Road" ". Esto se soluciona fácilmente configurando QueryParser para que su operación predeterminada sea AND en lugar de OR.

Al final, la solución correcta fue la siguiente:

int numberOfHits = 200; 
String LocationOfDirectory = "C:\\dir\\index"; 
TopScoreDocCollector collector = TopScoreDocCollector.create(numberOfHits, true); 
Directory directory = new SimpleFSDirectory(new File(LocationOfDirectory)); 
IndexSearcher searcher = new IndexSearcher(IndexReader.open(directory); 

StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_35); 

//WildcardQuery q = new WildcardQuery(new Term("Street", "the crescent"); 
QueryParser qp = new QueryParser(Version.LUCENE_35, "Street", analyzer); 
qp.setDefaultOperator(QueryParser.Operator.AND); 

Query q = qp.parse("grove road"); 

searcher.search(q, collector); 
ScoreDoc[] hits = collector.topDocs().scoreDocs; 
+1

Eliminar las palabras de parada de los nombres de las calles es incorrecto. Piense en nombres como [Both Street] (http://g.co/maps/r5rnc). Estoy seguro de que puedes encontrar ejemplos más vívidos. ¿Por qué eliminar algo si no tiene sentido? –

11

La razón por la que no recibe sus documentos de vuelta es que mientras que la indexación está utilizando StandardAnalyzer, que convierte a minúsculas fichas y elimina dejar de palabras. Entonces, el único término que se indexa para su ejemplo es 'creciente'. Sin embargo, las consultas con comodines no se analizan, por lo que 'el' se incluye como parte obligatoria de la consulta. Lo mismo ocurre con las consultas de frase en su escenario.

KeywordAnalyzer probablemente no sea muy adecuado para su caso de uso, ya que toma el contenido de campo completo como un token único. Puede usar SimpleAnalyzer para el campo calle - dividirá la entrada en todos los caracteres que no sean letras y luego los convertirá a minúsculas. También puede considerar usar WhitespaceAnalyzer con LowerCaseFilter. Debe probar diferentes opciones y determinar qué funciona mejor para sus datos y usuarios.

Además, puede utilizar diferentes analizadores por campo (por ejemplo, con PerFieldAnalyzerWrapper) si el cambio de analizador para ese campo interrumpe otras búsquedas.

0

Si quiere que las palabras exactas coincidan con las de la calle, puede establecer el campo "Calle" NOT_ANALYZED que no filtrará la palabra de detención "the".

doc.add(new Field("Street", "the crescent", Field.Store.YES, Field.Index.Not_Analyzed); 
+1

Esta no es una buena solución, de esta manera, siempre debe incluir 'the' en la consulta para obtener este resultado. –

+0

@Artur Nowak: Vote su respuesta. Un analizador adecuado es el punto. –

0

No hay necesidad de utilizar ningún Analyzer aquí coz Hibernate utiliza implícitamente StandardAnalyzer que dividir las palabras en base a white spaces lo que la solución que aquí se establece la Analyze a NO que se lleva a cabo de forma automática Multi Phrase Search

@Column(name="skill") 
    @Field(index=Index.YES, analyze=Analyze.NO, store=Store.NO) 
    @Analyzer(definition="SkillsAnalyzer") 
    private String skill; 
Cuestiones relacionadas