全部产品
Search
文档中心

Tablestore:Kueri wildcard berbasis tokenisasi

更新时间:Jul 06, 2025

Untuk mencari string menggunakan pola *word*, Anda dapat memanfaatkan kueri wildcard berbasis tokenisasi (kombinasi tokenisasi fuzzy dengan kueri frasa cocok) guna meningkatkan performa pencarian.

Informasi latar belakang

Kueri fuzzy merupakan kebutuhan umum dalam database. Sebagai contoh, Anda dapat melakukan kueri fuzzy untuk menanyakan nama file atau nomor ponsel. Untuk menjalankan kueri fuzzy di Tablestore, Anda dapat menggunakan fitur kueri wildcard dari indeks pencarian. Fitur ini mirip dengan operator LIKE di MySQL. Namun, fitur kueri wildcard hanya mendukung hingga 32 karakter dalam string yang digunakan untuk pencarian, dan performanya menurun seiring dengan bertambahnya ukuran data.

Untuk mengatasi masalah ini, indeks pencarian mendukung kueri wildcard berbasis tokenisasi untuk memastikan performa tinggi dalam pencarian fuzzy. Saat menggunakan kueri wildcard berbasis tokenisasi, Tablestore tidak membatasi panjang string yang digunakan untuk pencarian. Namun, jika nilai kolom melebihi 1.024 karakter, sistem akan memotong nilai tersebut dan hanya melakukan tokenisasi pada 1.024 karakter pertama.

Skenario

Pilih metode yang sesuai dengan skenario Anda untuk menjalankan kueri fuzzy.

  • Jika Anda menggunakan *word* untuk kueri wildcard, Anda dapat menggunakan kueri wildcard berbasis tokenisasi untuk menjalankan pencarian fuzzy. Contohnya, jika Anda menggunakan "123" untuk menanyakan nomor ponsel yang mengandung 123 di posisi mana pun, Anda dapat memanfaatkan kueri wildcard berbasis tokenisasi untuk pencarian fuzzy.

    Dalam kasus ini, kueri wildcard berbasis tokenisasi meningkatkan performa pencarian lebih dari 10 kali lipat dibandingkan dengan kueri wildcard biasa.

    Sebagai contoh, tabel data berisi kolom bernama file_name dengan tipe kolom Text, dan metode tokenisasi adalah tokenisasi fuzzy (FuzzyAnalyzer) untuk kolom tersebut dalam indeks pencarian. Jika Anda menggunakan indeks pencarian untuk menanyakan baris di mana nilai kolom file_name adalah 2021 woRK@Hangzhou, Anda harus menjalankan kueri frasa cocok (MatchPhraseQuery) dan menetapkan token menjadi substring berturut-turut untuk pencarian tersebut.

    • Jika token untuk pencarian adalah 2021, 20, 21, work, WORK, @, Hang, zhou, Hangzhou, atau @Hangzhou, baris di mana nilai kolom file_name adalah dapat cocok dengan token.

    • Namun, jika token untuk pencarian adalah 21work, 2021Hangzhou, 2120, atau #Hangzhou, baris di mana nilai kolom file_name adalah 2021 woRK@Hangzhou tidak akan cocok dengan token.

  • Untuk kueri kompleks lainnya, Anda dapat menggunakan kueri wildcard untuk pencarian fuzzy. Untuk informasi lebih lanjut, lihat Kueri Wildcard.

Gunakan tokenisasi fuzzy untuk kueri fuzzy

