Ungefähre Übereinstimmungen mit der Fuzzy-Suche finden

Auf dieser Seite wird beschrieben, wie Sie eine unscharfe Suche im Rahmen einer Volltextsuche verwenden.

Neben der genauen Tokensuche mit den Funktionen SEARCH und SEARCH_SUBSTRING unterstützt Spanner auch ungefähre (oder unscharfe) Suchvorgänge. Bei unscharfen Suchanfragen werden übereinstimmende Dokumente gefunden, obwohl es geringfügige Unterschiede zwischen der Anfrage und dem Dokument gibt.

Spanner unterstützt die folgenden Arten von unscharfer Suche:

  • Ungefähre Suche auf Grundlage von N-Grammen
  • Phonetische Suche mit Soundex

Die auf N-Grammen basierende unscharfe Suche basiert auf derselben Teilstring-Tokenisierung, die für eine Teilstring-Suche erforderlich ist. Die Konfiguration des Tokenizers ist wichtig, da sie sich auf die Suchqualität und ‑leistung auswirkt. Im folgenden Beispiel wird gezeigt, wie Sie eine Anfrage mit falsch geschriebenen oder unterschiedlich geschriebenen Wörtern erstellen, um ungefähre Übereinstimmungen im Suchindex zu finden.

Schema

GoogleSQL

CREATE TABLE Albums (
  AlbumId STRING(MAX) NOT NULL,
  AlbumTitle STRING(MAX),
  AlbumTitle_Tokens TOKENLIST AS (
    TOKENIZE_SUBSTRING(AlbumTitle, ngram_size_min=>2, ngram_size_max=>3,
                      relative_search_types=>["word_prefix", "word_suffix"])) HIDDEN
) PRIMARY KEY(AlbumId);

CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens)
STORING (AlbumTitle);

PostgreSQL

In diesem Beispiel wird spanner.tokenize_substring verwendet.

CREATE TABLE albums (
  albumid character varying NOT NULL,
  albumtitle character varying,
  albumtitle_tokens spanner.tokenlist GENERATED ALWAYS AS (
    spanner.tokenize_substring(albumtitle, ngram_size_min=>2, ngram_size_max=>3,
                      relative_search_types=>'{word_prefix, word_suffix}'::text[])) VIRTUAL HIDDEN,
PRIMARY KEY(albumid));

CREATE SEARCH INDEX albumsindex
ON albums(albumtitle_tokens)
INCLUDE (albumtitle);

Abfrage

Mit der folgenden Abfrage werden die Alben mit Titeln gefunden, die „Hatel Kaliphorn“ am ähnlichsten sind, z. B. „Hotel California“.

GoogleSQL

SELECT AlbumId
FROM Albums
WHERE SEARCH_NGRAMS(AlbumTitle_Tokens, "Hatel Kaliphorn")
ORDER BY SCORE_NGRAMS(AlbumTitle_Tokens, "Hatel Kaliphorn") DESC
LIMIT 10

PostgreSQL

In diesem Beispiel werden spanner.score_ngrams und spanner.search_ngrams verwendet.

SELECT albumid
FROM albums
WHERE spanner.search_ngrams(albumtitle_tokens, 'Hatel Kaliphorn')
ORDER BY spanner.score_ngrams(albumtitle_tokens, 'Hatel Kaliphorn') DESC
LIMIT 10

Leistung und Erinnerung für eine auf N-Grammen basierende ungefähre Suche optimieren

Die Beispielabfrage im vorherigen Abschnitt führt die Suche in zwei Phasen mit zwei verschiedenen Funktionen durch:

  1. SEARCH_NGRAMS findet alle infrage kommenden Alben, die gemeinsame N-Gramme mit der Suchanfrage haben. Beispiele: Dreistellige N-Gramme für „California“ sind [cal, ali, lif, ifo, for, orn, rni, nia] und für „Kaliphorn“ [kal, ali, lip, iph, pho, hor, orn]. Die freigegebenen N-Gramme in diesen Datasets sind [ali, orn]. Standardmäßig werden mit SEARCH_NGRAMS alle Dokumente mit mindestens zwei gemeinsamen N-Grammen abgeglichen. Daher wird „Kaliphorn“ mit „California“ abgeglichen.
  2. SCORE_NGRAMS sortiert Ergebnisse nach Ähnlichkeit. Die Ähnlichkeit zweier Strings wird als Verhältnis von unterschiedlichen gemeinsamen N‑Grammen zu unterschiedlichen nicht gemeinsamen N‑Grammen definiert:
$$ \frac{shared\_ngrams}{total\_ngrams_{index} + total\_ngrams_{query} - shared\_ngrams} $$

Normalerweise ist die Suchanfrage für die Funktionen SEARCH_NGRAMS und SCORE_NGRAMS identisch. Die empfohlene Vorgehensweise ist, das Argument mit Abfrageparametern anstelle von Stringliteralen zu verwenden und denselben Abfrageparameter in den Funktionen SEARCH_NGRAMS und SCORE_NGRAMS anzugeben.

Spanner hat drei Konfigurationsargumente, die mit SEARCH_NGRAMS verwendet werden können:

  • Die Mindest- und Höchstgrößen für N-Gramme werden mit den Funktionen TOKENIZE_SUBSTRING oder TOKENIZE_NGRAMS angegeben. Wir raten von N-Grammen mit einem Zeichen ab, da sie mit einer sehr großen Anzahl von Dokumenten übereinstimmen könnten. Andererseits führen lange N-Gramme dazu, dass SEARCH_NGRAMS kurze falsch geschriebene Wörter nicht erkennt.
  • Die Mindestanzahl von N-Grammen, die mit SEARCH_NGRAMS übereinstimmen müssen (mit den Argumenten min_ngrams und min_ngrams_percent in SEARCH_NGRAMS festgelegt). Höhere Zahlen beschleunigen die Abfrage in der Regel, verringern aber den Recall.

