Textabgleich

Die Textübereinstimmung in Milvus ermöglicht eine präzise Dokumentensuche anhand bestimmter Begriffe. Diese Funktion wird in erster Linie für die gefilterte Suche verwendet, um bestimmte Bedingungen zu erfüllen, und kann skalare Filterung einbeziehen, um die Suchergebnisse zu verfeinern, wodurch Ähnlichkeitssuchen innerhalb von Vektoren ermöglicht werden, die skalare Kriterien erfüllen.

TEXT_MATCH findet exakte analysierte Begriffe, während die „ TEXT_MATCH_FUZZY “-Suche einen geringen Edit-Abstand zwischen Such- und indizierten Tokens toleriert. Bei beiden handelt es sich um boolesche Filteroperationen, bei denen die Relevanz der gefundenen Dokumente nicht bewertet wird. Wenn Sie die relevantesten Dokumente basierend auf der semantischen Bedeutung und Wichtigkeit der Suchbegriffe abrufen möchten, empfehlen wir Ihnen die Verwendung der Volltextsuche.

Übersicht

Milvus integriert Tantivy, um seinen zugrunde liegenden invertierten Index und die begriffbasierte Textsuche zu betreiben. Für jeden Texteintrag indiziert Milvus diesen nach folgendem Verfahren:

  1. Analysator: Der Analysator verarbeitet den Eingabetext, indem er ihn in einzelne Wörter (Token) zerlegt und anschließend bei Bedarf Filter anwendet. Auf diese Weise kann Milvus einen Index auf Basis dieser Token erstellen.

  2. Indizierung: Nach der Textanalyse erstellt Milvus einen invertierten Index, der jedes eindeutige Token den Dokumenten zuordnet, in denen es vorkommt.

Wenn ein Benutzer eine Textsuche durchführt, wird der invertierte Index verwendet, um schnell alle Dokumente abzurufen, die die Suchbegriffe enthalten. Dies ist wesentlich schneller, als jedes Dokument einzeln zu durchsuchen.

Keyword Match Stichwortabgleich

Textabgleich aktivieren

Der Textabgleich funktioniert bei Zeichenfolgenfeldern, für die der Abgleich aktiviert ist. Die Beispiele auf dieser Seite verwenden VARCHAR, das von allen Client-SDKs unterstützt wird. In Milvus 3.0.x TEXT unterstützen Felder den Textabgleich auch, wenn „Storage V3“ aktiviert ist. Legen Sie für beide Feldtypen sowohl „ enable_analyzer “ als auch „ enable_match “ auf „ True “ fest und konfigurieren Sie anschließend optional einen Analysator, wenn Sie Ihr Sammlungsschema definieren.

Setzen Sie „ enable_analyzer “ und enable_match

Um den Textabgleich für ein bestimmtes „ VARCHAR “-Feld zu aktivieren, setzen Sie bei der Definition des Feldschemas sowohl den Parameter „ enable_analyzer “ als auch „ enable_match “ auf „ True “. Dadurch wird Milvus angewiesen, Text zu tokenisieren und einen invertierten Index für das angegebene Feld zu erstellen, was schnelle und effiziente Textabgleiche ermöglicht.

from pymilvus import MilvusClient, DataType

schema = MilvusClient.create_schema(enable_dynamic_field=False)
schema.add_field(
    field_name="id",
    datatype=DataType.INT64,
    is_primary=True,
    auto_id=True
)
schema.add_field(
    field_name='text', 
    datatype=DataType.VARCHAR, 
    max_length=1000, 
    enable_analyzer=True, # Whether to enable text analysis for this field
    enable_match=True # Whether to enable text match
)
schema.add_field(
    field_name="embeddings",
    datatype=DataType.FLOAT_VECTOR,
    dim=5
)
import io.milvus.v2.common.DataType;
import io.milvus.v2.service.collection.request.AddFieldReq;
import io.milvus.v2.service.collection.request.CreateCollectionReq;

CreateCollectionReq.CollectionSchema schema = CreateCollectionReq.CollectionSchema.builder()
        .enableDynamicField(false)
        .build();
schema.addField(AddFieldReq.builder()
        .fieldName("id")
        .dataType(DataType.Int64)
        .isPrimaryKey(true)
        .autoID(true)
        .build());
schema.addField(AddFieldReq.builder()
        .fieldName("text")
        .dataType(DataType.VarChar)
        .maxLength(1000)
        .enableAnalyzer(true)
        .enableMatch(true)
        .build());
schema.addField(AddFieldReq.builder()
        .fieldName("embeddings")
        .dataType(DataType.FloatVector)
        .dimension(5)
        .build());