Untuk menggunakan kueri wildcard berbasis tokenisasi dalam pencarian fuzzy, ikuti langkah-langkah berikut:

  1. Buat indeks pencarian. Saat membuat indeks pencarian, tetapkan tipe bidang ke Text dan metode tokenisasi ke tokenisasi fuzzy (FuzzyAnalyzer) untuk kolom tertentu, serta pertahankan pengaturan default untuk parameter lainnya. Untuk informasi lebih lanjut, lihat Buat Indeks Pencarian.

    Catatan

    Jika indeks pencarian sudah ada, Anda dapat menambahkan kolom virtual untuk kolom tertentu dengan secara dinamis memodifikasi skema indeks pencarian. Kemudian, tetapkan tipe bidang ke Text dan metode tokenisasi ke tokenisasi fuzzy untuk kolom virtual tersebut. Untuk informasi lebih lanjut, lihat Modifikasi Dinamis Skema Indeks Pencarian dan Kolom Virtual.

  2. Gunakan indeks pencarian untuk menanyakan data. Saat menggunakan indeks pencarian, jalankan kueri frasa cocok. Untuk informasi lebih lanjut, lihat Kueri Frasa Cocok.

Lampiran: kasus uji

Berikut adalah contoh kasus uji yang menunjukkan cara menggunakan kueri wildcard berbasis tokenisasi untuk pencarian fuzzy:

import com.alicloud.openservices.tablestore.SyncClient;
import com.alicloud.openservices.tablestore.model.ColumnValue;
import com.alicloud.openservices.tablestore.model.PrimaryKey;
import com.alicloud.openservices.tablestore.model.PrimaryKeyBuilder;
import com.alicloud.openservices.tablestore.model.PrimaryKeyValue;
import com.alicloud.openservices.tablestore.model.PutRowRequest;
import com.alicloud.openservices.tablestore.model.RowPutChange;
import com.alicloud.openservices.tablestore.model.search.CreateSearchIndexRequest;
import com.alicloud.openservices.tablestore.model.search.CreateSearchIndexResponse;
import com.alicloud.openservices.tablestore.model.search.FieldSchema;
import com.alicloud.openservices.tablestore.model.search.FieldType;
import com.alicloud.openservices.tablestore.model.search.IndexSchema;
import com.alicloud.openservices.tablestore.model.search.SearchQuery;
import com.alicloud.openservices.tablestore.model.search.SearchRequest;
import com.alicloud.openservices.tablestore.model.search.SearchResponse;
import com.alicloud.openservices.tablestore.model.search.query.QueryBuilders;

import java.util.Arrays;
import java.util.Collections;

import static org.junit.Assert.assertEquals;


public class TestFuzzy {
    private static final String tableName = "analysis_test";
    private static final String indexName = "analysis_test_index";


    public void testFuzzyMatchPhrase(SyncClient client) throws Exception {
        // Tentukan skema indeks pencarian.
        IndexSchema indexSchema = new IndexSchema();
        indexSchema.setFieldSchemas(Collections.singletonList(
                // Catatan: Jika Anda menentukan Text sebagai tipe field name yang dipetakan ke kolom name bertipe Keyword dalam tabel data dan menetapkan metode tokenisasi untuk field tersebut, pengecualian mungkin terjadi dalam kueri.
                // Jika Anda ingin mempertahankan kedua tipe Keyword dan Text, lihat contoh yang diberikan dalam topik "Kolom virtual". Jika Anda menggunakan *abc* untuk mencocokkan field name, hanya field name bertipe Text yang diperlukan. Field name bertipe Keyword tidak diperlukan.
                new FieldSchema("name", FieldType.TEXT).setAnalyzer(FieldSchema.Analyzer.Fuzzy)
        ));
        // Buat indeks pencarian.
        {
            CreateSearchIndexRequest request = new CreateSearchIndexRequest();
            request.setTableName(tableName);
            request.setIndexName(indexName);
            request.setIndexSchema(indexSchema);
            CreateSearchIndexResponse response = client.createSearchIndex(request);
        }

        // Tulis satu baris data ke tabel data.
        PrimaryKey primaryKey = PrimaryKeyBuilder.createPrimaryKeyBuilder()
                .addPrimaryKeyColumn("pk1", PrimaryKeyValue.fromString("1"))
                .addPrimaryKeyColumn("pk2", PrimaryKeyValue.fromLong(1))
                .addPrimaryKeyColumn("pk3", PrimaryKeyValue.fromBinary(new byte[]{1, 2, 3}))
                .build();
        RowPutChange rowPutChange = new RowPutChange(tableName, primaryKey);
        // Tentukan kolom atribut dari baris.
        rowPutChange.addColumn("name", ColumnValue.fromString("TheLionKing1024x768P.mp4"));
        PutRowRequest request = new PutRowRequest(rowPutChange);
        client.putRow(request);

        // Tunggu hingga baris data disinkronkan ke indeks pencarian.
        Thread.sleep(1000 * 180);

        // Gunakan *abc* untuk kueri.
        assertMatchPhraseQuery(client, tableName, indexName, "name", "The", 1);
        assertMatchPhraseQuery(client, tableName, indexName, "name", "TheLion", 1);
        assertMatchPhraseQuery(client, tableName, indexName, "name", "The Lion", 0);
        assertMatchPhraseQuery(client, tableName, indexName, "name", "TheLionKing102", 1);
        assertMatchPhraseQuery(client, tableName, indexName, "name", "TheLionKing1024", 1);
        assertMatchPhraseQuery(client, tableName, indexName, "name", "TheLionKing1024x", 1);
        assertMatchPhraseQuery(client, tableName, indexName, "name", "TheLionKing1024x7", 1);
        assertMatchPhraseQuery(client, tableName, indexName, "name", "TheLionKing1024x768P.mp4", 1);
        assertMatchPhraseQuery(client, tableName, indexName, "name", "24x768P.mp4", 1);
        assertMatchPhraseQuery(client, tableName, indexName, "name", "24x76 8P.mp4", 0);
        assertMatchPhraseQuery(client, tableName, indexName, "name", "24x7 P.mp4", 0);
    }

