すべてのプロダクト
Search
ドキュメントセンター

Tablestore:ベクトルデータの書き込み

最終更新日:Jan 18, 2026

データテーブルは、文字列形式とバイナリ形式でのベクトルデータの書き込みをサポートしています。文字列形式は可読性が高く、トラブルシューティングを簡素化できる一方、バイナリ形式はストレージコストを削減できます。

前提条件

大規模言語モデル (LLM) を使用して、画像、動画、テキストなどのコンテンツをベクトルデータに変換済みであること。詳細については、「ベクトルの生成」をご参照ください。

バイナリ形式

ベクトルデータをバイナリ形式で保存すると、ディスク領域の使用量が少なくなり、ストレージコストを削減できます。コストを重視するシナリオで、かつベクトルのディメンションが高い場合は、ベクトルデータをバイナリ形式で書き込むことを推奨します。

ベクトルデータをバイナリ形式で書き込む場合、Tablestore SDK またはツールを使用して、ベクターをバイナリデータに変換する必要があります。

重要
  • バイナリ形式で書き込まれた場合でも、ベクターの型は Float32 のままです。

  • ベクターはデータテーブルにバイナリ形式で保存され、読み取り時もバイナリデータとして扱われます。可読性を向上させるために、ユーティリティクラスを使用して文字列形式に変換できます。

Tablestore SDK を使用したバイナリ形式への変換

説明

Java SDK バージョン 5.17.6 および Python SDK バージョン 6.2.1 以降、Tablestore は VectorUtils ユーティリティクラスを使用したベクトルデータのバイナリ変換をサポートしています。

import com.alicloud.openservices.tablestore.SyncClient;
import com.alicloud.openservices.tablestore.model.*;
import com.alicloud.openservices.tablestore.model.search.vector.VectorUtils;

import java.util.Random;
import java.util.UUID;


// ランダムなベクターを生成するヘルパーメソッド。
private static float[] generateRandomFloats(int length, Random random) {
    float[] result = new float[length];
    for (int i = 0; i < length; i++) {
        result[i] = random.nextFloat();
    }
    return result;
}

// データをバッチで書き込みます。
private static void batchWriteRow(SyncClient tableStoreClient) throws Exception {
    
    Random random = new Random();
    // 100 行ずつのバッチで 1,000 行のデータを書き込みます。
    for (int i = 0; i < 10; i++) {
        BatchWriteRowRequest batchWriteRowRequest = new BatchWriteRowRequest();
        for (int j = 0; j < 100; j++) {
            // ビジネスデータ。
            String text = "A string for full-text search. An embedding vector is generated from this field and written to the field_vector field below for vector semantic similarity search";
            // 変換されたベクター。変換を実行します。
            float[] vector = generateRandomFloats(1024,random);
            RowPutChange rowPutChange = new RowPutChange("TABLE_NAME");
            // プライマリキーを設定します。
            rowPutChange.setPrimaryKey(PrimaryKeyBuilder.createPrimaryKeyBuilder().addPrimaryKeyColumn("PK_1", PrimaryKeyValue.fromString(UUID.randomUUID().toString())).build());
            // 属性列を設定します。
            rowPutChange.addColumn("field_string", ColumnValue.fromLong(i));
            rowPutChange.addColumn("field_long", ColumnValue.fromLong(i * 100 + j));
            rowPutChange.addColumn("field_text", ColumnValue.fromString(text));
            // ベクトルデータをバイナリ形式で書き込みます。
            rowPutChange.addColumn("field_vector", ColumnValue.fromBinary(VectorUtils.toBytes(vector)));

            batchWriteRowRequest.addRowChange(rowPutChange);
        }
        BatchWriteRowResponse batchWriteRowResponse = tableStoreClient.batchWriteRow(batchWriteRowRequest);
        System.out.println("Batch write successful: " + batchWriteRowResponse.isAllSucceed());
        if (!batchWriteRowResponse.isAllSucceed()) {
            for (BatchWriteRowResponse.RowResult rowResult : batchWriteRowResponse.getFailedRows()) {
                System.out.println("Failed row: " + batchWriteRowRequest.getRowChange(rowResult.getTableName(), rowResult.getIndex()).getPrimaryKey());
                System.out.println("Failure reason: " + rowResult.getError());
            }
        }
    }
}
import time
import tablestore.utils
from tablestore import *

