una leer importar fuera desde datos como archivo java search lucene filtering catalog

java - leer - Usando Lucene para contar resultados en categorías



leer excel desde java como si fuera una base de datos (5)

Estoy intentando usar Lucene Java 2.3.2 para implementar la búsqueda en un catálogo de productos. Además de los campos regulares para un producto, hay un campo llamado ''Categoría''. Un producto puede caer en múltiples categorías. Actualmente, utilizo FilteredQuery para buscar el mismo término de búsqueda con cada categoría para obtener el número de resultados por categoría.

Esto resulta en 20-30 llamadas de búsqueda interna por consulta para mostrar los resultados. Esto está ralentizando considerablemente la búsqueda. ¿Hay una manera más rápida de lograr el mismo resultado usando Lucene?


Así que déjame ver si entiendo la pregunta correctamente: dada una consulta del usuario, quieres mostrar cuántas coincidencias hay para la consulta en cada categoría. ¿Correcto?

Piénselo de esta manera: su consulta es en realidad originalQuery AND (category1 OR category2 or ...) excepto que además de un puntaje general, quiere obtener un número para cada una de las categorías. Lamentablemente, la interfaz para recopilar visitas en Lucene es muy limitada, y solo te da un puntaje general para una consulta. Pero podría implementar un marcador / recopilador personalizado.

Eche un vistazo a la fuente de org.apache.lucene.search.DisjunctionSumScorer. Puede copiar algo de eso para escribir un marcador personalizado que repite las coincidencias de categorías mientras se realiza la búsqueda principal. Y puede mantener un Map<String,Long> para realizar un seguimiento de las coincidencias en cada categoría.


Es posible que desee considerar buscar en todos los documentos que coinciden con categorías utilizando un iterador de TermDocs .

Este código de ejemplo pasa por cada término de "Categoría" y luego cuenta la cantidad de documentos que coinciden con ese término.

public static void countDocumentsInCategories(IndexReader reader) throws IOException { TermEnum terms = null; TermDocs td = null; try { terms = reader.terms(new Term("Category", "")); td = reader.termDocs(); do { Term currentTerm = terms.term(); if (!currentTerm.field().equals("Category")) { break; } int numDocs = 0; td.seek(terms); while (td.next()) { numDocs++; } System.out.println(currentTerm.field() + " : " + currentTerm.text() + " --> " + numDocs); } while (terms.next()); } finally { if (td != null) td.close(); if (terms != null) terms.close(); } }

Este código debe ejecutarse razonablemente rápido incluso para índices grandes.

Aquí hay un código que prueba ese método:

public static void main(String[] args) throws Exception { RAMDirectory store = new RAMDirectory(); IndexWriter w = new IndexWriter(store, new StandardAnalyzer()); addDocument(w, 1, "Apple", "fruit", "computer"); addDocument(w, 2, "Orange", "fruit", "colour"); addDocument(w, 3, "Dell", "computer"); addDocument(w, 4, "Cumquat", "fruit"); w.close(); IndexReader r = IndexReader.open(store); countDocumentsInCategories(r); r.close(); } private static void addDocument(IndexWriter w, int id, String name, String... categories) throws IOException { Document d = new Document(); d.add(new Field("ID", String.valueOf(id), Field.Store.YES, Field.Index.UN_TOKENIZED)); d.add(new Field("Name", name, Field.Store.NO, Field.Index.UN_TOKENIZED)); for (String category : categories) { d.add(new Field("Category", category, Field.Store.NO, Field.Index.UN_TOKENIZED)); } w.addDocument(d); }


Esto es lo que hice, aunque es un poco pesado en la memoria:

Lo que necesita es crear de antemano un montón de BitSet s, uno para cada categoría, que contengan el id. De documento de todos los documentos en una categoría. Ahora, en el tiempo de búsqueda, utiliza un HitCollector y comprueba los id. De documento en relación con los BitSets.

Aquí está el código para crear los conjuntos de bits:

public BitSet[] getBitSets(IndexSearcher indexSearcher, Category[] categories) { BitSet[] bitSets = new BitSet[categories.length]; for(int i=0; i<categories.length; i++) { Query query = categories[i].getQuery(); final BitSet bitset = new BitSet() indexSearcher.search(query, new HitCollector() { public void collect(int doc, float score) { bitSet.set(doc); } }); bitSets[i] = bitSet; } return bitSets; }

Esta es solo una forma de hacer esto. Probablemente podría utilizar TermDocs en lugar de ejecutar una búsqueda completa si sus categorías son lo suficientemente simples, pero esto solo debería ejecutarse una vez cuando cargue el índice de todos modos.

Ahora, cuando llega el momento de contar las categorías de resultados de búsqueda, haz esto:

public int[] getCategroryCount(IndexSearcher indexSearcher, Query query, final BitSet[] bitSets) { final int[] count = new int[bitSets.length]; indexSearcher.search(query, new HitCollector() { public void collect(int doc, float score) { for(int i=0; i<bitSets.length; i++) { if(bitSets[i].get(doc)) count[i]++; } } }); return count; }

Con lo que termina es una matriz que contiene el recuento de cada categoría dentro de los resultados de búsqueda. Si también necesita los resultados de búsqueda, debe agregar un TopDocCollector a su recopilador de hits (yo dawg ...). O bien, podría ejecutar la búsqueda nuevamente. 2 búsquedas son mejores que 30.


No tengo suficiente reputación para comentar (!) Pero en la respuesta de Matt Quail estoy bastante seguro de que podrías reemplazar esto:

int numDocs = 0; td.seek(terms); while (td.next()) { numDocs++; }

con este:

int numDocs = terms.docFreq()

y luego deshacerse de la variable td por completo. Esto debería hacerlo aún más rápido.


Sachin, creo que quieres una búsqueda facetada . No sale de la caja con Lucene. Le sugiero que intente usar SOLR , que tiene facetas como una característica importante y conveniente.