搜尋的資料模型設計

資訊檢索系統(也稱為搜尋引擎)對於各種人工智慧(AI)應用而言是不可或缺的,例如檢索增強世代(Retrieval-augmented generation,RAG)、視覺搜尋和產品推薦。這些系統的核心是精心設計的資料模型,用以組織、索引和擷取資訊。

Milvus 可讓您透過集合模式指定搜尋資料模型,組織非結構化資料、其密集或稀疏向量表示法,以及結構化的元資料。無論您處理的是文字、影像或其他資料類型,這份實務指南將協助您了解並應用關鍵的模式概念,以便在實務中設計搜尋資料模型。

Data Model Anatomy 資料模型剖析

資料模型

搜尋系統的資料模型設計包括分析業務需求,並將資訊抽象為模式表達的資料模型。定義良好的模式對於使資料模型符合業務目標、確保資料一致性和服務品質非常重要。 此外,選擇適當的資料類型和索引對於經濟地達成業務目標也很重要。

分析業務需求

要有效解決業務需求,首先要分析使用者將執行的查詢類型,並決定最適合的搜尋方法。

  • 使用者查詢:確定使用者預期執行的查詢類型。這有助於確保您的模式支援真實世界的使用個案,並最佳化搜尋效能。這些可能包括

    • 檢索符合自然語言查詢的文件

    • 尋找與參考影像相似或符合文字描述的影像

    • 依據名稱、類別或品牌等屬性搜尋產品

    • 根據結構化的元資料(例如出版日期、標籤、評分)過濾項目

    • 在混合查詢中結合多重條件(例如,在視覺搜尋中,同時考慮圖片及其標題的語意相似性)

  • 搜尋方法:根據使用者將執行的查詢類型,選擇適當的搜尋技術。不同的方法可達到不同的目的,而且通常可以結合使用,以獲得更強大的結果:

    • 語意搜尋:使用密集向量相似性來尋找意義相似的項目,適用於文字或影像等非結構化資料。

    • 全文搜尋:使用關鍵字比對來補充語意搜尋。 全文檢索可利用詞彙分析,避免將長字分割成零碎的詞組,在檢索過程中掌握特殊詞彙。

    • 元資料篩選:在向量搜尋之上,應用日期範圍、類別或標籤等限制條件。

將業務需求轉換為搜尋資料模型

下一步是將您的業務需求轉換成具體的資料模型,方法是辨識資訊的核心元件及其搜尋方法:

  • 定義您需要儲存的資料,例如原始內容 (文字、影像、音訊)、相關的元資料 (標題、標籤、作者),以及上下文屬性 (時間戳記、使用者行為等)

  • 為每個元素決定適當的資料類型和格式。例如

    • 文字描述 → 字串

    • 影像或文件嵌入 → 密集或稀疏向量

    • 類別、標籤或旗標 → 字串、陣列和 bool

    • 價格或評價等數字屬性 → 整數或浮點數

    • 結構化資訊,如作者詳細資訊 → json

明確定義這些元素可確保資料的一致性、準確的搜尋結果,以及容易與下游應用程式邏輯整合。

模式設計

在 Milvus 中,資料模型透過集合模式來表達。在集合模式中設計正確的欄位是實現有效檢索的關鍵。每個欄位定義了儲存於資料集中的特定資料類型,並在搜尋過程中扮演獨特的角色。在高層次上,Milvus 支援兩種主要的欄位類型:向量欄位標量欄位

現在,您可以將資料模型映射為欄位模式,包括向量和任何輔助標量欄位。確保每個欄位都與資料模型的屬性相關,尤其要注意向量類型(dense 或 spase)及其維度。

向量欄位

向量欄位會儲存文字、影像和音訊等非結構化資料類型的嵌入。這些嵌入可能是密集、稀疏或二進位,視資料類型和使用的檢索方法而定。通常,密集向量用於語意搜尋,而稀疏向量則較適合全文或詞彙比對。當儲存和計算資源有限時,二進位向量就很有用。一個資料集可能包含數個向量欄位,以啟用多模式或混合式的檢索策略。有關此主題的詳細指南,請參考多向量混合檢索

