2011-09-30 15 views
7

En Solr (3.3), ¿es posible hacer un campo letra por letra que se pueda buscar a través de un EdgeNGramFilterFactory y también sensible a las consultas de frase?Solr: consulta de frase exacta con EdgeNGramFilterFactory

Por ejemplo, yo estoy buscando un campo que, cuando contengan "informática contrat", se encontrará si el usuario escribe:

  • contrat
  • Informatique
  • contr
  • Informa
  • "contrat Informatique"
  • "información contrat" ​​

Actualmente, he hecho algo como esto:

<fieldtype name="terms" class="solr.TextField"> 
    <analyzer type="index"> 
     <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-ISOLatin1Accent.txt"/> 
     <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="1" catenateNumbers="1" catenateAll="0" splitOnCaseChange="1"/> 
     <tokenizer class="solr.LowerCaseTokenizerFactory"/> 
     <filter class="solr.EdgeNGramFilterFactory" minGramSize="2" maxGramSize="15" side="front"/> 
    </analyzer> 
    <analyzer type="query"> 
     <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-ISOLatin1Accent.txt"/> 
     <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="1" catenateNumbers="1" catenateAll="0" splitOnCaseChange="1"/> 
     <tokenizer class="solr.LowerCaseTokenizerFactory"/> 
    </analyzer> 
</fieldtype> 

... pero fracasó en la frase consultas.

Cuando miro en el analizador de esquema en administrador Solr, considero que "informática contrat" ​​genera las fichas siguientes:

[...] contr contra contrat in inf info infor inform [...] 

Así que la consulta funciona con "contrat en" (fichas consecutivas), pero no "contrat inf" (porque estas dos fichas están separadas).

Estoy bastante seguro de que cualquier tipo de derivación puede funcionar con consultas de frase, pero no puedo encontrar el tokenizador de filtro correcto para usar antes del EdgeNGramFilterFactory.

Respuesta

2

Como por desgracia no podía soportar a utilizar un PositionFilter derecha como Jayendra Patil sugirió (PositionFilter hace cualquier consulta una o consulta booleana), he usado una enfoque diferente.

Aún con el EdgeNGramFilter, agregué el hecho de que cada palabra clave que escribió el usuario es obligatoria y deshabilité todas las frases.

Si el usuario solicita "cont info", se transforma en +cont +info. Es un poco más permisivo que una frase verdadera, pero logró hacer lo que quiero (y no devuelve resultados con solo un término de los dos).

El único inconveniente de esta solución es que los términos se pueden permutar en los resultados (por lo que también se encontrará un documento con "informatique contrat"), pero no es gran cosa.

+0

Hola, Xavier. ¿Puede explicar cómo transformó "cont info" en + cont + info? ¿Hay alguna clase de utilities fuera de la caja para esto? ¿O es solo identificar las citas dobles y transformarlas manualmente? Estoy tratando de resolver esto: http: // stackoverflow.com/questions/37033381/solr-search-field-best-practices – wattale

+0

Fue una operación manual, buscando comillas dobles y agregando el signo más. No encontré nada que pudiera automatizar esto para mí: -/ –

+0

Gracias por la respuesta xavier, para mí también después de rastrear tanto contenido no pude encontrar una solución lista para usar. Pensé que estaba reinventando la rueda haciendo esto manualmente. Pero supongo que hacerlo manualmente es la única opción disponible: | – wattale

1

Esto es lo que estaba pensando -
Para que los ngrams sean una frase coincidente, la posición de los tokens generados para cada palabra debe ser la misma.
Comprobé el filtro de gramos de borde e incrementa los tokens, y no encontré ningún parámetro para evitarlo.
Hay un filtro de posición disponible que mantiene la posición de los tokens en el mismo token que en el inicio.
Entonces, si se utiliza la siguiente configuración, todos los tokens están en la misma posición y coinciden con la frase de consulta (las mismas posiciones de token se combinan como frases)
Lo revisé a través de la herramienta anaylsis y las consultas coinciden.

lo que es posible que desee probar la pista: -

<analyzer type="index"> 
    <tokenizer class="solr.WhitespaceTokenizerFactory" /> 
    <charFilter class="solr.MappingCharFilterFactory" 
      mapping="mapping-ISOLatin1Accent.txt" /> 
    <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" 
      generateNumberParts="1" catenateWords="1" catenateNumbers="1" 
      catenateAll="0" splitOnCaseChange="1"/> 
    <filter class="solr.LowerCaseFilterFactory" /> 
    <filter class="solr.EdgeNGramFilterFactory" minGramSize="2" 
      maxGramSize="15" side="front"/> 
    <filter class="solr.PositionFilterFactory" /> 
</analyzer> 
+0