    // Gunakan kolom virtual.
    public void testFuzzyMatchPhraseWithVirtualField(SyncClient client) throws Exception {
        // Tentukan skema indeks pencarian.
        IndexSchema indexSchema = new IndexSchema();
        indexSchema.setFieldSchemas(Arrays.asList(
                // Tetapkan tipe field name ke Keyword, yang memfasilitasi kueri ekuivalen.
                new FieldSchema("name", FieldType.KEYWORD).setIndex(true).setStore(true),
                // Buat kolom virtual bernama name_virtual_text dan tetapkan tipe bidang ke Text dan metode tokenisasi ke Fuzzy untuk kolom virtual tersebut. Sumber data kolom virtual adalah field name.
                new FieldSchema("name_virtual_text", FieldType.TEXT).setIndex(true).setAnalyzer(FieldSchema.Analyzer.Fuzzy).setVirtualField(true).setSourceFieldName("name")
        ));
        // Buat indeks pencarian.
        {
            CreateSearchIndexRequest request = new CreateSearchIndexRequest();
            request.setTableName(tableName);
            request.setIndexName(indexName);
            request.setIndexSchema(indexSchema);
            CreateSearchIndexResponse response = client.createSearchIndex(request);
        }

        // Tulis satu baris data ke tabel data.
        PrimaryKey primaryKey = PrimaryKeyBuilder.createPrimaryKeyBuilder()
                .addPrimaryKeyColumn("pk1", PrimaryKeyValue.fromString("1"))
                .addPrimaryKeyColumn("pk2", PrimaryKeyValue.fromLong(1))
                .addPrimaryKeyColumn("pk3", PrimaryKeyValue.fromBinary(new byte[]{1, 2, 3}))
                .build();
        RowPutChange rowPutChange = new RowPutChange(tableName, primaryKey);
        // Tentukan kolom atribut dari baris.
        rowPutChange.addColumn("name", ColumnValue.fromString("TheLionKing1024x768P.mp4"));
        PutRowRequest request = new PutRowRequest(rowPutChange);
        client.putRow(request);

        // Tunggu hingga baris data disinkronkan ke indeks pencarian.
        Thread.sleep(1000 * 180);

        // Gunakan *abc* untuk kueri.
        // Catatan: Field untuk kueri adalah name_virtual_text bukan name.
        assertMatchPhraseQuery(client, tableName, indexName, "name_virtual_text", "The", 1);
        assertMatchPhraseQuery(client, tableName, indexName, "name_virtual_text", "TheLion", 1);
        assertMatchPhraseQuery(client, tableName, indexName, "name_virtual_text", "The Lion", 0);
        assertMatchPhraseQuery(client, tableName, indexName, "name_virtual_text", "TheLionKing102", 1);
        assertMatchPhraseQuery(client, tableName, indexName, "name_virtual_text", "TheLionKing1024", 1);
        assertMatchPhraseQuery(client, tableName, indexName, "name_virtual_text", "TheLionKing1024x", 1);
        assertMatchPhraseQuery(client, tableName, indexName, "name_virtual_text", "TheLionKing1024x7", 1);
        assertMatchPhraseQuery(client, tableName, indexName, "name_virtual_text", "TheLionKing1024x768P.mp4", 1);
        assertMatchPhraseQuery(client, tableName, indexName, "name_virtual_text", "24x768P.mp4", 1);
        assertMatchPhraseQuery(client, tableName, indexName, "name_virtual_text", "24x76 8P.mp4", 0);
        assertMatchPhraseQuery(client, tableName, indexName, "name_virtual_text", "24x7 P.mp4", 0);
    }