def batch_write_vector(rows_count):
    print('Begin prepare data: %d' % rows_count)
    batch_write_row_reqs = BatchWriteRowRequest()
    put_row_items = []
    for i in range(rows_count):
        pk = [('PK1', i)]

        cols = [('field_string', 'key%03d' % i),
                ('field_long', i),
                ('field_text', 'some text'),
                ('field_vector', tablestore.utils.VectorUtils.floats_to_bytes([0.1, 0.2, 0.3, 0.4]))]

        put_row_item = PutRowItem(Row(pk,cols),Condition(RowExistenceExpectation.IGNORE))
        put_row_items.append(put_row_item)
    batch_write_row_reqs.add(TableInBatchWriteRowItem(table_name, put_row_items))
    client.batch_write_row(batch_write_row_reqs)

    print('End prepare data.')
    print('Wait for data sync to search index.')
    time.sleep(60)

ツールを使用したバイナリ形式への変換

public class VectorUtils {
    private static final ByteOrder order = ByteOrder.LITTLE_ENDIAN;

    /**
     * float[] 配列をバイナリ形式に変換します。
     * @param vector 変換するベクター。
     * @return byte バイナリ形式のデータ。
     */
    public static byte[] toBytes(float[] vector) {
        if (vector == null || vector.length == 0) {
            throw new ClientException("vector is null or empty");
        }
        ByteBuffer buffer = ByteBuffer.allocate(vector.length * 4);
        buffer.order(order);
        for (float value : vector) {
            buffer.putFloat(value);
        }
        return buffer.array();
    }

    /**
     * データをバイナリ形式から float[] 配列に戻します。
     * @param bytes バイナリ形式のデータ。
     * @return Float 元のベクター。
     */
    public static float[] toFloats(byte[] bytes) {
        int length = bytes.length / 4;
        if (bytes.length % 4 != 0 || length == 0) {
            throw new ClientException("bytes length is not multiple of 4(SIZE_OF_FLOAT32) or length is 0");
        }
        ByteBuffer buffer = ByteBuffer.wrap(bytes);
        buffer.order(order);
        float[] vector = new float[length];
        buffer.asFloatBuffer().get(vector);
        return vector;
    }
}
// Float32ToBytes は []float32 スライスをバイト配列に変換します。
func Float32ToBytes(vector []float32) ([]byte, error) {
	if len(vector) == 0 {
		return nil, errors.New("vector is null or empty")
	}
	data := make([]byte, 4*len(vector))

	for i, v := range vector {
		binary.LittleEndian.PutUint32(data[i*4:(i+1)*4], math.Float32bits(v))
	}
	return data, nil
}

// ToFloat32 はバイト配列を []float32 スライスに戻します。
func ToFloat32(data []byte) ([]float32, error) {
	if data == nil {
		return nil, errors.New("bytes is null")
	}
	if len(data)%4 != 0 || len(data) == 0 {
		return nil, errors.New("bytes length is not multiple of 4(SIZE_OF_FLOAT32) or length is 0")
	}
	floats := make([]float32, len(data)/4)
	buf := bytes.NewReader(data)

	for i := range floats {
		if err := binary.Read(buf, binary.LittleEndian, &floats[i]); err != nil {
			return nil, err
		}
	}

	return floats, nil
}
class VectorUtils:
    # 浮動小数点数をバイト配列に変換します。
    @staticmethod
    def floats_to_bytes(floats):
        if not isinstance(floats, (list, tuple)) or not all(isinstance(f, float) for f in floats):
            raise TypeError("Input must be a list/tuple of floats")
        if len(floats) == 0:
            raise ValueError("vector is empty")
        return bytearray(struct.pack('<' + 'f' * len(floats), *floats))
      
    # バイト配列を浮動小数点数に戻します。
    @staticmethod
    def bytes_to_floats(byte_data):
        if not isinstance(byte_data, bytearray):
            raise TypeError("Input must be a bytearray object")
        num_floats = len(byte_data) // 4
        if len(byte_data) % 4 != 0 or num_floats == 0:
            raise ValueError("bytes length is not multiple of 4(SIZE_OF_FLOAT32) or length is 0")
        floats = struct.unpack('<' + 'f' * num_floats, byte_data)
        return list(floats)

