Come cercare una parte di una parola con ElasticSearch


128

Di recente ho iniziato a utilizzare ElasticSearch e non riesco a farlo cercare una parte di una parola.

Esempio: ho tre documenti del mio couchdb indicizzati in ElasticSearch:

{
  "_id" : "1",
  "name" : "John Doeman",
  "function" : "Janitor"
}
{
  "_id" : "2",
  "name" : "Jane Doewoman",
  "function" : "Teacher"
}
{
  "_id" : "3",
  "name" : "Jimmy Jackal",
  "function" : "Student"
} 

Quindi ora voglio cercare tutti i documenti contenenti "Doe"

curl http://localhost:9200/my_idx/my_type/_search?q=Doe

Ciò non restituisce alcun hit. Ma se lo cerco

curl http://localhost:9200/my_idx/my_type/_search?q=Doeman

Restituisce un documento (John Doeman).

Ho provato a impostare analizzatori e filtri diversi come proprietà del mio indice. Ho anche provato a utilizzare una query completa (ad esempio:

{
  "query": {
    "term": {
      "name": "Doe"
    }
  }
}

) Ma niente sembra funzionare.

Come posso fare in modo che ElasticSearch trovi John Doeman e Jane Doewoman quando cerco "Doe"?

AGGIORNARE

Ho provato a usare il tokenizer e il filtro nGram, come proposto da Igor, in questo modo:

{
  "index": {
    "index": "my_idx",
    "type": "my_type",
    "bulk_size": "100",
    "bulk_timeout": "10ms",
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "type": "custom",
          "tokenizer": "my_ngram_tokenizer",
          "filter": [
            "my_ngram_filter"
          ]
        }
      },
      "filter": {
        "my_ngram_filter": {
          "type": "nGram",
          "min_gram": 1,
          "max_gram": 1
        }
      },
      "tokenizer": {
        "my_ngram_tokenizer": {
          "type": "nGram",
          "min_gram": 1,
          "max_gram": 1
        }
      }
    }
  }
}

Il problema che sto riscontrando ora è che ogni singola query restituisce TUTTI i documenti. Qualche puntatore? La documentazione di ElasticSearch sull'uso di nGram non è eccezionale ...


9
non c'è da meravigliarsi, hai impostato ngram min / max su 1, quindi 1 lettera :)
Martin B.

Risposte:


85

Sto usando anche nGram. Uso tokenizer standard e nGram solo come filtro. Ecco la mia configurazione:

{
  "index": {
    "index": "my_idx",
    "type": "my_type",
    "analysis": {
      "index_analyzer": {
        "my_index_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "mynGram"
          ]
        }
      },
      "search_analyzer": {
        "my_search_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "standard",
            "lowercase",
            "mynGram"
          ]
        }
      },
      "filter": {
        "mynGram": {
          "type": "nGram",
          "min_gram": 2,
          "max_gram": 50
        }
      }
    }
  }
}

Troviamo parti di parole fino a 50 lettere. Regola il max_gram di cui hai bisogno. In tedesco le parole possono diventare davvero grandi, quindi ho impostato un valore elevato.



È quello che ottieni dalle impostazioni dell'indice o è quello che pubblichi su elasticsearch per configurarlo?
Tomas Jansson,

È un POST configurare Elasticsearch.
roka,

Non sono fermo con le versioni attuali di Elasticsearch, ma dovrei menzionarlo nei documenti: elastic.co/guide/en/elasticsearch/reference/current/index.html
roka

1
@JimC Non uso ElasticSearch da almeno 7 anni, quindi non conosco gli attuali cambiamenti del progetto.
roka,

63

La ricerca con caratteri jolly iniziali e finali sarà estremamente lenta su un indice di grandi dimensioni. Se vuoi essere in grado di cercare per prefisso di parole, rimuovi i caratteri jolly iniziali. Se hai davvero bisogno di trovare una sottostringa nel mezzo di una parola, sarebbe meglio usare il tokenizer ngram.


14
Igor ha ragione. Almeno rimuovi il primo *. Per l'esempio di NGram ElasticSearch, vedere questo articolo: gist.github.com/988923
karmi,

3
@karmi: grazie per il tuo esempio completo! Forse vuoi aggiungere il tuo commento come una risposta effettiva, è ciò che ha funzionato per me e quello che vorrei votare.
Fabian Steeg,

54

Penso che non sia necessario modificare alcuna mappatura. Prova a usare query_string , è perfetto. Tutti gli scenari funzioneranno con l'analizzatore standard predefinito:

Abbiamo dati:

{"_id" : "1","name" : "John Doeman","function" : "Janitor"}
{"_id" : "2","name" : "Jane Doewoman","function" : "Teacher"}

Scenario 1:

{"query": {
    "query_string" : {"default_field" : "name", "query" : "*Doe*"}
} }

Risposta:

{"_id" : "1","name" : "John Doeman","function" : "Janitor"}
{"_id" : "2","name" : "Jane Doewoman","function" : "Teacher"}

Scenario 2:

{"query": {
    "query_string" : {"default_field" : "name", "query" : "*Jan*"}
} }

Risposta:

{"_id" : "1","name" : "John Doeman","function" : "Janitor"}

Scenario 3:

{"query": {
    "query_string" : {"default_field" : "name", "query" : "*oh* *oe*"}
} }

Risposta:

{"_id" : "1","name" : "John Doeman","function" : "Janitor"}
{"_id" : "2","name" : "Jane Doewoman","function" : "Teacher"}