    // Lakukan kueri frasa cocok.
    public static void assertMatchPhraseQuery(SyncClient client, String tableName, String indexName, String fieldName, String searchContent, long exceptCount) {
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.setTableName(tableName);
        searchRequest.setIndexName(indexName);
        SearchQuery searchQuery = new SearchQuery();
        // Lakukan kueri frasa cocok untuk menanyakan data yang cocok dengan token.
        searchQuery.setQuery(QueryBuilders.matchPhrase(fieldName, searchContent).build());
        searchQuery.setLimit(0);
        // Tentukan bahwa jumlah total baris yang cocok dikembalikan. Jika Anda tidak peduli dengan jumlah total baris yang cocok, atur parameter ini ke false untuk performa yang lebih baik.
        searchQuery.setGetTotalCount(true);
        searchRequest.setSearchQuery(searchQuery);
        SearchResponse response = client.search(searchRequest);
        assertEquals(String.format("field:[%s], searchContent:[%s]", fieldName, searchContent), exceptCount, response.getTotalCount());
    }
}

FAQ

Referensi

  • Saat menggunakan indeks pencarian untuk menanyakan data, Anda dapat memilih dari metode kueri berikut: kueri tepat, kueri terms, kueri match all, kueri match, kueri frasa cocok, kueri prefix, kueri rentang, kueri wildcard, kueri fuzzy, kueri Boolean, kueri geo, kueri nested, kueri vektor KNN, dan kueri exists. Pilih metode kueri berdasarkan kebutuhan bisnis Anda untuk menanyakan data dari berbagai dimensi.

    Anda dapat mengurutkan atau membagi halaman baris yang memenuhi kondisi kueri menggunakan fitur pengurutan dan paging. Untuk informasi lebih lanjut, lihat Melakukan Pengurutan dan Paging.

    Anda juga dapat menggunakan fitur collapse (distinct) untuk merangkum set hasil berdasarkan kolom tertentu. Dengan cara ini, data dari tipe yang ditentukan hanya muncul sekali dalam hasil kueri. Untuk informasi lebih lanjut, lihat Collapse (Distinct).

  • Jika Anda ingin menganalisis data dalam tabel data, Anda dapat menggunakan fitur agregasi dari operasi Search atau menjalankan pernyataan SQL. Contohnya, Anda dapat memperoleh nilai minimum dan maksimum, jumlah, serta total jumlah baris. Untuk informasi lebih lanjut, lihat Agregasi dan Kueri SQL.

  • Jika Anda ingin memperoleh semua baris yang memenuhi kondisi kueri tanpa perlu mengurutkan baris, Anda dapat memanggil operasi ParallelScan dan ComputeSplits untuk menggunakan fitur pemindaian paralel. Untuk informasi lebih lanjut, lihat Pemindaian Paralel.