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:
Eingabe von Rohtext: Sie fügen Textdokumente ein oder geben eine Suchanfrage im Klartext ein – es sind keine Einbettungsmodelle erforderlich.
Textanalyse: Milvus verwendet einen Analysator, um Ihren Text in aussagekräftige Begriffe zu verarbeiten, die indexiert und durchsucht werden können.
Verarbeitung mit der BM25-Funktion: Eine integrierte Funktion wandelt diese Begriffe in spärliche Vektordarstellungen um, die für die BM25-Bewertung optimiert sind.
Speicherung in einer Sammlung: Milvus speichert die resultierenden spärlichen Einbettungen in einer Sammlung, um ein schnelles Abrufen und Ranking zu ermöglichen.
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.
Volltextsuche
Um die Volltextsuche zu nutzen, befolgen Sie diese Hauptschritte:
Erstellen Sie eine Sammlung: Richten Sie die erforderlichen Felder ein und definieren Sie eine BM25-Funktion, die Rohtext in spärliche Einbettungen umwandelt.
Daten einfügen: Importieren Sie Ihre Rohtextdokumente in die Sammlung.
Suchen durchführen: Verwenden Sie Suchanfragen in natürlicher Sprache, um nach der BM25-Relevanz sortierte Ergebnisse abzurufen.
Erstellen einer Sammlung für die BM25-Volltextsuche
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 (
VARCHARoderTEXT): 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 denstandardAnalyzer 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 |
|---|---|
|
Der Name der Funktion. Diese Funktion wandelt Ihren Rohtext aus dem Feld „ |
|
Der Name des Feldes „ |
|
Der Name des Feldes, in dem die intern generierten Sparse-Vektoren gespeichert werden. Bei „ |
|
Der Typ der zu verwendenden Funktion. Muss „ |
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 |
|---|---|
|
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 „ |
|
Der Typ des zu erstellenden Index. Für die BM25-Volltextsuche in Milvus setzen Sie diesen Wert auf „ |
|
Der Wert für diesen Parameter muss speziell für die Volltextsuche auf „ |
|
Ein Wörterbuch mit zusätzlichen, für den Index spezifischen Parametern. |
|
Der Algorithmus, der zum Erstellen und Abfragen des spärlichen BM25-Invertierindexes verwendet wird. Gültige Werte:
|
|
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. |
|
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"
}'
Volltextsuche durchführen
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 |
|---|---|
|
Ein Wörterbuch, das Suchparameter enthält. |
|
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“. |
|
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. |
|
Der Name des Feldes, das intern generierte Sparse-Vektoren enthält. |
|
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. |
|
Maximale Anzahl der zurückzugebenden Top-Treffer. |
FAQ
Kann ich die von der BM25-Funktion in der Volltextsuche generierten Sparse-Vektoren ausgeben oder darauf zugreifen?
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_fieldsSie 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“.