Pencocokan Teks

Fitur Pencocokan Teks di Milvus memungkinkan pencarian dokumen yang akurat berdasarkan istilah tertentu. Fitur ini terutama digunakan untuk pencarian yang difilter guna memenuhi kondisi tertentu dan dapat menggabungkan penyaringan skalar untuk menyempurnakan hasil kueri, sehingga memungkinkan pencarian kesamaan dalam vektor yang memenuhi kriteria skalar.

TEXT_MATCH menemukan istilah yang dianalisis secara tepat, sedangkan Pencarian dengan Jarak Pengeditan ( TEXT_MATCH_FUZZY ) dapat mentoleransi jarak pengeditan kecil antara token kueri dan token yang diindeks. Keduanya merupakan operasi penyaringan Boolean dan tidak menilai relevansi dokumen yang cocok. Jika Anda ingin mengambil dokumen yang paling relevan berdasarkan makna semantik dan pentingnya istilah kueri, kami menyarankan Anda menggunakan Pencarian Teks Lengkap (Full Text Search).

Gambaran Umum

Milvus mengintegrasikan Tantivy untuk mendukung indeks terbalik dan pencarian teks berbasis istilah yang mendasarinya. Untuk setiap entri teks, Milvus mengindeksnya dengan mengikuti prosedur berikut:

  1. Penganalisis: Penganalisis memproses teks masukan dengan memecahnya menjadi kata-kata individual, atau token, lalu menerapkan filter sesuai kebutuhan. Hal ini memungkinkan Milvus membangun indeks berdasarkan token-token tersebut.

  2. Pengindeksan: Setelah analisis teks, Milvus membuat indeks terbalik yang memetakan setiap token unik ke dokumen yang mengandungnya.

Saat pengguna melakukan pencocokan teks, indeks terbalik digunakan untuk dengan cepat mengambil semua dokumen yang mengandung istilah tersebut. Proses ini jauh lebih cepat daripada memindai setiap dokumen satu per satu.

Keyword Match Pencocokan Kata Kunci

Aktifkan pencocokan teks

Pencocokan teks berfungsi pada bidang string yang diaktifkan untuk pencocokan. Contoh-contoh di halaman ini menggunakan VARCHAR, yang didukung di seluruh SDK klien. Di Milvus 3.0.x, TEXT bidang juga mendukung pencocokan teks saat Storage V3 diaktifkan. Untuk kedua jenis bidang tersebut, atur enable_analyzer dan enable_match menjadi True, lalu konfigurasikan penganalisis secara opsional saat mendefinisikan skema koleksi Anda.

Atur ` enable_analyzer ` dan enable_match

Untuk mengaktifkan pencocokan teks pada bidang VARCHAR tertentu, atur parameter enable_analyzer dan enable_match menjadi True saat mendefinisikan skema bidang. Hal ini menginstruksikan Milvus untuk menokenisasi teks dan membuat indeks terbalik untuk bidang yang ditentukan, sehingga memungkinkan pencocokan teks yang cepat dan efisien.

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

Opsional: Konfigurasikan penganalisis

Kinerja dan akurasi pencocokan kata kunci bergantung pada penganalisis yang dipilih. Penganalisis yang berbeda dirancang khusus untuk berbagai bahasa dan struktur teks, sehingga memilih yang tepat dapat secara signifikan memengaruhi hasil pencarian untuk kasus penggunaan spesifik Anda.

Secara default, Milvus menggunakan penganalisis ` standard `, yang menokenisasi teks berdasarkan spasi dan tanda baca, menghapus token yang lebih panjang dari 40 karakter, serta mengubah teks menjadi huruf kecil. Tidak diperlukan parameter tambahan untuk menerapkan pengaturan default ini. Untuk informasi lebih lanjut, lihat Standard.

Jika diperlukan penganalisis yang berbeda, Anda dapat mengonfigurasinya menggunakan parameter ` analyzer_params `. Misalnya, untuk menerapkan penganalisis ` english ` guna memproses teks bahasa Inggris:

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 juga menyediakan berbagai penganalisis lain yang sesuai untuk berbagai bahasa dan skenario. Untuk detail lebih lanjut, lihat Ikhtisar Penganalisis.

Gunakan pencocokan teks

Setelah Anda mengaktifkan pencocokan teks untuk bidang ` VARCHAR ` atau ` TEXT ` dalam skema koleksi Anda, Anda dapat melakukan pencocokan teks menggunakan ekspresi ` TEXT_MATCH `.

Sintaks ekspresi TEXT_MATCH

Ekspresi ` TEXT_MATCH ` digunakan untuk menentukan bidang dan istilah yang akan dicari. Sintaksnya adalah sebagai berikut:

TEXT_MATCH(field_name, text)
  • field_name: Nama kolom VARCHAR atau TEXT yang mendukung pencocokan dan akan dicari.

  • text: Istilah-istilah yang akan dicari. Beberapa istilah dapat dipisahkan dengan spasi atau pemisah lain yang sesuai berdasarkan bahasa dan penganalisis yang dikonfigurasi.

Secara default, TEXT_MATCH menggunakan logika pencocokan OR, yang berarti ekspresi ini akan mengembalikan dokumen yang mengandung salah satu istilah yang ditentukan. Misalnya, untuk mencari dokumen yang mengandung istilah machine atau deep di bidang text, gunakan ekspresi berikut:

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')\""

