Pencarian Teks Lengkap

Pencarian teks lengkap adalah fitur yang mengambil dokumen yang mengandung istilah atau frasa tertentu dalam kumpulan data teks, kemudian mengurutkan hasilnya berdasarkan relevansi. Fitur ini mengatasi keterbatasan pencarian semantik, yang mungkin mengabaikan istilah yang tepat, sehingga memastikan Anda mendapatkan hasil yang paling akurat dan relevan secara kontekstual. Selain itu, fitur ini menyederhanakan pencarian vektor dengan menerima masukan teks mentah, secara otomatis mengubah data teks Anda menjadi embedding spars tanpa perlu membuat embedding vektor secara manual.

Dengan menggunakan algoritma BM25 untuk penilaian relevansi, fitur ini sangat berguna dalam skenario Retrieval-Augmented Generation (RAG), di mana fitur ini memprioritaskan dokumen yang sangat sesuai dengan istilah pencarian tertentu.

Dengan mengintegrasikan pencarian teks lengkap dengan pencarian vektor padat berbasis semantik, Anda dapat meningkatkan akurasi dan relevansi hasil pencarian. Untuk informasi lebih lanjut, lihat Pencarian Hibrida.

Implementasi BM25

Milvus menyediakan pencarian teks lengkap yang didukung oleh algoritma relevansi BM25, sebuah fungsi penilaian yang banyak digunakan dalam sistem pencarian informasi, dan Milvus mengintegrasikannya ke dalam alur kerja pencarian untuk memberikan hasil teks yang akurat dan diurutkan berdasarkan relevansi.

Pencarian teks lengkap di Milvus mengikuti alur kerja berikut:

  1. Masukan teks mentah: Anda memasukkan dokumen teks atau memberikan kueri menggunakan teks biasa, tanpa memerlukan model embedding.

  2. Analisis teks: Milvus menggunakan penganalisis untuk memproses teks Anda menjadi istilah-istilah bermakna yang dapat diindeks dan dicari.

  3. Pemrosesan fungsi BM25: Fungsi bawaan mengubah istilah-istilah ini menjadi representasi vektor spars yang dioptimalkan untuk penilaian BM25.

  4. Penyimpanan koleksi: Milvus menyimpan embedding langka yang dihasilkan dalam sebuah koleksi untuk pengambilan dan pemeringkatan yang cepat.

  5. Penilaian relevansi BM25: Saat pencarian, Milvus menerapkan fungsi penilaian BM25 untuk menghitung relevansi dokumen dan menampilkan hasil yang diurutkan berdasarkan kesesuaian terbaik dengan istilah kueri.

Full Text Search Pencarian Teks Lengkap

Untuk menggunakan pencarian teks lengkap, ikuti langkah-langkah utama berikut:

  1. Buat koleksi: Atur bidang yang diperlukan dan tentukan fungsi BM25 yang mengubah teks mentah menjadi representasi vektor langka.

  2. Masukkan data: Impor dokumen teks mentah Anda ke dalam koleksi.

  3. Lakukan pencarian: Gunakan teks kueri dalam bahasa alami untuk mengambil hasil yang diurutkan berdasarkan relevansi BM25.

Untuk mengaktifkan pencarian teks lengkap yang didukung BM25, Anda harus menyiapkan koleksi dengan bidang yang diperlukan, tentukan fungsi BM25 untuk menghasilkan vektor spars, konfigurasikan indeks, lalu buat koleksi tersebut.

Tentukan bidang skema

Skema koleksi Anda harus mencakup setidaknya tiga bidang yang diperlukan:

  • Bidang utama: Mengidentifikasi setiap entitas dalam koleksi secara unik.

  • Bidang string (VARCHAR atau TEXT): Menyimpan dokumen teks mentah. Harus mengatur enable_analyzer=True agar Milvus dapat memproses teks untuk peringkat relevansi BM25. Secara default, Milvus menggunakan standard analyzer untuk analisis teks. Untuk mengonfigurasi analyzer yang berbeda, lihat Ikhtisar Analyzer. Contoh-contoh pada halaman ini menggunakan VARCHAR; untuk teks panjang, Anda dapat mendefinisikan bidang input sebagai TEXT dan mengabaikan max_length. Untuk contoh lengkap, lihat Bidang Teks.

  • Bidang vektor jarang (SPARSE_FLOAT_VECTOR): Menyimpan embedding jarang yang dihasilkan secara otomatis oleh fungsi BM25.

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"
            }
        ]
    }'

Dalam konfigurasi di atas,

  • id: berfungsi sebagai kunci utama dan dihasilkan secara otomatis dengan auto_id=True.

  • text: menyimpan data teks mentah Anda untuk operasi pencarian teks lengkap. Bidang ini dapat menggunakan ` VARCHAR ` untuk teks terbatas atau ` TEXT ` untuk konten sumber yang panjang.

  • sparse: bidang vektor yang disediakan untuk menyimpan embedding jarang yang dihasilkan secara internal guna operasi pencarian teks lengkap. Tipe datanya harus SPARSE_FLOAT_VECTOR.

Tentukan fungsi BM25

Fungsi BM25 mengubah teks yang telah ditokenisasi menjadi vektor langka yang mendukung penilaian BM25.

Tentukan fungsi tersebut dan tambahkan ke skema Anda:

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

Deskripsi

name

Nama fungsi. Fungsi ini mengubah teks mentah Anda dari bidang ` text ` menjadi vektor langka yang kompatibel dengan BM25, yang akan disimpan di bidang ` sparse `.

input_field_names

Nama bidang ` VARCHAR ` atau ` TEXT ` yang memerlukan konversi teks ke vektor langka. Untuk ` FunctionType.BM25`, parameter ini hanya menerima satu nama bidang.

output_field_names