import "github.com/milvus-io/milvus/client/v2/entity"

schema := entity.NewSchema().WithDynamicFieldEnabled(false)
schema.WithField(entity.NewField().
    WithName("id").
    WithDataType(entity.FieldTypeInt64).
    WithIsPrimaryKey(true).
    WithIsAutoID(true),
).WithField(entity.NewField().
    WithName("text").
    WithDataType(entity.FieldTypeVarChar).
    WithEnableAnalyzer(true).
    WithEnableMatch(true).
    WithMaxLength(1000),
).WithField(entity.NewField().
    WithName("embeddings").
    WithDataType(entity.FieldTypeFloatVector).
    WithDim(5),
)
const schema = [
  {
    name: "id",
    data_type: DataType.Int64,
    is_primary_key: true,
  },
  {
    name: "text",
    data_type: "VarChar",
    enable_analyzer: true,
    enable_match: true,
    max_length: 1000,
  },
  {
    name: "embeddings",
    data_type: DataType.FloatVector,
    dim: 5,
  },
];
export schema='{
        "autoId": true,
        "enabledDynamicField": false,
        "fields": [
            {
                "fieldName": "id",
                "dataType": "Int64",
                "isPrimary": true
            },
            {
                "fieldName": "text",
                "dataType": "VarChar",
                "elementTypeParams": {
                    "max_length": 1000,
                    "enable_analyzer": true,
                    "enable_match": true
                }
            },
            {
                "fieldName": "embeddings",
                "dataType": "FloatVector",
                "elementTypeParams": {
                    "dim": "5"
                }
            }
        ]
    }'

Optional: Konfigurieren Sie einen Analysator

Die Leistung und Genauigkeit des Schlüsselwortabgleichs hängen vom ausgewählten Analysator ab. Verschiedene Analysatoren sind auf unterschiedliche Sprachen und Textstrukturen zugeschnitten, sodass die Wahl des richtigen Analysators die Suchergebnisse für Ihren spezifischen Anwendungsfall erheblich beeinflussen kann.

Standardmäßig verwendet Milvus den Analysator „ standard “, der Text anhand von Leerzeichen und Satzzeichen in Token zerlegt, Token mit mehr als 40 Zeichen entfernt und den Text in Kleinbuchstaben umwandelt. Für die Anwendung dieser Standardeinstellung sind keine zusätzlichen Parameter erforderlich. Weitere Informationen finden Sie unter „Standard“.

Falls ein anderer Analysator erforderlich ist, können Sie diesen mithilfe des Parameters „ analyzer_params “ konfigurieren. Um beispielsweise den Analysator „ english “ für die Verarbeitung von englischem Text anzuwenden:

analyzer_params = {
    "type": "english"
}
schema.add_field(
    field_name='text',
    datatype=DataType.VARCHAR,
    max_length=200,
    enable_analyzer=True,
    analyzer_params = analyzer_params,
    enable_match = True,
)
Map<String, Object> analyzerParams = new HashMap<>();
analyzerParams.put("type", "english");
schema.addField(AddFieldReq.builder()
        .fieldName("text")
        .dataType(DataType.VarChar)
        .maxLength(200)
        .enableAnalyzer(true)
        .analyzerParams(analyzerParams)
        .enableMatch(true)
        .build());
analyzerParams := map[string]any{"type": "english"}
schema.WithField(entity.NewField().
    WithName("text").
    WithDataType(entity.FieldTypeVarChar).
    WithEnableAnalyzer(true).
    WithEnableMatch(true).
    WithAnalyzerParams(analyzerParams).
    WithMaxLength(200),
)
const schema = [
  {
    name: "id",
    data_type: DataType.Int64,
    is_primary_key: true,
  },
  {
    name: "text",
    data_type: "VarChar",
    enable_analyzer: true,
    enable_match: true,
    max_length: 1000,
    analyzer_params: { type: 'english' },
  },
  {
    name: "embeddings",
    data_type: DataType.FloatVector,
    dim: 5,
  },
];
export schema='{
        "autoId": true,
        "enabledDynamicField": false,
        "fields": [
            {
                "fieldName": "id",
                "dataType": "Int64",
                "isPrimary": true
            },
            {
                "fieldName": "text",
                "dataType": "VarChar",
                "elementTypeParams": {
                    "max_length": 200,
                    "enable_analyzer": true,
                    "enable_match": true,
                    "analyzer_params": {"type": "english"}
                }
            },
            {
                "fieldName": "embeddings",
                "dataType": "FloatVector",
                "elementTypeParams": {
                    "dim": "5"
                }
            }
        ]
    }'

