Volltextsuche

Die Volltextsuche ist eine Funktion, die Dokumente in Textdatensätzen abruft, die bestimmte Begriffe oder Phrasen enthalten, und die Ergebnisse anschließend nach Relevanz sortiert. Diese Funktion überwindet die Einschränkungen der semantischen Suche, bei der präzise Begriffe möglicherweise übersehen werden, und stellt sicher, dass Sie die genauesten und kontextuell relevantesten Ergebnisse erhalten. Zudem vereinfacht sie die Vektorsuche, indem sie Rohtext als Eingabe akzeptiert und Ihre Textdaten automatisch in spärliche Einbettungen umwandelt, ohne dass Vektoreinbettungen manuell generiert werden müssen.

Durch die Verwendung des BM25-Algorithmus zur Relevanzbewertung ist diese Funktion besonders wertvoll in RAG-Szenarien (Retrieval-Augmented Generation), in denen Dokumente priorisiert werden, die genau mit bestimmten Suchbegriffen übereinstimmen.

Durch die Integration der Volltextsuche mit der semantisch basierten dichten Vektorsuche können Sie die Genauigkeit und Relevanz der Suchergebnisse verbessern. Weitere Informationen finden Sie unter „Hybrid Search“.

BM25-Implementierung

Milvus bietet eine Volltextsuche auf Basis des BM25-Relevanzalgorithmus, einer in Informationsabrufsystemen weit verbreiteten Bewertungsfunktion, und integriert diese in den Such-Workflow, um präzise, nach Relevanz geordnete Textergebnisse zu liefern.

Die Volltextsuche in Milvus folgt dem folgenden Arbeitsablauf:

  1. Eingabe von Rohtext: Sie fügen Textdokumente ein oder geben eine Suchanfrage im Klartext ein – es sind keine Einbettungsmodelle erforderlich.

  2. Textanalyse: Milvus verwendet einen Analysator, um Ihren Text in aussagekräftige Begriffe zu verarbeiten, die indexiert und durchsucht werden können.

  3. Verarbeitung mit der BM25-Funktion: Eine integrierte Funktion wandelt diese Begriffe in spärliche Vektordarstellungen um, die für die BM25-Bewertung optimiert sind.

  4. Speicherung in einer Sammlung: Milvus speichert die resultierenden spärlichen Einbettungen in einer Sammlung, um ein schnelles Abrufen und Ranking zu ermöglichen.

  5. BM25-Relevanzbewertung: Bei der Suche wendet Milvus die BM25-Bewertungsfunktion an, um die Relevanz der Dokumente zu berechnen und die Ergebnisse in einer Rangfolge zurückzugeben, die am besten zu den Suchbegriffen passt.

Full Text Search Volltextsuche

Um die Volltextsuche zu nutzen, befolgen Sie diese Hauptschritte:

  1. Erstellen Sie eine Sammlung: Richten Sie die erforderlichen Felder ein und definieren Sie eine BM25-Funktion, die Rohtext in spärliche Einbettungen umwandelt.

  2. Daten einfügen: Importieren Sie Ihre Rohtextdokumente in die Sammlung.

  3. Suchen durchführen: Verwenden Sie Suchanfragen in natürlicher Sprache, um nach der BM25-Relevanz sortierte Ergebnisse abzurufen.

Um die BM25-basierte Volltextsuche zu aktivieren, müssen Sie eine Sammlung mit den erforderlichen Feldern vorbereiten, eine BM25-Funktion zur Generierung spärlicher Vektoren definieren, einen Index konfigurieren und anschließend die Sammlung erstellen.

Schema-Felder definieren

Ihr Sammlungsschema muss mindestens drei Pflichtfelder enthalten:

  • Primärfeld: Identifiziert jede Entität in der Sammlung eindeutig.

  • Zeichenfolgenfeld (VARCHAR oder TEXT): Speichert Textdokumente im Rohformat. „ enable_analyzer=True “ muss gesetzt sein, damit Milvus den Text für das BM25-Relevanzranking verarbeiten kann. Standardmäßig verwendet Milvus den standard Analyzer für die Textanalyse. Informationen zur Konfiguration eines anderen Analyzers finden Sie unter „Analyzer-Übersicht“. Die Beispiele auf dieser Seite verwenden „ VARCHAR “; bei Langtexten können Sie das Eingabefeld als „ TEXT “ definieren und „ max_length “ weglassen. Ein vollständiges Beispiel finden Sie unter „Textfeld“.

  • Sparse-Vektor-Feld (SPARSE_FLOAT_VECTOR): Speichert spärliche Einbettungen, die automatisch von der BM25-Funktion generiert werden.