変換の検証

このセクションでは、Java SDK を使用して、ベクトルデータとバイナリデータ間の変換を検証する方法を示します。変換後の浮動小数点数配列が元の配列と同一であれば、変換は成功です。

public class VectorUtilsTest {
    public static void main(String[] args) {
        float[] vector = new float[] { 1, 2, 3, 4 };
        byte[] bytes = VectorUtils.toBytes(vector);
        System.out.println("変換後のバイナリデータ: " + Arrays.toString(bytes));
        float[] newVector = VectorUtils.toFloats(bytes);
        System.out.println("変換後の浮動小数点数配列: " + Arrays.toString(newVector));
    }
}

文字列形式

ベクターを文字列形式で保存すると、ディスク領域の使用量は増えますが、可読性は向上します。ベクトルデータを文字列形式で書き込む場合は、Float32 配列を [0.1,0.2,0.3,0.4] のような JSON 文字列に変換します。

このセクションでは、Java SDK を使用して、ベクトルデータを文字列形式で書き込む方法を示します。

// データをバッチで書き込みます。
private static void batchWriteRow(SyncClient tableStoreClient) throws Exception {
    // 100 行ずつのバッチで 1,000 行のデータを書き込みます。
    for (int i = 0; i < 10; i++) {
        BatchWriteRowRequest batchWriteRowRequest = new BatchWriteRowRequest();
        for (int j = 0; j < 100; j++) {
            // ビジネスデータ。
            String text = "A string for full-text search. An embedding vector is generated from this field and written to the field_vector field below for vector semantic similarity search";
            // テキストをベクターに変換します。
            String vector = "[1, 2, 3, 4]";
            RowPutChange rowPutChange = new RowPutChange("TABLE_NAME");
            // プライマリキーを設定します。
            rowPutChange.setPrimaryKey(PrimaryKeyBuilder.createPrimaryKeyBuilder().addPrimaryKeyColumn("PK_1", PrimaryKeyValue.fromString(UUID.randomUUID().toString())).build());
            // 属性列を設定します。
            rowPutChange.addColumn("field_string", ColumnValue.fromLong(i));
            rowPutChange.addColumn("field_long", ColumnValue.fromLong(i * 100 + j));
            rowPutChange.addColumn("field_text", ColumnValue.fromString(text));
            // ベクターのフォーマットは float32 配列の文字列です。例: [1, 5.1, 4.7, 0.08 ]。
            rowPutChange.addColumn("field_vector", ColumnValue.fromString(vector));

            batchWriteRowRequest.addRowChange(rowPutChange);
        }
        BatchWriteRowResponse batchWriteRowResponse = tableStoreClient.batchWriteRow(batchWriteRowRequest);
        System.out.println("Batch write successful: " + batchWriteRowResponse.isAllSucceed());
        if (!batchWriteRowResponse.isAllSucceed()) {
            for (BatchWriteRowResponse.RowResult rowResult : batchWriteRowResponse.getFailedRows()) {
                System.out.println("Failed row: " + batchWriteRowRequest.getRowChange(rowResult.getTableName(), rowResult.getIndex()).getPrimaryKey());
                System.out.println("Failure reason: " + rowResult.getError());
            }
        }
    }
}