So suchen Sie mit ElasticSearch nach einem Teil eines Wortes

128

Ich habe vor kurzem angefangen, ElasticSearch zu verwenden, und ich kann es scheinbar nicht schaffen, nach einem Teil eines Wortes zu suchen.

Beispiel: Ich habe drei Dokumente aus meiner Couchdb in ElasticSearch indiziert:

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

Jetzt möchte ich nach allen Dokumenten suchen, die "Doe" enthalten.

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

Das gibt keine Treffer zurück. Aber wenn ich suche

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

Es wird ein Dokument zurückgegeben (John Doeman).

Ich habe versucht, verschiedene Analysatoren und verschiedene Filter als Eigenschaften meines Index festzulegen. Ich habe auch versucht, eine vollständige Abfrage zu verwenden (zum Beispiel:

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

) Aber nichts scheint zu funktionieren.

Wie kann ich ElasticSearch dazu bringen, sowohl John Doeman als auch Jane Doewoman zu finden, wenn ich nach "Doe" suche?

AKTUALISIEREN

Ich habe versucht, den nGram-Tokenizer und -Filter wie von Igor vorgeschlagen zu verwenden:

{
  "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
        }
      }
    }
  }
}

Das Problem, das ich jetzt habe, ist, dass jede einzelne Abfrage ALLE Dokumente zurückgibt. Irgendwelche Hinweise? Die ElasticSearch-Dokumentation zur Verwendung von nGram ist nicht großartig ...

ldx
quelle
9
Kein Wunder, Sie haben min / max ngram auf 1 gesetzt, also 1 Buchstabe :)
Martin B.

Antworten:

85

Ich benutze auch nGram. Ich benutze Standard Tokenizer und nGram nur als Filter. Hier ist mein Setup:

{
  "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
        }
      }
    }
  }
}

Lassen Sie uns Wortteile mit bis zu 50 Buchstaben finden. Passen Sie das max_gram nach Bedarf an. In deutscher Sprache kann es sehr groß werden, deshalb habe ich einen hohen Wert festgelegt.

Roka
quelle
Erhalten Sie das aus den Einstellungen des Index oder veröffentlichen Sie es an elasticsearch, um es zu konfigurieren?
Tomas Jansson
Es ist ein POST zum Konfigurieren von Elasticsearch.
Roka
Ich bin nicht fest mit aktuellen Versionen von Elasticsearch, sollte es aber in den Dokumenten erwähnen: elastic.co/guide/en/elasticsearch/reference/current/index.html
roka
1
@JimC Ich habe ElasticSearch seit mindestens 7 Jahren nicht mehr verwendet, daher kenne ich die aktuellen Änderungen des Projekts nicht.
Roka
63

Die Suche mit führenden und nachfolgenden Platzhaltern wird in einem großen Index extrem langsam sein. Wenn Sie nach dem Wortpräfix suchen möchten, entfernen Sie den führenden Platzhalter. Wenn Sie wirklich einen Teilstring in der Mitte eines Wortes finden müssen, sollten Sie den ngram-Tokenizer verwenden.

imotov
quelle
14
Igor hat recht. Entfernen Sie mindestens das führende *. Ein Beispiel für NGram ElasticSearch finden Sie in diesem Inhalt: gist.github.com/988923
karmi
3
@karmi: Danke für dein komplettes Beispiel! Vielleicht möchten Sie Ihren Kommentar als tatsächliche Antwort hinzufügen. Das hat ihn für mich zum Laufen gebracht und ich möchte ihn verbessern.
Fabian Steeg
54

Ich denke, es ist nicht nötig, ein Mapping zu ändern. Versuchen Sie, query_string zu verwenden , es ist perfekt. Alle Szenarien funktionieren mit dem Standard-Standardanalysator:

Wir haben Daten:

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

Szenario 1:

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

Antwort:

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

Szenario 2:

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

Antwort:

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

Szenario 3:

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

Antwort:

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

BEARBEITEN - Gleiche Implementierung mit elastischer Suche nach Federdaten https://stackoverflow.com/a/43579948/2357869

Eine weitere Erklärung, wie query_string besser ist als andere https://stackoverflow.com/a/43321606/2357869

Opster Elasticsearch Pro-Vijay
quelle
3
Ich denke, das ist das einfachste
Esgi Dendyanri
Ja . Ich habe in meinem Projekt implementiert.
Opster Elasticsearch Pro-Vijay
Wie füge ich mehrere Felder hinzu, in denen gesucht werden soll?
Shubham A.
Versuchen Sie Folgendes: - {"query": {"query_string": {"fields": ["content", "name"], "query": "this AND that"}}}
Opster Elasticsearch Pro-Vijay
Überprüfen Sie diesen Link elastisch.co/guide/en/elasticsearch/reference/current/…
Opster Elasticsearch Pro-Vijay
14