Anda juga dapat menggabungkan beberapa ekspresi TEXT_MATCH menggunakan operator logika untuk melakukan pencocokan AND.

  • Untuk mencari dokumen yang mengandung baik machine maupun deep di bidang text, gunakan ekspresi berikut:

    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')\""
    
  • Untuk mencari dokumen yang mengandung baik machine maupun learning tetapi tanpa deep di bidang text, gunakan ekspresi berikut:

    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')\""
    

Sintaks ekspresi TEXT_MATCH_FUZZYCompatible with Milvus 3.0.0+

Gunakan TEXT_MATCH_FUZZY untuk mentoleransi perbedaan ejaan antara token kueri dan token yang diindeks. Milvus menganalisis teks kueri dengan penganalisis bidang dan menerapkan pencocokan fuzzy ke setiap token yang dihasilkan. Jika kueri menghasilkan beberapa token, ekspresi tersebut akan mencocokkan suatu entitas ketika ada token yang memenuhi jarak edit yang telah dikonfigurasi.

Sintaksnya adalah sebagai berikut:

TEXT_MATCH_FUZZY(field_name, text, max_edit_distance = 1)
  • field_name: Nama bidang ` VARCHAR ` atau ` TEXT ` yang diaktifkan untuk pencocokan, yang akan dicari.

  • text: Teks kueri yang akan dianalisis dan dicocokkan dengan token yang diindeks.

  • max_edit_distance: Jarak edit maksimum yang diizinkan untuk setiap token kueri. Nama opsi harus persis max_edit_distance, dan nilainya harus 0, 1, atau 2. Nilai 0 melakukan pencocokan token yang tepat, setara dengan TEXT_MATCH.

Misalnya, ekspresi berikut mencocokkan token yang berbeda satu karakter dari machne, termasuk 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 merupakan bagian dari sintaks ekspresi filter, sehingga SDK klien tidak memerlukan metode pencocokan kabur khusus. Masukkan ekspresi tersebut melalui parameter filter yang sama yang digunakan untuk TEXT_MATCH dalam operasi pencarian atau kueri.

Pencarian dengan pencocokan teks

Pencocokan teks dapat digunakan bersama dengan pencarian kesamaan vektor untuk mempersempit cakupan pencarian dan meningkatkan kinerja pencarian. Dengan menyaring koleksi menggunakan pencocokan teks sebelum pencarian kesamaan vektor, Anda dapat mengurangi jumlah dokumen yang perlu dicari, sehingga waktu kueri menjadi lebih cepat.

Dalam contoh ini, ekspresi ` filter ` menyaring hasil pencarian agar hanya mencakup dokumen yang cocok dengan istilah yang ditentukan, yaitu ` keyword1 ` atau ` keyword2`. Pencarian kesamaan vektor kemudian dilakukan pada subset dokumen yang telah disaring ini.

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

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

Kueri dengan pencocokan teks

Pencocokan teks juga dapat digunakan untuk penyaringan skalar dalam operasi kueri. Dengan menentukan ekspresi TEXT_MATCH pada parameter expr dari metode query(), Anda dapat mengambil dokumen yang cocok dengan istilah yang diberikan.

Contoh di bawah ini mengambil dokumen di mana bidang ` text ` berisi kedua istilah ` keyword1 ` dan ` keyword2`.

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

Pertimbangan

  • Mengaktifkan pencocokan istilah untuk suatu bidang akan memicu pembuatan indeks terbalik, yang menghabiskan sumber daya penyimpanan. Pertimbangkan dampak penyimpanan saat memutuskan untuk mengaktifkan fitur ini, karena dampaknya bervariasi tergantung pada ukuran teks, token unik, dan penganalisis yang digunakan.

  • Setelah Anda mendefinisikan penganalisis dalam skema Anda, pengaturannya menjadi permanen untuk koleksi tersebut. Jika Anda memutuskan bahwa penganalisis lain lebih sesuai dengan kebutuhan Anda, Anda dapat mempertimbangkan untuk menghapus koleksi yang ada dan membuat yang baru dengan konfigurasi penganalisis yang diinginkan.

  • Aturan escape dalam ekspresi filter:

    • Karakter yang diapit tanda kutip ganda atau tanda kutip tunggal dalam ekspresi diinterpretasikan sebagai konstanta string. Jika konstanta string tersebut mengandung karakter escape, karakter escape tersebut harus direpresentasikan dengan urutan escape. Misalnya, gunakan ` \\ ` untuk merepresentasikan ` \`, ` \\t ` untuk merepresentasikan tab ` \t`, dan ` \\n ` untuk merepresentasikan baris baru.

    • Jika konstanta string diapit oleh tanda kutip tunggal, tanda kutip tunggal di dalam konstanta tersebut harus direpresentasikan sebagai \\', sedangkan tanda kutip ganda dapat direpresentasikan sebagai " atau \\". Contoh: 'It\\'s milvus'.

    • Jika konstanta string diapit oleh tanda kutip ganda, tanda kutip ganda di dalam konstanta tersebut harus ditulis sebagai \\", sedangkan tanda kutip tunggal dapat ditulis sebagai ' atau \\'. Contoh: "He said \\"Hi\\"".