Um ein gutes Gleichgewicht zwischen Leistung und Recall zu erreichen, können Sie diese Argumente an die jeweilige Anfrage und Arbeitslast anpassen.

Wir empfehlen außerdem, einen inneren LIMIT einzufügen, um sehr teure Anfragen zu vermeiden, wenn eine Kombination aus beliebten N-Grammen gefunden wird.

GoogleSQL

SELECT AlbumId
FROM (
  SELECT AlbumId,
        SCORE_NGRAMS(AlbumTitle_Tokens, @p) AS score
  FROM Albums
  WHERE SEARCH_NGRAMS(AlbumTitle_Tokens, @p)
  LIMIT 10000  # inner limit
)
ORDER BY score DESC
LIMIT 10  # outer limit

PostgreSQL

In diesem Beispiel werden spanner.score_ngrams und spanner.search_ngrams verwendet. Der Abfrageparameter $1 ist an „Hatel Kaliphorn“ gebunden.

SELECT albumid
FROM
  (
    SELECT albumid, spanner.score_ngrams(albumtitle_tokens, $1) AS score
    FROM albums
    WHERE spanner.search_ngrams(albumtitle_tokens, $1)
    LIMIT 10000
  ) AS inner_query
ORDER BY inner_query.score DESC
LIMIT 10

N-Gramm-basierte unscharfe Suche im Vergleich zum erweiterten Anfragemodus

Neben der N-Gramm-basierten Fuzzy-Suche werden im erweiterten Anfragemodus auch einige falsch geschriebene Wörter berücksichtigt. Daher gibt es einige Überschneidungen zwischen den beiden Funktionen. In der folgenden Tabelle werden die Unterschiede zusammengefasst:

Fuzzy-Suche auf Grundlage von N-Grammen Erweiterter Abfragemodus
Kosten Erfordert eine teurere Substring-Tokenisierung auf Grundlage von N-Grammen Erfordert eine kostengünstigere Volltext-Tokenisierung
Suchanfragetypen Funktioniert gut bei kurzen Dokumenten mit wenigen Wörtern, z. B. bei einem Personennamen, einem Städtenamen oder einem Produktnamen Funktioniert gleichermaßen gut mit Dokumenten und Suchanfragen jeder Größe
Suche nach Wortteilen Führt eine Teilstring-Suche durch, bei der Rechtschreibfehler berücksichtigt werden Unterstützt nur die Suche nach ganzen Wörtern (SEARCH_SUBSTRING unterstützt das Argument enhance_query nicht).
Falsch geschriebene Wörter Unterstützt falsch geschriebene Wörter im Index oder in der Abfrage Unterstützt nur falsch geschriebene Wörter in der Anfrage
Korrekturen Findet alle falsch geschriebenen Übereinstimmungen, auch wenn es sich bei der Übereinstimmung nicht um ein echtes Wort handelt Korrigiert Rechtschreibfehler bei häufigen, bekannten Wörtern

Phonetische Suche mit Soundex durchführen

Spanner bietet die Funktion SOUNDEX, mit der Sie Wörter finden können, die unterschiedlich geschrieben, aber gleich ausgesprochen werden. Beispiel: SOUNDEX("steven"), SOUNDEX("stephen") und SOUNDEX("stefan") sind alle „s315“, während SOUNDEX("stella") „s340“ ist. Bei SOUNDEX wird zwischen Groß- und Kleinschreibung unterschieden und es funktioniert nur für lateinische Alphabete.

Die phonetische Suche mit SOUNDEX kann mit einer generierten Spalte und einem Suchindex implementiert werden, wie im folgenden Beispiel gezeigt:

GoogleSQL

CREATE TABLE Singers (
  SingerId INT64,
  AlbumTitle STRING(MAX),
  AlbumTitle_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN,
  Name STRING(MAX),
  NameSoundex STRING(MAX) AS (LOWER(SOUNDEX(Name))),
  NameSoundex_Tokens TOKENLIST AS (TOKEN(NameSoundex)) HIDDEN
) PRIMARY KEY(SingerId);

CREATE SEARCH INDEX SingersPhoneticIndex ON Singers(AlbumTitle_Tokens, NameSoundex_Tokens);

PostgreSQL

In diesem Beispiel wird spanner.soundex verwendet.

CREATE TABLE singers (
  singerid bigint,
  albumtitle character varying,
  albumtitle_tokens spanner.tokenlist GENERATED ALWAYS AS (spanner.tokenize_fulltext(albumtitle)) VIRTUAL HIDDEN,
  name character varying,
  namesoundex character varying GENERATED ALWAYS AS (lower(spanner.soundex(name))) VIRTUAL,
  namesoundex_tokens spanner.tokenlist GENERATED ALWAYS AS (spanner.token(lower(spanner.soundex(name))) VIRTUAL HIDDEN,
PRIMARY KEY(singerid));

CREATE SEARCH INDEX singersphoneticindex ON singers(albumtitle_tokens, namesoundex_tokens);

Die folgende Abfrage ordnet „stefan“ auf SOUNDEX „Steven“ zu und enthält AlbumTitle mit „cat“:

GoogleSQL

SELECT SingerId
FROM Singers
WHERE NameSoundex = LOWER(SOUNDEX("stefan")) AND SEARCH(AlbumTitle_Tokens, "cat")

PostgreSQL

SELECT singerid
FROM singers
WHERE namesoundex = lower(spanner.soundex('stefan')) AND spanner.search(albumtitle_tokens, 'cat')

Nächste Schritte