EDIT - Stessa implementazione con ricerca elastica dei dati di primavera https://stackoverflow.com/a/43579948/2357869

Un'altra spiegazione di come query_string è meglio di altri https://stackoverflow.com/a/43321606/2357869


3
penso che questo sia il più semplice
Esgi Dendyanri

Sì . Ho implementato nel mio progetto.
Opster Elasticsearch Pro-Vijay,

Come includere più campi in cui cercare?
Shubham A.

prova questo: - {"query": {"query_string": {"fields": ["content", "name"], "query": "this AND that"}}}
Opster Elasticsearch Pro-Vijay



6

Prova la soluzione con è descritta qui: Ricerche di sottostring esatte in ElasticSearch

{
    "mappings": {
        "my_type": {
            "index_analyzer":"index_ngram",
            "search_analyzer":"search_ngram"
        }
    },
    "settings": {
        "analysis": {
            "filter": {
                "ngram_filter": {
                    "type": "ngram",
                    "min_gram": 3,
                    "max_gram": 8
                }
            },
            "analyzer": {
                "index_ngram": {
                    "type": "custom",
                    "tokenizer": "keyword",
                    "filter": [ "ngram_filter", "lowercase" ]
                },
                "search_ngram": {
                    "type": "custom",
                    "tokenizer": "keyword",
                    "filter": "lowercase"
                }
            }
        }
    }
}

Per risolvere il problema di utilizzo del disco e il termine di ricerca troppo lungo vengono utilizzati ngram lunghi di 8 caratteri (configurati con: "max_gram": 8 ). Per cercare termini con più di 8 caratteri, trasforma la tua ricerca in una query booleana AND alla ricerca di ogni sottostringa distinta di 8 caratteri in quella stringa. Ad esempio, se un utente cercasse un cantiere di grandi dimensioni (una stringa di 10 caratteri), la ricerca sarebbe:

"arge ya AND arge yar E rge yard .


2
dead link, pls fix
DarkMukke,

Ho cercato qualcosa di simile per un po '. Grazie! Sai come la memoria si ridimensiona con min_grame max_gramsembra che dipenda linearmente dalla dimensione dei valori del campo e dall'intervallo di mine max. Quanto è disapprovato usare qualcosa del genere?
Glen Thompson,

Inoltre, c'è qualche motivo per cui ngramè un filtro su un tokenizer? potresti non solo averlo come tokenizer e quindi applicare un filtro minuscolo ... L' index_ngram: { type: "custom", tokenizer: "ngram_tokenizer", filter: [ "lowercase" ] }ho provato e sembra dare gli stessi risultati usando l'analizzatore test api
Glen Thompson,

2

Se si desidera implementare la funzionalità di completamento automatico, Completion Suggester è la soluzione più accurata. Il prossimo post sul blog contiene una descrizione molto chiara di come funziona.

In due parole, è una struttura di dati in memoria chiamata FST che contiene suggerimenti validi ed è ottimizzata per il recupero rapido e l'utilizzo della memoria. In sostanza, è solo un grafico. Per esempio, e FST contenente le parole hotel, marriot, mercure, munchene munichsarebbe simile a questa:

inserisci qui la descrizione dell'immagine


2

puoi usare regexp.

{ "_id" : "1", "name" : "John Doeman" , "function" : "Janitor"}
{ "_id" : "2", "name" : "Jane Doewoman","function" : "Teacher"  }
{ "_id" : "3", "name" : "Jimmy Jackal" ,"function" : "Student"  } 

se usi questa query:

{
  "query": {
    "regexp": {
      "name": "J.*"
    }
  }
}

ti verranno dati tutti i dati che il loro nome inizia con "J". Considera che vuoi ricevere solo i primi due record che il loro nome termina con "man" in modo da poter usare questa query:

{
  "query": { 
    "regexp": {
      "name": ".*man"
    }
  }
}

e se vuoi ricevere tutti i record che nel loro nome esistono "m", puoi usare questa query:

{
  "query": { 
    "regexp": {
      "name": ".*m.*"
    }
  }
}

Questo funziona per me. E spero che la mia risposta sia adatta a risolvere il tuo problema.


1

L'uso delle wilcard (*) impedisce il calcolo di un punteggio


1
Potresti aggiungere maggiori dettagli alla tua risposta? Fornire un codice di esempio o un riferimento alla documentazione su ciò che fa.
Cray,

0

Sto usando questo e ho lavorato

"query": {
        "query_string" : {
            "query" : "*test*",
            "fields" : ["field1","field2"],
            "analyze_wildcard" : true,
            "allow_leading_wildcard": true
        }
    }

-6

Non importa.

Ho dovuto consultare la documentazione di Lucene. Sembra che io possa usare i caratteri jolly! :-)

curl http://localhost:9200/my_idx/my_type/_search?q=*Doe*

fa il trucco!


11
Vedi la risposta di @imotov. L'uso dei caratteri jolly non si ridimensionerà affatto.
Mike Munroe,

5
@Idx - Guarda come la tua risposta è sottoposta a downgrade. Downvotes rappresenta la qualità e la pertinenza di una risposta. Potresti risparmiare un minuto per accettare la risposta giusta? Almeno i nuovi utenti ti sarebbero grati.
asyncwait,

3
Abbastanza downvotes. OP ha chiarito quale sia la risposta migliore ora. +1 per condividere quella che sembrava essere la risposta migliore prima che qualcuno ne pubblicasse una migliore.
s
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.