El La idea es clara, pero no parece funcionar de todos modos: - Incluso si obtuve coincidencias a través de la herramienta de análisis de administración, una consulta real no arroja nada (probablemente porque en la herramienta de análisis, la forma en que resalta tokens no molesta con frases) Además, [PositionFilter] (http://tinyurl.com/solr-positionfilter) hace que la consulta _boolean_ sea dicha en la wiki, por lo que "contrat informatique" o incluso "+ contrat + informatique" devuelve documentos con "contrat" ​​pero también sin "informatique" como el operador predeterminado es un OR. Una posible alternativa sería transformar la consulta en + contrat + informatique, creo. –

4

La búsqueda de frase exacta no funciona porque el parámetro slop de consulta = 0 de forma predeterminada. Buscando una frase '' Hola mundo '' busca términos con posiciones secuenciales. Ojalá EdgeNGramFilter tuviera un parámetro para controlar el posicionamiento de la salida, esto parece un viejo question.

Al configurar el parámetro qs en un valor muy alto (más que la distancia máxima entre ngramos) puede recuperar las frases. Esto resuelve parcialmente el problema permitiendo frases, pero no exactas, también se encontrarán permutaciones. Para que la búsqueda de "informática contrat" ​​se correspondería con el texto como "... contrato abandonado. Informatique ..."

enter image description here

Para apoyar exacta consulta frase termino de usar separate fields for ngrams.

pasos necesarios:

Definir tipos de campos separados para indexar los valores regulares y gramos:

<fieldType name="text" class="solr.TextField" omitNorms="false"> 
    <analyzer> 
    <tokenizer class="solr.StandardTokenizerFactory"/> 
    <filter class="solr.LowerCaseFilterFactory"/> 
    </analyzer> 
</fieldType> 

<fieldType name="ngrams" class="solr.TextField" omitNorms="false"> 
    <analyzer type="index"> 
    <tokenizer class="solr.StandardTokenizerFactory"/> 
    <filter class="solr.LowerCaseFilterFactory"/> 
    <filter class="solr.EdgeNGramFilterFactory" minGramSize="2" maxGramSize="15" side="front"/> 
    </analyzer> 
    <analyzer type="query"> 
    <tokenizer class="solr.StandardTokenizerFactory"/> 
    <filter class="solr.LowerCaseFilterFactory"/> 
    </analyzer> 
</fieldType> 

Dile a Solr a copy fields cuando la indexación:

Puede definir N-gramas separados reflexión para cada campo:

<field name="contact_ngrams" type="ngrams" indexed="true" stored="false"/> 
<field name="product_ngrams" type="ngrams" indexed="true" stored="false"/> 
<copyField source="contact_text" dest="contact_ngrams"/> 
<copyField source="product_text" dest="product_ngrams"/> 

O usted puede poner todos los N-gramas en un solo campo:

<field name="heap_ngrams" type="ngrams" indexed="true" stored="false"/> 
<copyField source="*_text" dest="heap_ngrams"/> 

Tenga en cuenta que usted no será capaz de separar refuerzos en este caso.

Y lo último es especificar ngrams campos y refuerzos en la consulta. Una forma es configurar su aplicación. Otra forma es especificar "anexa" params en el solrconfig.xml

<lst name="appends"> 
    <str name="qf">heap_ngrams</str> 
    </lst> 
1

he hecho un arreglo para EdgeNGramFilter tan posiciones dentro de un símbolo no se incrementan más:

public class CustomEdgeNGramTokenFilterFactory extends TokenFilterFactory { 
    private int maxGramSize = 0; 

    private int minGramSize = 0; 

    @Override 
    public void init(Map<String, String> args) { 
     super.init(args); 
     String maxArg = args.get("maxGramSize"); 
     maxGramSize = (maxArg != null ? Integer.parseInt(maxArg) 
       : EdgeNGramTokenFilter.DEFAULT_MAX_GRAM_SIZE); 

     String minArg = args.get("minGramSize"); 
     minGramSize = (minArg != null ? Integer.parseInt(minArg) 
       : EdgeNGramTokenFilter.DEFAULT_MIN_GRAM_SIZE); 

    } 

    @Override 
    public CustomEdgeNGramTokenFilter create(TokenStream input) { 
     return new CustomEdgeNGramTokenFilter(input, minGramSize, maxGramSize); 
    } 
} 
public class CustomEdgeNGramTokenFilter extends TokenFilter { 
    private final int minGram; 
    private final int maxGram; 
    private char[] curTermBuffer; 
    private int curTermLength; 
    private int curGramSize; 

    private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); 
    private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class); 
    private final PositionIncrementAttribute positionIncrementAttribute = addAttribute(PositionIncrementAttribute.class); 

    /** 
    * Creates EdgeNGramTokenFilter that can generate n-grams in the sizes of the given range 
    * 
    * @param input {@link org.apache.lucene.analysis.TokenStream} holding the input to be tokenized 
    * @param minGram the smallest n-gram to generate 
    * @param maxGram the largest n-gram to generate 
    */ 
    public CustomEdgeNGramTokenFilter(TokenStream input, int minGram, int maxGram) { 
     super(input); 

     if (minGram < 1) { 
      throw new IllegalArgumentException("minGram must be greater than zero"); 
     } 

     if (minGram > maxGram) { 
      throw new IllegalArgumentException("minGram must not be greater than maxGram"); 
     } 

     this.minGram = minGram; 
     this.maxGram = maxGram; 
    } 

@Override 
public final boolean incrementToken() throws IOException { 
    while (true) { 
     int positionIncrement = 0; 
     if (curTermBuffer == null) { 
      if (!input.incrementToken()) { 
       return false; 
      } else { 
       positionIncrement = positionIncrementAttribute.getPositionIncrement(); 
       curTermBuffer = termAtt.buffer().clone(); 
       curTermLength = termAtt.length(); 
       curGramSize = minGram; 
      } 
     } 
     if (curGramSize <= maxGram) { 
      if (!(curGramSize > curTermLength   // if the remaining input is too short, we can't generate any n-grams 
        || curGramSize > maxGram)) {  // if we have hit the end of our n-gram size range, quit 
       // grab gramSize chars from front 
       int start = 0; 
       int end = start + curGramSize; 
       offsetAtt.setOffset(start, end); 
       positionIncrementAttribute.setPositionIncrement(positionIncrement); 
       termAtt.copyBuffer(curTermBuffer, start, curGramSize); 
       curGramSize++; 

       return true; 
      } 
     } 
     curTermBuffer = null; 
    } 
} 

    @Override 
    public void reset() throws IOException { 
     super.reset(); 
     curTermBuffer = null; 
    } 
} 
Cuestiones relacionadas