對于通配符查詢(WildcardQuery)中查詢模式為*word*
的場景,您可以使用基于分詞的通配符查詢方式(即模糊分詞和短語匹配查詢組合使用)來實現性能更好的模糊查詢。
背景信息
模糊查詢是數據庫業務中常見的需求,例如查詢文件名、手機號碼等。在表格存儲中要實現模糊查詢,通常使用多元索引的通配符查詢來實現類似于MySQL中的like功能,但是通配符查詢存在查詢詞長度限制(最長32個字符)以及性能會隨著數據量增長而下降的限制。
為了解決通配符查詢存在的問題,多元索引支持使用基于分詞的通配符查詢方式來實現性能更好的模糊查詢。當使用基于分詞的通配符查詢方式時,查詢詞長度無限制,但是原文內容會限制最大1024字符或者漢字,超過后會截斷,只保留前1024個字符或者漢字。
適用場景
請根據查詢場景選擇合適的方式實現模糊查詢。
對于通配符查詢中查詢模式為
*word*
的場景,例如通過"123"
匹配手機號碼中任意位置包含123
的號碼,請使用基于分詞的通配符查詢來實現模糊查詢。在此場景中,大部分情況下使用基于分詞的通配符查詢方式會比使用通配符查詢有10倍以上的性能提升。
假設數據表中包含file_name列,該列在多元索引中的字段類型為Text且分詞類型為模糊分詞(Fuzzy_Analyzer)。如果使用多元索引查詢需要查詢到file_name列值為
2021 woRK@杭州
的行,則查詢時必須使用短語匹配查詢(MatchPhraseQuery),并設置查詢詞為位置連續的子字符串。如果查詢詞為
2021
、20
、21
、work
、WORK
、@
、杭
、州
、杭州
、@杭州
中的任意一個,則可以匹配到file_name列值為2021 woRK@杭州
的行。如果查詢詞為
21work
、2021杭州
、2120
、#杭州
中的任意一個,則無法匹配到file_name列值為2021 woRK@杭州
的行。
對于其他復雜查詢場景,請使用通配符查詢方式來實現模糊查詢。更多信息,請參見通配符查詢。
使用方式
使用基于分詞的通配符查詢方式實現模糊查詢的具體步驟如下:
創建多元索引時,指定列類型為Text且分詞類型為模糊分詞(Fuzzy Analyzer),其他參數保持默認配置即可。具體操作,請參見創建多元索引。
說明如果已創建多元索引,您可以通過動態修改schema功能為指定列添加虛擬列,同時設置虛擬列為Text類型且分詞類型為模糊分詞來實現。具體操作,請分別參見動態修改schema和虛擬列。
使用多元索引查詢數據時,使用MatchPhraseQuery。具體操作,請參見短語匹配查詢。
附錄:測試用例
以下示例通過測試用例的方式展示了使用基于分詞的通配符查詢方式實現模糊查詢的效果。
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 {
// 定義表schema。
IndexSchema indexSchema = new IndexSchema();
indexSchema.setFieldSchemas(Collections.singletonList(
// 注意:當原來查詢的name字段為Keyword類型時,如果修改該字段為Text類型并為該字段設置分詞后,查詢可能會出現異常。
// 如果需要同時保留Keyword和Text類型,請參見“虛擬列”功能的示例。假如使用name字段只需要完成匹配*abc*的查詢功能,則只用Text類型的字段即可,無需Keyword類型。
new FieldSchema("name", FieldType.TEXT).setAnalyzer(FieldSchema.Analyzer.Fuzzy)
));
// 創建多元索引。
{
CreateSearchIndexRequest request = new CreateSearchIndexRequest();
request.setTableName(tableName);
request.setIndexName(indexName);
request.setIndexSchema(indexSchema);
CreateSearchIndexResponse response = client.createSearchIndex(request);
}
// 寫入一行數據。
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);
// 寫入屬性列。
rowPutChange.addColumn("name", ColumnValue.fromString("調音師1024x768P.mp4"));
PutRowRequest request = new PutRowRequest(rowPutChange);
client.putRow(request);
// 等待多元索引中同步完成一條數據。
Thread.sleep(1000 * 180);
// 匹配*abc*的查詢功能場景展示。
assertMatchPhraseQuery(client, tableName, indexName, "name", "調", 1);
assertMatchPhraseQuery(client, tableName, indexName, "name", "調音", 1);
assertMatchPhraseQuery(client, tableName, indexName, "name", "調 音", 0);
assertMatchPhraseQuery(client, tableName, indexName, "name", "調音師102", 1);
assertMatchPhraseQuery(client, tableName, indexName, "name", "調音師1024", 1);
assertMatchPhraseQuery(client, tableName, indexName, "name", "調音師1024x", 1);
assertMatchPhraseQuery(client, tableName, indexName, "name", "調音師1024x7", 1);
assertMatchPhraseQuery(client, tableName, indexName, "name", "調音師1024x768P.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);
}
// 使用虛擬列。
public void testFuzzyMatchPhraseWithVirtualField(SyncClient client) throws Exception {
// 定義表schema。
IndexSchema indexSchema = new IndexSchema();
indexSchema.setFieldSchemas(Arrays.asList(
// 原始字段為Keyword類型,方便進行等值查詢。
new FieldSchema("name", FieldType.KEYWORD).setIndex(true).setStore(true),
// 創建一個虛擬列“name_virtual_text”,同時設置虛擬列為Text類型且分詞類型為Fuzzy。該虛擬列的來源為“name”字段。
new FieldSchema("name_virtual_text", FieldType.TEXT).setIndex(true).setAnalyzer(FieldSchema.Analyzer.Fuzzy).setVirtualField(true).setSourceFieldName("name")
));
// 創建多元索引。
{
CreateSearchIndexRequest request = new CreateSearchIndexRequest();
request.setTableName(tableName);
request.setIndexName(indexName);
request.setIndexSchema(indexSchema);
CreateSearchIndexResponse response = client.createSearchIndex(request);
}
// 寫入一行數據。
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);
// 寫入屬性列。
rowPutChange.addColumn("name", ColumnValue.fromString("調音師1024x768P.mp4"));
PutRowRequest request = new PutRowRequest(rowPutChange);
client.putRow(request);
// 等待多元索引中同步完成一條數據。
Thread.sleep(1000 * 180);
// 配置*abc*的查詢場景展示。
// 請注意查詢字段為虛擬列“name_virtual_text”,而不是“name”。
assertMatchPhraseQuery(client, tableName, indexName, "name_virtual_text", "調", 1);
assertMatchPhraseQuery(client, tableName, indexName, "name_virtual_text", "調音", 1);
assertMatchPhraseQuery(client, tableName, indexName, "name_virtual_text", "調 音", 0);
assertMatchPhraseQuery(client, tableName, indexName, "name_virtual_text", "調音師102", 1);
assertMatchPhraseQuery(client, tableName, indexName, "name_virtual_text", "調音師1024", 1);
assertMatchPhraseQuery(client, tableName, indexName, "name_virtual_text", "調音師1024x", 1);
assertMatchPhraseQuery(client, tableName, indexName, "name_virtual_text", "調音師1024x7", 1);
assertMatchPhraseQuery(client, tableName, indexName, "name_virtual_text", "調音師1024x768P.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);
}
// 展示MatchPhraseQuery如何實現。
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();
// 使用MatchPhraseQuery查詢分詞字段。
searchQuery.setQuery(QueryBuilders.matchPhrase(fieldName, searchContent).build());
searchQuery.setLimit(0);
// 為了展示功能需要,此處設置返回匹配總行數。如果不需要關心匹配總行數,請設置為false,來實現更高性能。
searchQuery.setGetTotalCount(true);
searchRequest.setSearchQuery(searchQuery);
SearchResponse response = client.search(searchRequest);
assertEquals(String.format("field:[%s], searchContent:[%s]", fieldName, searchContent), exceptCount, response.getTotalCount());
}
}
常見問題
相關文檔
多元索引查詢類型包括精確查詢、多詞精確查詢、全匹配查詢、匹配查詢、短語匹配查詢、前綴查詢、范圍查詢、通配符查詢、基于分詞的通配符查詢、多條件組合查詢、地理位置查詢、嵌套類型查詢、向量檢索介紹與使用和列存在性查詢,您可以選擇合適的查詢類型進行多維度數據查詢。
如果要對結果集進行排序或者翻頁,您可以使用排序和翻頁功能來實現。具體操作,請參見排序和翻頁。
如果要按照某一列對結果集做折疊,使對應類型的數據在結果展示中只出現一次,您可以使用折疊(去重)功能來實現。具體操作,請參見折疊(去重)。
如果要進行數據分析,例如求最值、求和、統計行數等,您可以使用Search接口的統計聚合功能或者SQL查詢來實現。具體操作,請參見統計聚合和SQL查詢。
如果要快速導出數據,而不關心整個結果集的順序時,您可以使用ParallelScan接口和ComputeSplits接口實現多并發導出數據。具體操作,請參見并發導出數據。