Milvus bietet außerdem verschiedene andere Analysatoren, die für unterschiedliche Sprachen und Szenarien geeignet sind. Weitere Details finden Sie unter „Analyzer-Übersicht“.

Textabgleich verwenden

Sobald Sie den Textabgleich für ein Feld vom Typ „ VARCHAR “ oder „ TEXT “ in Ihrem Sammlungsschema aktiviert haben, können Sie Textabgleiche mithilfe des Ausdrucks „ TEXT_MATCH “ durchführen.

Syntax des TEXT_MATCH-Ausdrucks

Der Ausdruck „ TEXT_MATCH “ wird verwendet, um das Feld und die zu suchenden Begriffe anzugeben. Die Syntax lautet wie folgt:

TEXT_MATCH(field_name, text)
  • field_name: Der Name des für den Abgleich aktivierten Felds vom Typ „ VARCHAR “ oder „ TEXT “, nach dem gesucht werden soll.

  • text: Die zu suchenden Begriffe. Mehrere Begriffe können durch Leerzeichen oder andere geeignete Trennzeichen getrennt werden, je nach Sprache und konfiguriertem Analysator.

Standardmäßig verwendet „ TEXT_MATCH “ die „OR“-Vergleichslogik, d. h., es werden Dokumente zurückgegeben, die einen der angegebenen Begriffe enthalten. Um beispielsweise nach Dokumenten zu suchen, die den Begriff „ machine “ oder „ deep “ im Feld „ text “ enthalten, verwenden Sie den folgenden Ausdruck:

filter = "TEXT_MATCH(text, 'machine deep')"
String filter = "TEXT_MATCH(text, 'machine deep')";
filter := "TEXT_MATCH(text, 'machine deep')"
const filter = "TEXT_MATCH(text, 'machine deep')";
export filter="\"TEXT_MATCH(text, 'machine deep')\""

Sie können auch mehrere „ TEXT_MATCH “-Ausdrücke mithilfe logischer Operatoren kombinieren, um eine UND -Verknüpfung durchzuführen.

  • Um nach Dokumenten zu suchen, die sowohl „ machine “ als auch „ deep “ im Feld „ text “ enthalten, verwenden Sie den folgenden Ausdruck:

    filter = "TEXT_MATCH(text, 'machine') and TEXT_MATCH(text, 'deep')"
    
    String filter = "TEXT_MATCH(text, 'machine') and TEXT_MATCH(text, 'deep')";
    
    filter := "TEXT_MATCH(text, 'machine') and TEXT_MATCH(text, 'deep')"
    
    const filter = "TEXT_MATCH(text, 'machine') and TEXT_MATCH(text, 'deep')"
    
    export filter="\"TEXT_MATCH(text, 'machine') and TEXT_MATCH(text, 'deep')\""
    
  • Um nach Dokumenten zu suchen, die sowohl „ machine “ als auch „ learning “ enthalten, jedoch nicht „ deep “ im Feld „ text “, verwenden Sie die folgenden Ausdrücke:

    filter = "not TEXT_MATCH(text, 'deep') and TEXT_MATCH(text, 'machine') and TEXT_MATCH(text, 'learning')"
    
    String filter = "not TEXT_MATCH(text, 'deep') and TEXT_MATCH(text, 'machine') and TEXT_MATCH(text, 'learning')";
    
    filter := "not TEXT_MATCH(text, 'deep') and TEXT_MATCH(text, 'machine') and TEXT_MATCH(text, 'learning')"
    
    const filter = "not TEXT_MATCH(text, 'deep') and TEXT_MATCH(text, 'machine') and TEXT_MATCH(text, 'learning')";
    
    export filter="\"not TEXT_MATCH(text, 'deep') and TEXT_MATCH(text, 'machine') and TEXT_MATCH(text, 'learning')\""
    

Syntax des Ausdrucks „TEXT_MATCH_FUZZY“Compatible with Milvus 3.0.0+

Verwenden Sie „ TEXT_MATCH_FUZZY “, um Rechtschreibunterschiede zwischen Suchbegriffen und indizierten Tokens zu tolerieren. Milvus analysiert den Suchtext mit dem Analysator des Feldes und wendet auf jedes resultierende Token einen Fuzzy-Abgleich an. Wenn die Suche mehrere Tokens liefert, passt der Ausdruck zu einer Entität, sobald ein beliebiges Token die konfigurierte Editierdistanz erfüllt.

Die Syntax lautet wie folgt:

TEXT_MATCH_FUZZY(field_name, text, max_edit_distance = 1)
  • field_name: Der Name des für den Abgleich aktivierten Felds „ VARCHAR “ oder „ TEXT “, in dem gesucht werden soll.

  • text: Der Suchtext, der analysiert und mit den indizierten Tokens abgeglichen werden soll.

  • max_edit_distance: Der maximal zulässige Editierabstand für jedes Abfragetoken. Der Name der Option muss genau „ max_edit_distance “ lauten, und ihr Wert muss „ 0 “, „ 1 “ oder „ 2 “ sein. Der Wert „ 0 “ führt einen exakten Tokenabgleich durch, was „ TEXT_MATCH “ entspricht.

Der folgende Ausdruck findet beispielsweise Token, die bis auf eine Änderung mit „ machne “ übereinstimmen, einschließlich „ machine “:

filter = "TEXT_MATCH_FUZZY(text, 'machne', max_edit_distance = 1)"
String filter = "TEXT_MATCH_FUZZY(text, 'machne', max_edit_distance = 1)";
filter := "TEXT_MATCH_FUZZY(text, 'machne', max_edit_distance = 1)"
const filter = "TEXT_MATCH_FUZZY(text, 'machne', max_edit_distance = 1)";
export filter="\"TEXT_MATCH_FUZZY(text, 'machne', max_edit_distance = 1)\""

TEXT_MATCH_FUZZY ist Teil der Syntax für Filterausdrücke, sodass Client-SDKs keine spezielle Methode für unscharfe Übereinstimmungen benötigen. Übergeben Sie den Ausdruck über denselben Parameter ` filter `, der auch für ` TEXT_MATCH ` bei Such- oder Abfragevorgängen verwendet wird.

Suche mit Textübereinstimmung

Die Textübereinstimmung kann in Kombination mit der Vektorähnlichkeitssuche verwendet werden, um den Suchumfang einzugrenzen und die Suchleistung zu verbessern. Indem Sie die Sammlung vor der Vektorähnlichkeitssuche mithilfe der Textübereinstimmung filtern, können Sie die Anzahl der zu durchsuchenden Dokumente reduzieren, was zu schnelleren Abfragezeiten führt.

In diesem Beispiel filtert der Ausdruck „ filter “ die Suchergebnisse so, dass nur Dokumente berücksichtigt werden, die mit den angegebenen Begriffen „ keyword1 “ oder „ keyword2 “ übereinstimmen. Die Vektorähnlichkeitssuche wird dann auf diese gefilterte Teilmenge von Dokumenten durchgeführt.

Sie können die übereinstimmenden Begriffe in den Suchergebnissen hervorheben, indem Sie einen Text-Highlighter konfigurieren. Weitere Informationen finden Sie unter „Text-Highlighter “.

# Match entities with `keyword1` or `keyword2`
filter = "TEXT_MATCH(text, 'keyword1 keyword2')"

# Assuming 'embeddings' is the vector field and 'text' is the VARCHAR field
result = client.search(
    collection_name="my_collection", # Your collection name
    anns_field="embeddings", # Vector field name
    data=[query_vector], # Query vector
    filter=filter,
    search_params={"params": {"nprobe": 10}},
    limit=10, # Max. number of results to return
    output_fields=["id", "text"] # Fields to return
)
String filter = "TEXT_MATCH(text, 'keyword1 keyword2')";

SearchResp searchResp = client.search(SearchReq.builder()
        .collectionName("my_collection")
        .annsField("embeddings")
        .data(Collections.singletonList(queryVector)))
        .filter(filter)
        .topK(10)
        .outputFields(Arrays.asList("id", "text"))
        .build());
filter := "TEXT_MATCH(text, 'keyword1 keyword2')"

resultSets, err := client.Search(ctx, milvusclient.NewSearchOption(
    "my_collection", // collectionName
    10,               // limit
    []entity.Vector{entity.FloatVector(queryVector)},
).WithANNSField("embeddings").
    WithFilter(filter).
    WithOutputFields("id", "text"))
if err != nil {
    fmt.Println(err.Error())
    // handle error
}
// Match entities with `keyword1` or `keyword2`
const filter = "TEXT_MATCH(text, 'keyword1 keyword2')";

// Assuming 'embeddings' is the vector field and 'text' is the VARCHAR field
const result = await client.search(
    collection_name: "my_collection", // Your collection name
    anns_field: "embeddings", // Vector field name
    data: [query_vector], // Query vector
    filter: filter,
    params: {"nprobe": 10},
    limit: 10, // Max. number of results to return
    output_fields: ["id", "text"] //Fields to return
);
export filter="\"TEXT_MATCH(text, 'keyword1 keyword2')\""

