多元索引支援基於非主鍵列和多個查詢條件檢索資料。本文以商品資料為例,介紹如何通過 Tablestore Java SDK 建立資料表和多元索引、寫入資料並完成一次精確查詢。
準備工作
已開通 Tablestore 服務並建立執行個體。具體操作,請參見開通服務和建立執行個體。
已安裝 Tablestore Java SDK、初始化用戶端並獲得
SyncClient執行個體。具體操作,請參見Java SDK。
操作步驟
本樣本建立名稱為 example_table 的資料表和名稱為 example_index 的多元索引。索引就緒後,寫入 3 條商品資料,再查詢 category 列值為 books 的商品。
將各步驟的樣本方法添加到已初始化用戶端的 Java 程式中,然後按以下順序調用:
private static void runQuickStart(SyncClient client)
throws InterruptedException {
String tableName = "example_table";
String indexName = "example_index";
createTable(client, tableName);
createSearchIndex(client, tableName, indexName);
waitUntilSearchIndexIsReady(client, tableName, indexName);
putSampleRows(client, tableName);
SearchResponse response =
waitUntilBooksAreQueryable(client, tableName, indexName);
System.out.println("Total count: " + response.getTotalCount());
System.out.println("Rows: " + response.getRows());
}
步驟一:建立資料表
多元索引要求資料表的最大版本數為 1,並且資料生命週期為 -1(資料永不到期)或禁止更新資料。本樣本將最大版本數設定為 1,將資料生命週期設定為 -1。
private static void createTable(
SyncClient client, String tableName) {
TableMeta tableMeta = new TableMeta(tableName);
tableMeta.addPrimaryKeyColumn(
new PrimaryKeySchema(
"product_id", PrimaryKeyType.STRING));
TableOptions tableOptions = new TableOptions();
tableOptions.setTimeToLive(-1);
tableOptions.setMaxVersions(1);
client.createTable(
new CreateTableRequest(tableMeta, tableOptions));
}
步驟二:建立多元索引
為 category 和 price 屬性列建立索引欄位。索引欄位的名稱和資料類型必須與資料表中對應屬性列保持一致。
private static void createSearchIndex(
SyncClient client, String tableName, String indexName) {
IndexSchema indexSchema = new IndexSchema();
indexSchema.setFieldSchemas(Arrays.asList(
new FieldSchema("category", FieldType.KEYWORD),
new FieldSchema("price", FieldType.LONG)));
CreateSearchIndexRequest request =
new CreateSearchIndexRequest();
request.setTableName(tableName);
request.setIndexName(indexName);
request.setIndexSchema(indexSchema);
client.createSearchIndex(request);
}
建立多元索引為非同步作業。等待索引狀態變為 RUNNING 且同步階段變為 INCR 後,再查詢資料。
private static final int RETRY_ATTEMPTS = 90;
private static final long RETRY_INTERVAL_MILLIS = 1_000L;
private static void waitUntilSearchIndexIsReady(
SyncClient client, String tableName, String indexName)
throws InterruptedException {
for (int attempt = 1; attempt <= RETRY_ATTEMPTS; attempt++) {
DescribeSearchIndexRequest request =
new DescribeSearchIndexRequest();
request.setTableName(tableName);
request.setIndexName(indexName);
request.setIncludeSyncStat(true);
DescribeSearchIndexResponse response =
client.describeSearchIndex(request);
if (response.getIndexStatus() != null
&& response.getIndexStatus().indexStatusEnum
== DescribeSearchIndexResponse.IndexStatusEnum.RUNNING
&& response.getSyncStat() != null
&& response.getSyncStat().getSyncPhase()
== SyncStat.SyncPhase.INCR) {
return;
}
Thread.sleep(RETRY_INTERVAL_MILLIS);
}
throw new IllegalStateException(
"Search index did not become ready before timeout.");
}
步驟三:寫入樣本資料
寫入 3 條商品資料。其中,product_id 為主鍵列,category 和 price 為屬性列。
private static void putSampleRows(
SyncClient client, String tableName) {
putProduct(client, tableName, "product-001", "books", 89L);
putProduct(client, tableName, "product-002", "books", 129L);
putProduct(client, tableName, "product-003", "devices", 599L);
}
private static void putProduct(
SyncClient client,
String tableName,
String productId,
String category,
long price) {
PrimaryKey primaryKey =
PrimaryKeyBuilder.createPrimaryKeyBuilder()
.addPrimaryKeyColumn(
"product_id",
PrimaryKeyValue.fromString(productId))
.build();
RowPutChange rowPutChange =
new RowPutChange(tableName, primaryKey);
rowPutChange.addColumn(
"category", ColumnValue.fromString(category));
rowPutChange.addColumn(
"price", ColumnValue.fromLong(price));
client.putRow(new PutRowRequest(rowPutChange));
}
步驟四:查詢資料
使用精確查詢匹配 category 列值為 books 的商品。由於資料同步到多元索引需要一定時間,樣本會在結果未就緒時重試查詢。
private static SearchResponse waitUntilBooksAreQueryable(
SyncClient client, String tableName, String indexName)
throws InterruptedException {
for (int attempt = 1; attempt <= RETRY_ATTEMPTS; attempt++) {
SearchResponse response =
queryBooks(client, tableName, indexName);
if (response.isAllSuccess()
&& response.getTotalCount() == 2L
&& response.getRows().size() == 2) {
return response;
}
Thread.sleep(RETRY_INTERVAL_MILLIS);
}
throw new IllegalStateException(
"Sample rows were not queryable before timeout.");
}
private static SearchResponse queryBooks(
SyncClient client, String tableName, String indexName) {
TermQuery termQuery = new TermQuery();
termQuery.setFieldName("category");
termQuery.setTerm(ColumnValue.fromString("books"));
SearchQuery searchQuery = new SearchQuery();
searchQuery.setQuery(termQuery);
searchQuery.setLimit(10);
searchQuery.setTrackTotalCount(
SearchQuery.TRACK_TOTAL_COUNT);
SearchRequest.ColumnsToGet columnsToGet =
new SearchRequest.ColumnsToGet();
columnsToGet.setReturnAll(true);
SearchRequest request =
new SearchRequest(tableName, indexName, searchQuery);
request.setColumnsToGet(columnsToGet);
return client.search(request);
}
程式返回的總行數和結果行數均為 2,表示查詢成功。
清理資源
如果不再需要樣本資源,請先刪除多元索引,再刪除資料表。
DeleteSearchIndexRequest deleteIndexRequest =
new DeleteSearchIndexRequest();
deleteIndexRequest.setTableName("example_table");
deleteIndexRequest.setIndexName("example_index");
client.deleteSearchIndex(deleteIndexRequest);
client.deleteTable(new DeleteTableRequest("example_table"));
client.shutdown();