Nama bidang tempat vektor langka yang dihasilkan secara internal akan disimpan. Untuk ` FunctionType.BM25`, parameter ini hanya menerima satu nama bidang.

function_type

Jenis fungsi yang akan digunakan. Harus berupa ` FunctionType.BM25`.

Jika beberapa bidang teks memerlukan pemrosesan BM25, tentukan satu fungsi BM25 per bidang, masing-masing dengan nama dan bidang keluaran yang unik.

Konfigurasikan indeks

Setelah mendefinisikan skema dengan bidang-bidang yang diperlukan dan fungsi bawaan, atur indeks untuk koleksi Anda.

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

Deskripsi

field_name

Nama bidang vektor yang akan diindeks. Untuk pencarian teks lengkap, bidang ini haruslah bidang yang menyimpan vektor sparse yang dihasilkan. Dalam contoh ini, atur nilainya menjadi sparse.

index_type

Jenis indeks yang akan dibuat. Untuk pencarian teks lengkap BM25 di Milvus, atur nilai ini menjadi SPARSE_INVERTED_INDEX. Untuk informasi lebih lanjut, lihat SPARSE_INVERTED_INDEX.

metric_type

Nilai untuk parameter ini harus diatur ke BM25 secara khusus untuk fungsi pencarian teks lengkap.

params

Kamus parameter tambahan yang spesifik untuk indeks.

params.inverted_index_algo

Algoritma yang digunakan untuk membangun dan melakukan kueri pada indeks terbalik BM25 yang jarang. Nilai yang valid:

params.bm25_k1

Mengontrol saturasi frekuensi istilah. Nilai yang lebih tinggi meningkatkan bobot frekuensi istilah dalam peringkat dokumen. Rentang yang disarankan: [1,2; 2,0]. Nilai default: 1,2.

params.bm25_b

Mengontrol sejauh mana panjang dokumen dinormalisasi. Nilai antara 0 dan 1 biasanya digunakan, dengan nilai default 0,75. Nilai 0 berarti tidak ada normalisasi panjang, sedangkan nilai 1 berarti normalisasi panjang penuh.

Buat koleksi

Sekarang buat koleksi menggunakan skema dan parameter indeks yang telah ditentukan.

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
}"

Masukkan data teks

Setelah menyiapkan koleksi dan indeks, Anda siap untuk menyisipkan data teks. Dalam proses ini, Anda hanya perlu menyediakan teks mentah. Fungsi bawaan yang telah kita tentukan sebelumnya secara otomatis menghasilkan vektor spars yang sesuai untuk setiap entri teks.

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"
}'

Setelah Anda menyisipkan data ke dalam koleksi, Anda dapat melakukan pencarian teks lengkap menggunakan kueri teks mentah. Milvus secara otomatis mengubah kueri Anda menjadi vektor spars dan menentukan peringkat hasil pencarian yang cocok menggunakan algoritma BM25, lalu mengembalikan hasil topK (limit).

Anda dapat menyorot istilah yang cocok dalam hasil pencarian dengan mengonfigurasi penyorot teks. Lihat Penyorot Teks untuk detailnya.

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

Deskripsi

search_params

Sebuah kamus yang berisi parameter pencarian.

params.drop_ratio_search

Proporsi istilah dengan tingkat kepentingan rendah yang akan diabaikan selama pencarian. Nilai harus berada dalam rentang [0,0; 1,0). Untuk detailnya, lihat Vektor Jarang.

data

Teks kueri mentah dalam bahasa alami. Milvus secara otomatis mengubah kueri teks Anda menjadi vektor spars menggunakan fungsi BM25 — jangan berikan vektor yang telah dihitung sebelumnya.

anns_field

Nama bidang yang berisi vektor spars yang dihasilkan secara internal.

output_fields

Daftar nama bidang yang akan ditampilkan dalam hasil pencarian. Mendukung semua bidang kecuali bidang vektor langka yang berisi embedding yang dihasilkan oleh BM25. Bidang keluaran umum meliputi bidang kunci utama (misalnya, id) dan bidang teks asli (misalnya, text). Untuk informasi lebih lanjut, lihat FAQ.

limit

Jumlah maksimum hasil pencocokan teratas yang akan ditampilkan.

FAQ

Tidak, vektor spars yang dihasilkan oleh fungsi BM25 tidak dapat diakses atau ditampilkan secara langsung dalam pencarian teks lengkap. Berikut rinciannya:

  • Fungsi BM25 menghasilkan vektor spars secara internal untuk pemeringkatan dan pengambilan hasil

  • Vektor-vektor ini disimpan dalam bidang sparse tetapi tidak dapat dimasukkan ke dalam output_fields

  • Anda hanya dapat menampilkan bidang teks asli dan metadata (seperti id, text)

Contoh:

# ❌ 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
)

Mengapa saya perlu mendefinisikan bidang vektor spars jika saya tidak dapat mengaksesnya?

Bidang vektor sparsitas berfungsi sebagai indeks pencarian internal, mirip dengan indeks basis data yang tidak berinteraksi langsung dengan pengguna.

Alasan Desain:

  • Pemisahan Tanggung Jawab: Anda bekerja dengan teks (input/output), Milvus menangani vektor (pemrosesan internal)

  • Kinerja: Vektor langka yang dihitung sebelumnya memungkinkan pemeringkatan BM25 yang cepat selama kueri

  • Pengalaman Pengguna: Menyembunyikan operasi vektor yang kompleks di balik antarmuka teks yang sederhana

Jika Anda memerlukan akses ke vektor:

  • Gunakan operasi vektor spars yang dilakukan secara manual, bukan pencarian teks lengkap

  • Buat koleksi terpisah untuk alur kerja vektor langka khusus

Untuk detailnya, lihat Vektor Jarang.