export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{
    "collectionName": "my_collection",
    "annsField": "embeddings",
    "data": [[0.19886812562848388, 0.06023560599112088, 0.6976963061752597, 0.2614474506242501, 0.838729485096104]],
    "filter": '"$filter"',
    "searchParams": {
        "params": {
            "nprobe": 10
        }
    },
    "limit": 10,
    "outputFields": ["text","id"]
}'

Abfrage mit Textübereinstimmung

Die Textübereinstimmung kann auch für die skalare Filterung bei Suchvorgängen verwendet werden. Durch Angabe eines „ TEXT_MATCH “-Ausdrucks im Parameter „ expr “ der Methode „ query() “ können Sie Dokumente abrufen, die mit den angegebenen Begriffen übereinstimmen.

Das folgende Beispiel ruft Dokumente ab, bei denen das Feld „ text “ beide Begriffe „ keyword1 “ und „ keyword2 “ enthält.

# Match entities with both `keyword1` and `keyword2`
filter = "TEXT_MATCH(text, 'keyword1') and TEXT_MATCH(text, 'keyword2')"

result = client.query(
    collection_name="my_collection",
    filter=filter, 
    output_fields=["id", "text"]
)
String filter = "TEXT_MATCH(text, 'keyword1') and TEXT_MATCH(text, 'keyword2')";

QueryResp queryResp = client.query(QueryReq.builder()
        .collectionName("my_collection")
        .filter(filter)
        .outputFields(Arrays.asList("id", "text"))
        .build()
);
filter = "TEXT_MATCH(text, 'keyword1') and TEXT_MATCH(text, 'keyword2')"
resultSet, err := client.Query(ctx, milvusclient.NewQueryOption("my_collection").
    WithFilter(filter).
    WithOutputFields("id", "text"))
if err != nil {
    fmt.Println(err.Error())
    // handle error
}

// Match entities with both `keyword1` and `keyword2`
const filter = "TEXT_MATCH(text, 'keyword1') and TEXT_MATCH(text, 'keyword2')";

const result = await client.query(
    collection_name: "my_collection",
    filter: filter, 
    output_fields: ["id", "text"]
)
export filter="\"TEXT_MATCH(text, 'keyword1') and TEXT_MATCH(text, 'keyword2')\""

export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/query" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{
    "collectionName": "my_collection",
    "filter": '"$filter"',
    "outputFields": ["id", "text"]
}'

Hinweise

  • Das Aktivieren des Begriffsabgleichs für ein Feld löst die Erstellung eines invertierten Index aus, was Speicherressourcen beansprucht. Berücksichtigen Sie die Auswirkungen auf den Speicherplatz, wenn Sie sich für die Aktivierung dieser Funktion entscheiden, da diese je nach Textgröße, eindeutigen Tokens und dem verwendeten Analysator variieren.

  • Sobald Sie einen Analysator in Ihrem Schema definiert haben, sind dessen Einstellungen für diese Sammlung dauerhaft festgelegt. Wenn Sie zu dem Schluss kommen, dass ein anderer Analysator Ihren Anforderungen besser entspricht, können Sie die bestehende Sammlung löschen und eine neue mit der gewünschten Analysatorkonfiguration erstellen.

  • Escape-Regeln in „ filter “-Ausdrücken:

    • Zeichen, die in Ausdrücken in doppelte oder einfache Anführungszeichen gesetzt sind, werden als Zeichenfolgenkonstanten interpretiert. Wenn die Zeichenfolgenkonstante Escape-Zeichen enthält, müssen diese mit einer Escape-Sequenz dargestellt werden. Verwenden Sie beispielsweise „ \\ “, um „ \ “ darzustellen, „ \\t “, um ein Tabulatorzeichen „ \t “ darzustellen, und „ \\n “, um einen Zeilenumbruch darzustellen.

    • Wenn eine Zeichenfolgenkonstante in einfache Anführungszeichen gesetzt ist, sollte ein einfaches Anführungszeichen innerhalb der Konstante als „ \\' “ dargestellt werden, während ein doppeltes Anführungszeichen entweder als „ " “ oder „ \\" “ dargestellt werden kann. Beispiel: „ 'It\\'s milvus' “.

    • Wenn eine Zeichenfolgenkonstante in doppelte Anführungszeichen gesetzt ist, sollte ein doppeltes Anführungszeichen innerhalb der Konstante als „ \\" “ dargestellt werden, während ein einfaches Anführungszeichen entweder als „ ' “ oder „ \\' “ dargestellt werden kann. Beispiel: „ "He said \\"Hi\\"" “.