Volltextsuche
Die Volltextsuche ist eine Funktion, die Dokumente mit bestimmten Begriffen oder Phrasen in Textdatensätzen abruft und die Ergebnisse dann nach Relevanz einstuft. Diese Funktion überwindet die Beschränkungen der semantischen Suche, bei der präzise Begriffe übersehen werden können, und stellt sicher, dass Sie die genauesten und kontextuell relevanten Ergebnisse erhalten. Darüber hinaus vereinfacht es die Vektorsuche, indem es Rohtexteingaben akzeptiert und Ihre Textdaten automatisch in spärliche Einbettungen konvertiert, ohne dass Sie manuell Vektoreinbettungen erstellen müssen.
Durch die Verwendung des BM25-Algorithmus für die Relevanzbewertung ist diese Funktion besonders wertvoll in Retrieval-Augmented-Generating-Szenarien (RAG), bei denen Dokumente mit hoher Übereinstimmung mit bestimmten Suchbegriffen priorisiert werden.
Durch die Integration der Volltextsuche mit der semantikbasierten dichten Vektorsuche können Sie die Genauigkeit und Relevanz der Suchergebnisse verbessern. Weitere Informationen finden Sie unter Hybride Suche.
BM25-Implementierung
Milvus bietet eine Volltextsuche, die auf dem BM25-Relevanzalgorithmus basiert, einer in Information-Retrieval-Systemen weit verbreiteten Bewertungsfunktion, und Milvus integriert sie in den Suchworkflow, um genaue, nach Relevanz eingestufte Textergebnisse zu liefern.
Die Volltextsuche in Milvus folgt dem nachstehenden Arbeitsablauf:
Rohtexteingabe: Sie fügen Textdokumente ein oder stellen eine Abfrage mit reinem Text, ohne Einbettungsmodelle.
Text-Analyse: Milvus verwendet einen Analysator, um Ihren Text in sinnvolle Begriffe zu verarbeiten, die indiziert und durchsucht werden können.
BM25-Funktionsverarbeitung: Eine integrierte Funktion wandelt diese Begriffe in spärliche Vektordarstellungen um, die für die BM25-Bewertung optimiert sind.
Sammlungsspeicher: Milvus speichert die resultierenden spärlichen Einbettungen in einer Sammlung für schnelles Auffinden und Ranking.
BM25-Relevanz-Bewertung: Bei der Suche wendet Milvus die BM25-Bewertungsfunktion an, um die Relevanz der Dokumente zu berechnen und die am besten mit den Suchbegriffen übereinstimmenden Ergebnisse zu ermitteln.
Volltextsuche
Um die Volltextsuche zu nutzen, führen Sie die folgenden Schritte aus:
Erstellen Sie eine Sammlung: Richten Sie die erforderlichen Felder ein und definieren Sie eine BM25-Funktion, die Rohtext in Sparse Embeddings umwandelt.
Daten einfügen: Fügen Sie Ihre Rohtextdokumente in die Sammlung ein.
Suchen durchführen: Verwenden Sie natürlichsprachliche Suchanfragen, um auf der Grundlage der BM25-Relevanz geordnete Ergebnisse zu erhalten.
Erstellen einer Sammlung für die BM25-Volltextsuche
Um die BM25-Volltextsuche zu aktivieren, müssen Sie eine Sammlung mit den erforderlichen Feldern vorbereiten, eine BM25-Funktion zur Erzeugung von Sparse-Vektoren definieren, einen Index konfigurieren und dann die Sammlung erstellen.
Definieren von Schemafeldern
Ihr Sammlungsschema muss mindestens drei Pflichtfelder enthalten:
Primäres Feld: Identifiziert jede Entität in der Sammlung eindeutig.
Textfeld (
VARCHAR): Speichert Rohtextdokumente. Sie müssenenable_analyzer=Trueeinstellen, damit Milvus den Text für die BM25-Relevanzeinstufung verarbeiten kann. Standardmäßig verwendet Milvus denstandardAnalysator für die Textanalyse. Um einen anderen Analyzer zu konfigurieren, siehe Analyzer-Übersicht.Sparse Vector Field (
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 vorangehenden Konfiguration,
id: dient als Primärschlüssel und wird automatisch mitauto_id=Truegeneriert.text: speichert Ihre Rohtextdaten für Volltextsuchvorgänge. Der Datentyp mussVARCHARsein, daVARCHARder Milvus-String-Datentyp für die Textspeicherung ist.sparseVektorfeld: Ein Vektorfeld, das für die Speicherung von intern generierten Sparse Embeddings für Volltextsuchoperationen reserviert ist. Der Datentyp mussSPARSE_FLOAT_VECTORsein.
Definieren Sie die BM25-Funktion
Die BM25-Funktion konvertiert tokenisierten Text in Sparse-Vektoren, die die BM25-Bewertung unterstützen.
Definieren Sie die Funktion und fügen Sie sie zu Ihrem Schema hinzu:
bm25_function = Function(
name="text_bm25_emb", # Function name
input_field_names=["text"], # Name of the VARCHAR 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 erzeugten Sparse-Vektoren gespeichert werden. Für |
|
Der Typ der zu verwendenden Funktion. Muss |
Wenn mehrere VARCHAR Felder eine BM25-Verarbeitung erfordern, definieren Sie eine BM25-Funktion pro Feld, jedes mit einem eindeutigen Namen und 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, das die generierten Sparse-Vektoren speichert. In diesem Beispiel setzen Sie den Wert auf |
|
Der Typ des zu erstellenden Indexes. |
|
Der Wert für diesen Parameter muss speziell für die Volltextsuchfunktionalität auf |
|
Ein Wörterbuch mit zusätzlichen Parametern, die für den Index spezifisch sind. |
|
Der Algorithmus, der für den Aufbau und die Abfrage des Indexes verwendet wird. Gültige Werte:
|
|
Steuert die Sättigung der Termhäufigkeit. Höhere Werte erhöhen die Bedeutung der Termhäufigkeit bei der Bewertung von Dokumenten. Wertebereich: [1.2, 2.0]. |
|
Steuert das Ausmaß, in dem die Dokumentlänge normalisiert wird. Üblicherweise werden Werte zwischen 0 und 1 verwendet, wobei der Standardwert bei 0,75 liegt. Ein Wert von 1 bedeutet keine Längennormalisierung, während ein Wert von 0 eine vollständige Normalisierung 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" \
-d "{
\"collectionName\": \"my_collection\",
\"schema\": $schema,
\"indexParams\": $indexParams
}"
Einfügen von Textdaten
Nachdem Sie Ihre Sammlung und Ihren Index eingerichtet haben, können Sie nun Textdaten einfügen. Bei diesem Vorgang müssen Sie nur den Rohtext bereitstellen. Die integrierte Funktion, die wir zuvor definiert haben, erzeugt 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" \
-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 eine Volltextsuche mit Rohtextabfragen durchführen. Milvus konvertiert Ihre Abfrage automatisch in einen Sparse-Vektor und ordnet die übereinstimmenden Suchergebnisse mit dem BM25-Algorithmus ein und gibt dann die TopK (limit) Ergebnisse zurück.
Sie können die übereinstimmenden Begriffe in den Suchergebnissen hervorheben, indem Sie einen Text-Highlighter konfigurieren. Siehe Text-Highlighter für weitere Informationen.
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" \
--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 mit Suchparametern. |
|
Anteil der unwichtigen Begriffe, die bei der Suche ignoriert werden sollen. Für Details siehe Sparse Vector. |
|
Roher Abfragetext in natürlicher Sprache. Milvus konvertiert Ihre Textabfrage automatisch in Sparse Vectors unter Verwendung der BM25-Funktion - geben Sie keine vorberechneten 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 Feldes "Sparse Vector", das BM25-generierte Einbettungen enthält. Übliche Ausgabefelder sind das Primärschlüsselfeld (z. B. |
|
Maximale Anzahl von Top-Treffern, die zurückgegeben werden. |
FAQ
Kann ich die von der BM25-Funktion erzeugten Sparse-Vektoren in der Volltextsuche ausgeben oder darauf zugreifen?
Nein, die von der BM25-Funktion erzeugten Sparse-Vektoren sind in der Volltextsuche nicht direkt zugänglich oder ausgabefähig. Hier sind die Details:
Die BM25-Funktion generiert intern Sparse-Vektoren für Ranking und Retrieval
Diese Vektoren werden im Sparse-Feld gespeichert, können aber nicht in die Volltextsuche einbezogen werden.
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-Vektorfeld definieren, wenn ich nicht darauf zugreifen kann?
Das spärliche Vektorfeld dient als interner Suchindex, ähnlich wie Datenbankindizes, mit denen die Benutzer nicht direkt interagieren.
Entwurfsbegründung:
Trennung der Belange: Sie arbeiten mit Text (Eingabe/Ausgabe), Milvus bearbeitet Vektoren (interne Verarbeitung)
Leistung: Vorberechnete spärliche Vektoren ermöglichen ein schnelles BM25-Ranking bei Abfragen
Benutzerfreundlichkeit: Abstrahiert komplexe Vektoroperationen hinter einer einfachen Textschnittstelle
Wenn Sie Zugriff auf Vektoren benötigen:
Verwenden Sie manuelle Sparse-Vector-Operationen anstelle einer Volltextsuche
Erstellen Sie separate Sammlungen für benutzerdefinierte Sparse-Vector-Workflows
Einzelheiten finden Sie unter Sparse Vector.