Milvus 支援向量資料類型:FLOAT_VECTOR 代表密集向量SPARSE_FLOAT_VECTOR 代表稀疏向量BINARY_VECTOR 代表二進位向量

標量欄位

標量欄位儲存原始、結構化的值,通常稱為元資料,例如數字、字串或日期。這些值可以與向量搜尋結果一起傳回,對於篩選和排序非常重要。它們允許您根據特定屬性縮小搜尋結果的範圍,例如將文件限制在特定類別或定義的時間範圍內。

Milvus 支援標量類型,例如BOOL,INT8/16/32/64,FLOAT,DOUBLE,VARCHAR,JSON, 和ARRAY ,用於儲存和過濾非向量資料。這些類型增強了搜尋作業的精確度與客製化。

在模式設計中利用進階功能

設計模式時,僅使用支援的資料類型將資料映射到欄位是不夠的。必須徹底瞭解欄位之間的關係以及可用於配置的策略。在設計階段牢記關鍵功能,可確保模式不僅能滿足當前的資料處理需求,還能擴充並適應未來的需求。通過仔細整合這些功能,您可以建立一個強大的數據架構,最大限度地發揮 Milvus 的功能,並支持您更廣泛的數據策略和目標。以下是建立集合模式的主要功能概述:

主鍵

主鍵字段是模式的基本組成部分,因為它唯一識別集合中的每個實體。定義主索引鍵是必須的。它必須是整數或字串類型的標量欄位,並標示為is_primary=True 。您可以選擇為主索引鍵啟用auto_id ,主索引鍵會自動指派整數,並隨著更多資料被擷取到資料集中而單一成長。

如需詳細資訊,請參閱Primary Field & AutoID

分割

為了加快搜尋速度,您可以選擇開啟分割。透過指定特定的標量欄位進行分割,並在搜尋過程中根據此欄位指定篩選條件,可有效地將搜尋範圍限制為僅相關的分割。此方法可縮小搜尋範圍,大幅提升檢索作業的效率。

如需詳細資訊,請參閱使用分割鍵

分析器

分析器是處理和轉換文字資料的重要工具。它的主要功能是將原始文字轉換為標記,並將它們結構化,以便編制索引和進行檢索。分析器會將字串標記化、刪除停頓字詞,並將個別字詞轉化成標記。

如需詳細資訊,請參閱Analyzer 概觀

功能

Milvus 允許您定義內建函式作為模式的一部分,以自動衍生某些欄位。例如,您可以新增內建 BM25 函式,從VARCHAR 欄位產生稀疏向量,以支援全文檢索。這些由函式衍生的欄位可簡化預先處理程序,並確保資料集維持自足且可隨時查詢。

如需詳細資訊,請參閱全文檢索。

真實世界範例

在本節中,我們將概述上圖所示多媒體文件搜尋應用程式的模式設計與程式碼範例。此模式設計用於管理包含文章的資料集,其資料映射至下列欄位:

欄位

資料來源

搜尋方法使用

主鍵

分割鍵

分析器

功能輸入/輸出

article_id (INT64)

啟用後自動產生auto_id

使用 Get 進行查詢

Y

N

N

N

標題 (VARCHAR)

文章標題

文字匹配

N

N

Y

N

時間戳 (INT32)

發佈日期

依分割區金鑰篩選

N

Y

N

N

文字 (VARCHAR)

文章原始文字

多向量混合搜尋

N

N

Y

輸入

text_dense_vector (FLOAT_VECTOR)

由文字嵌入模型產生的密集向量

基本向量搜尋

N

N

N

N

text_sparse_vector (SPARSE_FLOAT_VECTOR)

內建 BM25 函式自動產生的稀疏向量

全文檢索