from pymilvus import MilvusClient, DataType, Function, FunctionType

client = MilvusClient(
    uri="http://localhost:19530",
    token="root:Milvus"
)

schema = client.create_schema()

schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True, auto_id=True) # Primary field
schema.add_field(field_name="text", datatype=DataType.VARCHAR, max_length=1000, enable_analyzer=True) # Text field
schema.add_field(field_name="sparse", datatype=DataType.SPARSE_FLOAT_VECTOR) # Sparse vector field; no dim required for sparse vectors
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()
        .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)
        .build());
schema.addField(AddFieldReq.builder()
        .fieldName("sparse")
        .dataType(DataType.SparseFloatVector)
        .build());
import (
    "context"
    "fmt"

    "github.com/milvus-io/milvus/client/v2/column"
    "github.com/milvus-io/milvus/client/v2/entity"
    "github.com/milvus-io/milvus/client/v2/index"
    "github.com/milvus-io/milvus/client/v2/milvusclient"
)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

milvusAddr := "localhost:19530"
client, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
    Address: milvusAddr,
})
if err != nil {
    fmt.Println(err.Error())
    // handle error
}
defer client.Close(ctx)

schema := entity.NewSchema()
schema.WithField(entity.NewField().
    WithName("id").
    WithDataType(entity.FieldTypeInt64).
    WithIsPrimaryKey(true).
    WithIsAutoID(true),
).WithField(entity.NewField().
    WithName("text").
    WithDataType(entity.FieldTypeVarChar).
    WithEnableAnalyzer(true).
    WithMaxLength(1000),
).WithField(entity.NewField().
    WithName("sparse").
    WithDataType(entity.FieldTypeSparseVector),
)
import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node";

const address = "http://localhost:19530";
const token = "root:Milvus";
const client = new MilvusClient({address, token});
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: "sparse",
    data_type: DataType.SparseFloatVector,
  },
];

console.log(res.results)
export schema='{
        "autoId": true,
        "enabledDynamicField": false,
        "fields": [
            {
                "fieldName": "id",
                "dataType": "Int64",
                "isPrimary": true
            },
            {
                "fieldName": "text",
                "dataType": "VarChar",
                "elementTypeParams": {
                    "max_length": 1000,
                    "enable_analyzer": true
                }
            },
            {
                "fieldName": "sparse",
                "dataType": "SparseFloatVector"
            }
        ]
    }'

In der vorstehenden Konfiguration

  • id: dient als Primärschlüssel und wird automatisch mit ` auto_id=True` generiert.

  • text: speichert Ihre Rohtextdaten für Volltextsuchvorgänge. Das Feld kann „ VARCHAR “ für begrenzte Textmengen oder „ TEXT “ für lange Quellinhalte verwenden.

  • sparse: Ein Vektorfeld, das für die Speicherung intern generierter spärlicher Einbettungen für Volltextsuche reserviert ist. Der Datentyp muss „ SPARSE_FLOAT_VECTOR “ sein.

Definieren Sie die BM25-Funktion

Die BM25-Funktion wandelt tokenisierten Text in spärliche Vektoren um, die die BM25-Bewertung unterstützen.

Definieren Sie die Funktion und fügen Sie sie Ihrem Schema hinzu:

bm25_function = Function(
    name="text_bm25_emb", # Function name
    input_field_names=["text"], # Name of the VARCHAR or TEXT field containing raw text data
    output_field_names=["sparse"], # Name of the SPARSE_FLOAT_VECTOR field reserved to store generated embeddings
    function_type=FunctionType.BM25, # Set to `BM25`
)

schema.add_function(bm25_function)
import io.milvus.common.clientenum.FunctionType;
import io.milvus.v2.service.collection.request.CreateCollectionReq.Function;

import java.util.*;

schema.addFunction(Function.builder()
        .functionType(FunctionType.BM25)
        .name("text_bm25_emb")
        .inputFieldNames(Collections.singletonList("text"))
        .outputFieldNames(Collections.singletonList("sparse"))
        .build());
function := entity.NewFunction().
    WithName("text_bm25_emb").
    WithInputFields("text").
    WithOutputFields("sparse").
    WithType(entity.FunctionTypeBM25)