Ohne Ihre Indexzuordnungen zu ändern, können Sie eine einfache Präfixabfrage durchführen, die Teilsuchen durchführt, wie Sie es sich erhoffen

dh.

{
  "query": { 
    "prefix" : { "name" : "Doe" }
  }
}

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-prefix-query.html

pythonHelpRequired
quelle
Können Sie eine Mehrfeldsuche mit einer Präfixabfrage durchführen?
Batmaci
Danke, genau das, wonach ich gesucht habe! Irgendwelche Gedanken zu den Auswirkungen auf die Leistung?
Vingtoft
6

Probieren Sie die Lösung aus, die hier beschrieben wird: Genaue Teilstringsuche 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"
                }
            }
        }
    }
}

Zur Lösung des Problems der Datenträgernutzung und des Problems mit zu langen Suchbegriffen werden kurze, 8 Zeichen lange ngramme verwendet (konfiguriert mit: "max_gram": 8 ). Um nach Begriffen mit mehr als 8 Zeichen zu suchen, verwandeln Sie Ihre Suche in eine boolesche UND-Abfrage, die nach jeder einzelnen 8-stelligen Teilzeichenfolge in dieser Zeichenfolge sucht. Wenn ein Benutzer beispielsweise nach einem großen Yard (einer 10-stelligen Zeichenfolge) sucht, lautet die Suche wie folgt:

"Arge ya und Arge yar und Rge Yard .

uı6ʎɹnɯ ꞁəıuɐp
quelle
2
Dead Link, pls fix
DarkMukke
Ich habe eine Weile nach so etwas gesucht. Danke dir! Wissen Sie, wie der Speicher mit dem skaliert, min_gramund max_grames scheint, als würde er linear von der Größe der Feldwerte und dem Bereich von minund abhängen max. Wie verpönt ist es, so etwas zu benutzen?
Glen Thompson
Gibt es auch einen Grund dafür, dass ngrames sich um einen Filter über einem Tokenizer handelt? Könnten Sie es nicht einfach als Tokenizer verwenden und dann einen Kleinbuchstabenfilter anwenden? index_ngram: { type: "custom", tokenizer: "ngram_tokenizer", filter: [ "lowercase" ] }Ich habe es versucht und es scheint die gleichen Ergebnisse mit der Analysetest-API
Glen Thompson
2

Wenn Sie die Autocomplete-Funktionalität implementieren möchten, ist Completion Suggester die beste Lösung. Der nächste Blog-Beitrag enthält eine sehr klare Beschreibung, wie dies funktioniert.

Mit zwei Worten, es handelt sich um eine speicherinterne Datenstruktur namens FST, die gültige Vorschläge enthält und für schnelles Abrufen und Speichernutzung optimiert ist. Im Wesentlichen ist es nur eine Grafik. Zum Beispiel, und FST , die die Worte hotel, marriot, mercure, munchenund munichwürde wie folgt aussehen:

Geben Sie hier die Bildbeschreibung ein

Neshta
quelle
2

Sie können Regexp verwenden.

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

Wenn Sie diese Abfrage verwenden:

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

Sie erhalten alle Daten, deren Name mit "J" beginnt. Wenn Sie nur die ersten beiden Datensätze erhalten möchten, deren Name mit "man" endet, können Sie diese Abfrage verwenden:

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

und wenn Sie alle Datensätze erhalten möchten, deren Name "m" enthält, können Sie diese Abfrage verwenden:

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

Das funktioniert bei mir. Und ich hoffe, meine Antwort ist geeignet, um Ihr Problem zu lösen.

Ali Moshiri
quelle
1

Die Verwendung von Wilcards (*) verhindert die Berechnung einer Punktzahl

Dardino
quelle
1
Könnten Sie Ihrer Antwort weitere Details hinzufügen? Geben Sie einen Beispielcode oder einen Verweis auf die Dokumentation dazu an.
Cray
0

Ich benutze das und habe mich arbeiten lassen

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

Keine Ursache.

Ich musste mir die Lucene-Dokumentation ansehen. Scheint, ich kann Platzhalter verwenden! :-)

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

macht den Trick!

ldx
quelle
11
Siehe @ imotov Antwort. Die Verwendung von Platzhaltern lässt sich überhaupt nicht gut skalieren.
Mike Munroe
5
@Idx - Sehen Sie, wie Ihre eigene Antwort abgelehnt wird. Downvotes geben an, wie Qualität und Relevanz einer Antwort ist. Können Sie sich eine Minute Zeit nehmen, um die richtige Antwort zu akzeptieren? Zumindest neue Benutzer wären Ihnen dankbar.
Asyncwait
3
Genug Abstimmungen. OP machte klar, was die beste Antwort jetzt ist. +1 für das Teilen der scheinbar besten Antwort, bevor jemand eine bessere veröffentlicht hat.
Daniel