N

N

N

輸出

如需更多關於模式的資訊,以及新增各類欄位的詳細指引,請參閱Schema Explained

初始化模式

首先,我們需要建立一個空模式。此步驟為定義資料模型建立基礎結構。

from pymilvus import MilvusClient

schema = MilvusClient.create_schema()
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.collection.request.CreateCollectionReq;

// 1. Connect to Milvus server
ConnectConfig connectConfig = ConnectConfig.builder()
        .uri("http://localhost:19530")
        .build();

MilvusClientV2 client = new MilvusClientV2(connectConfig);

// 2. Create an empty schema
CreateCollectionReq.CollectionSchema schema = client.createSchema();
import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node";

//Skip this step using JavaScript
import "github.com/milvus-io/milvus/client/v2/entity"

schema := entity.NewSchema()
# Skip this step using cURL

新增欄位

一旦建立了模式,下一步就是指定組成資料的欄位。每個欄位都與各自的資料類型和屬性相關聯。

from pymilvus import DataType

schema.add_field(field_name="article_id", datatype=DataType.INT64, is_primary=True, auto_id=True, description="article id")
schema.add_field(field_name="title", datatype=DataType.VARCHAR, enable_analyzer=True, enable_match=True, max_length=200, description="article title")
schema.add_field(field_name="timestamp", datatype=DataType.INT32, description="publish date")
schema.add_field(field_name="text", datatype=DataType.VARCHAR, max_length=2000, enable_analyzer=True, description="article text content")
schema.add_field(field_name="text_dense_vector", datatype=DataType.FLOAT_VECTOR, dim=768, description="text dense vector")
schema.add_field(field_name="text_sparse_vector", datatype=DataType.SPARSE_FLOAT_VECTOR, description="text sparse vector")
import io.milvus.v2.common.DataType;
import io.milvus.v2.service.collection.request.AddFieldReq;

schema.addField(AddFieldReq.builder()
        .fieldName("article_id")
        .dataType(DataType.Int64)
        .isPrimaryKey(true)
        .autoID(true)
        .build());
schema.addField(AddFieldReq.builder()
        .fieldName("title")
        .dataType(DataType.VarChar)
        .maxLength(200)
        .enableAnalyzer(true)
        .enableMatch(true)
        .build());
schema.addField(AddFieldReq.builder()
        .fieldName("timestamp")
        .dataType(DataType.Int32)
        .build())
schema.addField(AddFieldReq.builder()
        .fieldName("text")
        .dataType(DataType.VarChar)
        .maxLength(2000)
        .enableAnalyzer(true)
        .build());
schema.addField(AddFieldReq.builder()
        .fieldName("text_dense_vector")
        .dataType(DataType.FloatVector)
        .dimension(768)
        .build());
schema.addField(AddFieldReq.builder()
        .fieldName("text_sparse_vector")
        .dataType(DataType.SparseFloatVector)
        .build());