schema.WithFunction(function)
const functions = [
    {
      name: 'text_bm25_emb',
      description: 'bm25 function',
      type: FunctionType.BM25,
      input_field_names: ['text'],
      output_field_names: ['sparse'],
      params: {},
    },
];
export schema='{
        "autoId": true,
        "enabledDynamicField": false,
        "fields": [
            {
                "fieldName": "id",
                "dataType": "Int64",
                "isPrimary": true
            },
            {
                "fieldName": "text",
                "dataType": "VarChar",
                "elementTypeParams": {
                    "max_length": 1000,
                    "enable_analyzer": true
                }
            },
            {
                "fieldName": "sparse",
                "dataType": "SparseFloatVector"
            }
        ],
        "functions": [
            {
                "name": "text_bm25_emb",
                "type": "BM25",
                "inputFieldNames": ["text"],
                "outputFieldNames": ["sparse"],
                "params": {}
            }
        ]
    }'

Parameter

Beschreibung

name

Der Name der Funktion. Diese Funktion wandelt Ihren Rohtext aus dem Feld „ text “ in BM25-kompatible spärliche Vektoren um, die im Feld „ sparse “ gespeichert werden.

input_field_names

Der Name des Feldes „ VARCHAR “ oder „ TEXT “, für das eine Konvertierung von Text in spärliche Vektoren erforderlich ist. Bei „ FunctionType.BM25 “ akzeptiert dieser Parameter nur einen Feldnamen.

output_field_names

Der Name des Feldes, in dem die intern generierten Sparse-Vektoren gespeichert werden. Bei „ FunctionType.BM25 “ akzeptiert dieser Parameter nur einen Feldnamen.

function_type

Der Typ der zu verwendenden Funktion. Muss „ FunctionType.BM25 “ lauten.

Wenn mehrere Textfelder eine BM25-Verarbeitung erfordern, definieren Sie pro Feld eine BM25-Funktion mit jeweils einem eindeutigen Namen und einem Ausgabefeld.

Konfigurieren Sie den Index

Nachdem Sie das Schema mit den erforderlichen Feldern und der integrierten Funktion definiert haben, richten Sie den Index für Ihre Sammlung ein.

index_params = client.prepare_index_params()

index_params.add_index(
    field_name="sparse",

    index_type="SPARSE_INVERTED_INDEX",
    metric_type="BM25",
    params={
        "inverted_index_algo": "DAAT_MAXSCORE",
        "bm25_k1": 1.2,
        "bm25_b": 0.75
    }

)
import io.milvus.v2.common.IndexParam;

Map<String,Object> params = new HashMap<>();
params.put("inverted_index_algo", "DAAT_MAXSCORE");
params.put("bm25_k1", 1.2);
params.put("bm25_b", 0.75);

List<IndexParam> indexes = new ArrayList<>();
indexes.add(IndexParam.builder()
        .fieldName("sparse")
        .indexType(IndexParam.IndexType.AUTOINDEX)
        .metricType(IndexParam.MetricType.BM25)
        .extraParams(params)
        .build());    
indexOption := milvusclient.NewCreateIndexOption("my_collection", "sparse",
    index.NewAutoIndex(entity.MetricType(entity.BM25)))
    .WithExtraParam("inverted_index_algo", "DAAT_MAXSCORE")
    .WithExtraParam("bm25_k1", 1.2)
    .WithExtraParam("bm25_b", 0.75)
const index_params = [
  {
    field_name: "sparse",
    metric_type: "BM25",
    index_type: "SPARSE_INVERTED_INDEX",
    params: {
        "inverted_index_algo": "DAAT_MAXSCORE",
        "bm25_k1": 1.2,
        "bm25_b": 0.75
    }
  },
];
export indexParams='[
        {
            "fieldName": "sparse",
            "metricType": "BM25",
            "indexType": "AUTOINDEX",
            "params":{
               "inverted_index_algo": "DAAT_MAXSCORE",
               "bm25_k1": 1.2,
               "bm25_b": 0.75
            }
        }
    ]'

Parameter

Beschreibung

field_name

Der Name des zu indizierenden Vektorfeldes. Für die Volltextsuche sollte dies das Feld sein, in dem die generierten Sparse-Vektoren gespeichert werden. Setzen Sie den Wert in diesem Beispiel auf „ sparse “.

index_type

Der Typ des zu erstellenden Index. Für die BM25-Volltextsuche in Milvus setzen Sie diesen Wert auf „ SPARSE_INVERTED_INDEX “. Weitere Informationen finden Sie unter SPARSE_INVERTED_INDEX.

metric_type

Der Wert für diesen Parameter muss speziell für die Volltextsuche auf „ BM25 “ gesetzt werden.

params

Ein Wörterbuch mit zusätzlichen, für den Index spezifischen Parametern.

params.inverted_index_algo

Der Algorithmus, der zum Erstellen und Abfragen des spärlichen BM25-Invertierindexes verwendet wird. Gültige Werte:

params.bm25_k1

Steuert die Sättigung der Termhäufigkeit. Höhere Werte erhöhen die Bedeutung der Termhäufigkeiten bei der Dokumentenrangfolge. Empfohlener Bereich: [1,2; 2,0]. Standardwert: 1,2.

params.bm25_b

Steuert, inwieweit die Dokumentlänge normalisiert wird. Typischerweise werden Werte zwischen 0 und 1 verwendet, wobei der Standardwert 0,75 beträgt. Ein Wert von 0 bedeutet keine Längennormalisierung, während ein Wert von 1 eine vollständige Längennormalisierung bedeutet.

Erstellen Sie die Sammlung

Erstellen Sie nun die Sammlung unter Verwendung der definierten Schema- und Indexparameter.

client.create_collection(
    collection_name='my_collection', 
    schema=schema, 
    index_params=index_params
)
import io.milvus.v2.service.collection.request.CreateCollectionReq;

CreateCollectionReq requestCreate = CreateCollectionReq.builder()
        .collectionName("my_collection")
        .collectionSchema(schema)
        .indexParams(indexes)
        .build();
client.createCollection(requestCreate);
err = client.CreateCollection(ctx,
    milvusclient.NewCreateCollectionOption("my_collection", schema).
        WithIndexOptions(indexOption))
if err != nil {
    fmt.Println(err.Error())
    // handle error
}
await client.create_collection(
    collection_name: 'my_collection', 
    schema: schema, 
    index_params: index_params,
    functions: functions
);
export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d "{
    \"collectionName\": \"my_collection\",
    \"schema\": $schema,
    \"indexParams\": $indexParams
}"

Textdaten einfügen

Nachdem Sie Ihre Sammlung und Ihren Index eingerichtet haben, können Sie Textdaten einfügen. Dabei müssen Sie lediglich den Rohtext bereitstellen. Die zuvor definierte integrierte Funktion generiert automatisch den entsprechenden Sparse-Vektor für jeden Texteintrag.

client.insert('my_collection', [
    {'text': 'information retrieval is a field of study.'},
    {'text': 'information retrieval focuses on finding relevant information in large datasets.'},
    {'text': 'data mining and information retrieval overlap in research.'},
])
import com.google.gson.Gson;
import com.google.gson.JsonObject;

import io.milvus.v2.service.vector.request.InsertReq;

Gson gson = new Gson();
List<JsonObject> rows = Arrays.asList(
        gson.fromJson("{\"text\": \"information retrieval is a field of study.\"}", JsonObject.class),
        gson.fromJson("{\"text\": \"information retrieval focuses on finding relevant information in large datasets.\"}", JsonObject.class),
        gson.fromJson("{\"text\": \"data mining and information retrieval overlap in research.\"}", JsonObject.class)
);

client.insert(InsertReq.builder()
        .collectionName("my_collection")
        .data(rows)
        .build());
// go
await client.insert({
collection_name: 'my_collection', 
data: [
    {'text': 'information retrieval is a field of study.'},
    {'text': 'information retrieval focuses on finding relevant information in large datasets.'},
    {'text': 'data mining and information retrieval overlap in research.'},
]);
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/insert" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{
    "data": [
        {"text": "information retrieval is a field of study."},
        {"text": "information retrieval focuses on finding relevant information in large datasets."},
        {"text": "data mining and information retrieval overlap in research."}       
    ],
    "collectionName": "my_collection"
}'

Sobald Sie Daten in Ihre Sammlung eingefügt haben, können Sie Volltextsuchen mithilfe von Rohtext-Abfragen durchführen. Milvus wandelt Ihre Abfrage automatisch in einen spärlichen Vektor um, ordnet die übereinstimmenden Suchergebnisse mithilfe des BM25-Algorithmus nach Relevanz und gibt anschließend die Top-K-Ergebnisse (limit) zurück.

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

res = client.search(
    collection_name='my_collection', 
    data=['whats the focus of information retrieval?'],
    anns_field='sparse',
    output_fields=['text'], # Fields to return in search results; sparse field cannot be output
    limit=3,
)

print(res)
import io.milvus.v2.service.vector.request.SearchReq;
import io.milvus.v2.service.vector.request.data.EmbeddedText;
import io.milvus.v2.service.vector.response.SearchResp;

Map<String,Object> searchParams = new HashMap<>();

SearchResp searchResp = client.search(SearchReq.builder()
        .collectionName("my_collection")
        .data(Collections.singletonList(new EmbeddedText("whats the focus of information retrieval?")))
        .annsField("sparse")
        .topK(3)
        .searchParams(searchParams)
        .outputFields(Collections.singletonList("text"))
        .build());
