webentwicklung-frage-antwort-db.com.de

So suchen Sie mit ElasticSearch nach einem Teil eines Wortes

Ich habe vor kurzem mit ElasticSearch angefangen und kann scheinbar nicht nach einem Teil eines Wortes suchen.

Beispiel: Ich habe drei Dokumente aus meiner couchdb, die in ElasticSearch indiziert sind:

{
  "_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 danach suche

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

Es gibt ein Dokument zurück (John Doeman).

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

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

Aber nichts scheint zu funktionieren.

Wie kann ich erreichen, dass ElasticSearch John Doeman und Jane Doewoman findet, wenn ich nach "Doe" suche?

UPDATE

Ich habe versucht, den nGram-Tokenizer und -Filter wie von Igor vorgeschlagen so 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 bei jeder Abfrage ALLE Dokumente zurückgegeben werden. Die Dokumentation zu ElasticSearch zur Verwendung von nGram ist nicht großartig ...

94
ldx

Ich verwende auch nGram. Ich benutze den 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 Deutsch können Wörter sehr groß werden, daher habe ich sie auf einen hohen Wert gesetzt.

73
roka

Die Suche mit führenden und nachgestellten Platzhaltern wird für einen großen Index extrem langsam sein. Wenn Sie nach einem Word-Präfix suchen möchten, entfernen Sie den führenden Platzhalter. Wenn Sie wirklich eine Teilzeichenfolge in der Mitte eines Words suchen müssen, sollten Sie besser den ngram-Tokenizer verwenden. 

57
imotov

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

EDIT - Gleiche Implementierung mit Federdaten-Suche https://stackoverflow.com/a/43579948/2357869

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

25
Vijay Gupta

ohne die Indexzuordnungen zu ändern, können Sie eine einfache Präfixabfrage durchführen, die teilweise Suchvorgänge durchführt, wie Sie es wünschen

dh.

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

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

11

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

Um das Problem der Festplattennutzung und das zu lange Problem mit dem Suchbegriff zu lösen, werden kurze 8 Zeichen (ngrams) verwendet (konfiguriert mit: "max_gram": 8). Um nach Begriffen mit mehr als 8 Zeichen zu suchen, wandeln Sie Ihre Suche in eine boolesche UND-Abfrage um, die nach jedem eindeutigen 8-stelligen Teilstring in dieser Zeichenfolge sucht. Wenn ein Benutzer beispielsweise nach large yard (eine Zeichenfolge mit 10 Zeichen) sucht, würde die Suche wie folgt aussehen:

"arge ya AND arge yar AND rge yard.

Wenn Sie die Autovervollständigung implementieren möchten, ist Completion Suggester die beste Lösung. Der nächste blog post enthält eine sehr genaue Beschreibung, wie das funktioniert.

In zwei Worten handelt es sich um eine In-Memory-Datenstruktur namens FST, die gültige Vorschläge enthält und für ein schnelles Abrufen und Speichernutzung optimiert ist. Im Wesentlichen ist es nur eine Grafik. Ein FST mit den Wörtern hotel, marriot, mercure, munchen und munich würde beispielsweise so aussehen:

enter image description here

1
Neshta

sie können Regex 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, die in ihrem Namen "m" enthalten, können Sie diese Abfrage verwenden:

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

Dies funktioniert für mich. Und ich hoffe, dass meine Antwort geeignet ist, um Ihr Problem zu lösen.

0
Ali Moshiri

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

0
Dardino