const fields = [
    {
        name: "article_id",
        data_type: DataType.Int64,
        is_primary_key: true,
        auto_id: true
    },
    {
        name: "title",
        data_type: DataType.VarChar,
        max_length: 200,
        enable_analyzer: true,
        enable_match: true
    },
    {
        name: "timestamp",
        data_type: DataType.Int32
    },
    {
        name: "text",
        data_type: DataType.VarChar,
        max_length: 2000,
        enable_analyzer: true
    },
    {
        name: "text_dense_vector",
        data_type: DataType.FloatVector,
        dim: 768
    },
    {
        name: "text_sparse_vector",
        data_type: DataType.SparseFloatVector
    }
]
schema.WithField(entity.NewField().
    WithName("article_id").
    WithDataType(entity.FieldTypeInt64).
    WithIsPrimaryKey(true).
    WithIsAutoID(true).
    WithDescription("article id"),
).WithField(entity.NewField().
    WithName("title").
    WithDataType(entity.FieldTypeVarChar).
    WithMaxLength(200).
    WithEnableAnalyzer(true).
    WithEnableMatch(true).
    WithDescription("article title"),
).WithField(entity.NewField().
    WithName("timestamp").
    WithDataType(entity.FieldTypeInt32).
    WithDescription("publish date"),
).WithField(entity.NewField().
    WithName("text").
    WithDataType(entity.FieldTypeVarChar).
    WithMaxLength(2000).
    WithEnableAnalyzer(true).
    WithDescription("article text content"),
).WithField(entity.NewField().
    WithName("text_dense_vector").
    WithDataType(entity.FieldTypeFloatVector).
    WithDim(768).
    WithDescription("text dense vector"),
).WithField(entity.NewField().
    WithName("text_sparse_vector").
    WithDataType(entity.FieldTypeSparseVector).
    WithDescription("text sparse vector"),
)
export fields='[
    {
        "fieldName": "article_id",
        "dataType": "Int64",
        "isPrimary": true
    },
    {
        "fieldName": "title",
        "dataType": "VarChar",
        "elementTypeParams": {
            "max_length": 200,
            "enable_analyzer": true,
            "enable_match": true
        }
    },
    {
        "fieldName": "timestamp",
        "dataType": "Int32"
    },
    {
       "fieldName": "text",
       "dataType": "VarChar",
       "elementTypeParams": {
            "max_length": 2000,
            "enable_analyzer": true
        }
    },
    {
       "fieldName": "text_dense_vector",
       "dataType": "FloatVector",
       "elementTypeParams": {
            "dim": 768
        }
    },
    {
       "fieldName": "text_sparse_vector",
       "dataType": "SparseFloatVector",
    }
]'

export schema="{
    \"autoID\": true,
    \"fields\": $fields
}"

在本範例中,欄位指定了下列屬性:

  • Primary key(主鍵):article_id 用作主鍵,可自動為輸入的實體分配主鍵。

  • 分區鍵:timestamp 被指定為分區鍵,可透過分區進行篩選。這可能是

  • 文字分析器:文字分析器應用於 2 個字串欄位titletext ,以分別支援文字匹配和全文搜尋。

(選用)新增功能

為了增強資料查詢功能,可在模式中加入函式。例如,可以建立函式來處理特定欄位的相關資料。

from pymilvus import Function, FunctionType

bm25_function = Function(
    name="text_bm25",
    input_field_names=["text"],
    output_field_names=["text_sparse_vector"],
    function_type=FunctionType.BM25,
)

schema.add_function(bm25_function)
import io.milvus.common.clientenum.FunctionType;
import io.milvus.v2.service.collection.request.CreateCollectionReq.Function;

import java.util.*;

schema.addFunction(Function.builder()
        .functionType(FunctionType.BM25)
        .name("text_bm25")
        .inputFieldNames(Collections.singletonList("text"))
        .outputFieldNames(Collections.singletonList("text_sparse_vector"))
        .build());
import FunctionType from "@zilliz/milvus2-sdk-node";

const functions = [
    {
      name: 'text_bm25',
      description: 'bm25 function',
      type: FunctionType.BM25,
      input_field_names: ['text'],
      output_field_names: ['text_sparse_vector'],
      params: {},
    },
];
function := entity.NewFunction().
    WithName("text_bm25").
    WithInputFields("text").
    WithOutputFields("text_sparse_vector").
    WithType(entity.FunctionTypeBM25)
schema.WithFunction(function)
export myFunctions='[
    {
        "name": "text_bm25",
        "type": "BM25",
        "inputFieldNames": ["text"],
        "outputFieldNames": ["text_sparse_vector"],
        "params": {}
    }
]'

export schema="{
    \"autoID\": true,
    \"fields\": $fields
    \"functions\": $myFunctions
}"

本範例在模式中加入內建的 BM25 函式,利用text 欄位作為輸入,並將產生的稀疏向量儲存於text_sparse_vector 欄位。

下一步