annSearchParams := index.NewCustomAnnParam()
resultSets, err := client.Search(ctx, milvusclient.NewSearchOption(
    "my_collection", // collectionName
    3,               // limit
    []entity.Vector{entity.Text("whats the focus of information retrieval?")},
).WithConsistencyLevel(entity.ClStrong).
    WithANNSField("sparse").
    WithAnnParam(annSearchParams).
    WithOutputFields("text"))
if err != nil {
    fmt.Println(err.Error())
    // handle error
}

for _, resultSet := range resultSets {
    fmt.Println("IDs: ", resultSet.IDs.FieldData().GetScalars())
    fmt.Println("Scores: ", resultSet.Scores)
    fmt.Println("text: ", resultSet.GetColumn("text").FieldData().GetScalars())
}
await client.search(
    collection_name: 'my_collection', 
    data: ['whats the focus of information retrieval?'],
    anns_field: 'sparse',
    output_fields: ['text'],
    limit: 3,
)
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
--data-raw '{
    "collectionName": "my_collection",
    "data": [
        "whats the focus of information retrieval?"
    ],
    "annsField": "sparse",
    "limit": 3,
    "outputFields": [
        "text"
    ],
    "searchParams":{
        "params":{}
    }
}'

Parameter

Beschreibung

search_params

Ein Wörterbuch, das Suchparameter enthält.

params.drop_ratio_search

Anteil der Begriffe mit geringer Relevanz, die bei der Suche ignoriert werden sollen. Der Wert muss im Bereich [0,0; 1,0) liegen. Weitere Informationen finden Sie unter „Sparse Vector“.

data

Unverarbeiteter Suchtext in natürlicher Sprache. Milvus wandelt Ihre Textabfrage automatisch mithilfe der BM25-Funktion in Sparse-Vektoren um – geben Sie keine vorab berechneten Vektoren an.

anns_field

Der Name des Feldes, das intern generierte Sparse-Vektoren enthält.

output_fields

Liste der Feldnamen, die in den Suchergebnissen zurückgegeben werden sollen. Unterstützt alle Felder mit Ausnahme des Sparse-Vektor-Feldes, das die von BM25 generierten Einbettungen enthält. Zu den gängigen Ausgabefeldern gehören das Primärschlüsselfeld (z. B. id) und das Originaltextfeld (z. B. text). Weitere Informationen finden Sie in den FAQ.

limit

Maximale Anzahl der zurückzugebenden Top-Treffer.

FAQ

Nein, auf die von der BM25-Funktion generierten Sparse-Vektoren kann in der Volltextsuche nicht direkt zugegriffen werden und sie können auch nicht ausgegeben werden. Hier sind die Details:

  • Die BM25-Funktion generiert intern Sparse-Vektoren für das Ranking und die Suche

  • Diese Vektoren werden im „sparse“-Feld gespeichert, können jedoch nicht in output_fields

  • Sie können nur die ursprünglichen Textfelder und Metadaten (wie „ id “, „ text “) ausgeben

Beispiel:

# ❌ This throws an error - you cannot output the sparse field
client.search(
    collection_name='my_collection', 
    data=['query text'],
    anns_field='sparse',
    output_fields=['text', 'sparse']  # 'sparse' causes an error
    limit=3,
    search_params=search_params
)

# ✅ This works - output text fields only
client.search(
    collection_name='my_collection', 
    data=['query text'],
    anns_field='sparse',
    output_fields=['text']
    limit=3,
    search_params=search_params
)

Warum muss ich ein Sparse-Vektor-Feld definieren, wenn ich nicht darauf zugreifen kann?

Das spärliche Vektorfeld dient als interner Suchindex, ähnlich wie Datenbankindizes, mit denen Benutzer nicht direkt interagieren.

Entwurfsgrundlagen:

  • Trennung der Anliegen: Sie arbeiten mit Text (Eingabe/Ausgabe), Milvus kümmert sich um die Vektoren (interne Verarbeitung)

  • Leistung: Vorberechnete spärliche Vektoren ermöglichen ein schnelles BM25-Ranking bei Abfragen

  • Benutzererfahrung: Komplexe Vektoroperationen werden hinter einer einfachen Textschnittstelle verborgen

Wenn Sie auf Vektoren zugreifen müssen:

  • Verwenden Sie manuelle Operationen mit spärlichen Vektoren anstelle der Volltextsuche

  • Erstellen Sie separate Sammlungen für benutzerdefinierte Workflows mit spärlichen Vektoren

Weitere Informationen finden Sie unter „Sparse-Vektor“.