StructArray によるフィルター検索
このページでは、StructArray フィールドに対するベクトル検索にスカラーフィルタリングを追加する方法を説明します。StructArray フィルタリングには 2 つのレベルがあります。行レベルのフィルターは親エンティティを選択し、要素レベルのフィルターは、要素レベルのベクトル検索に参加する Struct 要素を制限します。
このページでは、StructArray フィールドを作成する の tech_articles コレクションを使用します。このコレクションには chunks という名前の StructArray フィールドがあり、section、page、quality_score、has_code などのスカラーサブフィールドと、検索用のベクトルサブフィールドがあります。
フィルタータイプを選択する
| 目的 | 使用するもの | 結果の動作 |
|---|---|---|
category などのトップレベルのスカラーフィールドでフィルターします。 | 通常のフィルター式。 | 検索の前または検索中に親エンティティを選択します。 |
| 要素レベルのベクトル検索を、スカラー条件に一致する Struct 要素に制限します。 | element_filter。 | 一致する Struct 要素のみを検索し、一致した要素のオフセットを返すことができます。 |
| いずれか、すべて、または指定した数の Struct 要素が述語に一致するかどうかでエンティティを選択します。 | MATCH_ANY、MATCH_ALL、MATCH_LEAST、MATCH_MOST、または MATCH_EXACT。 | 行レベルのフィルタリングです。これらの演算子自体はオフセットを返しません。 |
このページでは、検索ワークフローで StructArray フィルターを使用する方法を説明します。完全な構文ルール、サポートされる述語タイプ、およびサポートされない述語のマトリックスについては、StructArray 演算子 を参照してください。
トップレベルフィールドでフィルターする
条件が個々の Struct 要素ではなく親エンティティに属する場合は、通常のフィルター式を使用します。これは EmbeddingList 検索と要素レベルの検索の両方で機能します。
- Python
- Java
- Go
- Rust
- C++
- NodeJS
- cURL
from pymilvus import MilvusClient
from pymilvus.client.embedding_list import EmbeddingList
client = MilvusClient(
uri="YOUR_CLUSTER_ENDPOINT",
token="YOUR_CLUSTER_TOKEN",
)
query = EmbeddingList()
query.add([0.12, 0.21, 0.32, 0.44])
query.add([0.18, 0.23, 0.29, 0.36])
results = client.search(
collection_name="tech_articles",
data=[query],
anns_field="chunks[emb_list_vector]",
filter='category == "search"',
limit=3,
output_fields=[
"doc_id",
"title",
"category",
"chunks[text]",
"chunks[section]",
],
)
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.vector.request.SearchReq;
import io.milvus.v2.service.vector.request.data.EmbeddingList;
import io.milvus.v2.service.vector.request.data.FloatVec;
import io.milvus.v2.service.vector.response.SearchResp;
import java.util.Arrays;
import java.util.Collections;
ConnectConfig connectConfig = ConnectConfig.builder()
.uri("YOUR_CLUSTER_ENDPOINT")
.token("YOUR_CLUSTER_TOKEN")
.build();
MilvusClientV2 client = new MilvusClientV2(connectConfig);
EmbeddingList query = new EmbeddingList();
query.add(new FloatVec(Arrays.asList(0.12f, 0.21f, 0.32f, 0.44f)));
query.add(new FloatVec(Arrays.asList(0.18f, 0.23f, 0.29f, 0.36f)));
SearchResp results = client.search(SearchReq.builder()
.collectionName("tech_articles")
.data(Collections.singletonList(query))
.annsField("chunks[emb_list_vector]")
.filter("category == \"search\"")
.limit(3)
.outputFields(Arrays.asList("doc_id", "title", "category", "chunks[text]", "chunks[section]"))
.build());
import (
"context"
"fmt"
"log"
"github.com/milvus-io/milvus/client/v3/entity"
milvusclient "github.com/milvus-io/milvus/client/v3/milvusclient"
)
ctx := context.Background()
cli, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
Address: "YOUR_CLUSTER_ENDPOINT",
APIKey: "YOUR_CLUSTER_TOKEN",
})
if err != nil {
log.Fatal(err)
}
query := entity.FloatVectorArray{
entity.FloatVector{0.12, 0.21, 0.32, 0.44},
entity.FloatVector{0.18, 0.23, 0.29, 0.36},
}
results, err := cli.Search(ctx, milvusclient.NewSearchOption("tech_articles", 3, []entity.Vector{query}).
WithANNSField("chunks[emb_list_vector]").
WithFilter("category == \"search\"").
WithOutputFields("doc_id", "title", "category", "chunks[text]", "chunks[section]"))
if err != nil {
fmt.Println(err.Error())
}
fmt.Printf("returned %d result sets\n", len(results))
use milvus::v2::prelude::*;
let config = ConnectConfig::new().uri("YOUR_CLUSTER_ENDPOINT").token("YOUR_CLUSTER_TOKEN");
let client = ClientV2::new(&config).await?;
let query = EmbeddingList::new()
.add_vector(vec![0.12f32, 0.21, 0.32, 0.44])
.add_vector(vec![0.18f32, 0.23, 0.29, 0.36]);
let request = SearchRequest::builder()
.collection_name("tech_articles")
.vector_field("chunks[emb_list_vector]")
.vectors(SearchVectors::EmbeddingLists(vec![query]))
.filter("category == \"search\"")
.limit(3)
.output_fields(vec!["doc_id", "title", "category", "chunks[text]", "chunks[section]"])
.build()?;
let results = client.search(request).await?;
#include <iostream>
#include <memory>
#include "milvus/MilvusClientV2.h"
#include "milvus/request/dql/SearchRequest.h"
#include "milvus/types/EmbeddingList.h"
auto client = milvus::MilvusClientV2::Create();
auto status = client->Connect(milvus::ConnectParam("YOUR_CLUSTER_ENDPOINT").WithToken("YOUR_CLUSTER_TOKEN"));
if (!status.IsOk()) {
std::cout << status.Message() << std::endl;
}
milvus::EmbeddingList query;
query.AddFloatVector({0.12f, 0.21f, 0.32f, 0.44f});
query.AddFloatVector({0.18f, 0.23f, 0.29f, 0.36f});
milvus::SearchRequest request;
request.WithCollectionName("tech_articles")
.WithAnnsField("chunks[emb_list_vector]")
.WithLimit(3)
.WithFilter("category == \"search\"");
request.WithOutputFields({"doc_id", "title", "category", "chunks[text]", "chunks[section]"});
request.AddEmbeddingList(std::move(query));
milvus::SearchResponse response;
status = client->Search(request, response);
if (!status.IsOk()) {
std::cout << status.Message() << std::endl;
}
import { MilvusClient } from "@zilliz/milvus2-sdk-node";
const client = new MilvusClient({ address: "YOUR_CLUSTER_ENDPOINT", token: "YOUR_CLUSTER_TOKEN" });
const query = [
[0.12, 0.21, 0.32, 0.44],
[0.18, 0.23, 0.29, 0.36],
];
const results = await client.search({
collection_name: "tech_articles",
data: query,
anns_field: "chunks[emb_list_vector]",
filter: 'category == "search"',
limit: 3,
output_fields: ["doc_id", "title", "category", "chunks[text]", "chunks[section]"],
});
export CLUSTER_ENDPOINT="YOUR_CLUSTER_ENDPOINT"
export TOKEN="YOUR_CLUSTER_TOKEN"
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
"collectionName": "tech_articles",
"data": [[[0.12, 0.21, 0.32, 0.44], [0.18, 0.23, 0.29, 0.36]]],
"annsField": "chunks[emb_list_vector]",
"filter": "category == \"search\"",
"limit": 3,
"outputFields": ["doc_id", "title", "category", "chunks[text]", "chunks[section]"]
}'
上記のフィルターは、トップレベルの category フィールドが "search" であるエンティティのみを選択します。一致した 1 つの Struct 要素を特定するものではありません。
要素レベルのベクトル検索をフィルターする
スカラー条件を、要素レベルのベクトル検索に参加する同じ Struct 要素に適用する必要がある場合は、element_filter(structArrayField, predicate) を使用します。述語内では、$[subfield] を使用して現在の Struct 要素のスカラーサブフィールドを参照します。
- Python
- Java
- Go
- Rust
- C++
- NodeJS
- cURL
query_vector = [0.19, 0.24, 0.30, 0.37]
filter_expr = (
'category == "search" && '
'element_filter(chunks, '
'$[section] == "index" && '
'$[quality_score] > 0.9 && '
'$[has_code] == true)'
)
results = client.search(
collection_name="tech_articles",
data=[query_vector],
anns_field="chunks[emb]",
filter=filter_expr,
limit=5,
output_fields=[
"doc_id",
"title",
"chunks[text]",
"chunks[section]",
"chunks[page]",
"chunks[quality_score]",
"chunks[has_code]",
],
)
for hits in results:
for hit in hits:
print(
"doc_id:", hit["doc_id"],
"distance:", hit["distance"],
"offset:", hit.get("offset"),
"entity:", hit["entity"],
)
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.vector.request.SearchReq;
import io.milvus.v2.service.vector.request.data.FloatVec;
import io.milvus.v2.service.vector.response.SearchResp;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
ConnectConfig connectConfig = ConnectConfig.builder()
.uri("YOUR_CLUSTER_ENDPOINT")
.token("YOUR_CLUSTER_TOKEN")
.build();
MilvusClientV2 client = new MilvusClientV2(connectConfig);
FloatVec queryVector = new FloatVec(Arrays.asList(0.19f, 0.24f, 0.30f, 0.37f));
String filterExpr = "category == \"search\" && element_filter(chunks, $[section] == \"index\" && $[quality_score] > 0.9 && $[has_code] == true)";
SearchResp results = client.search(SearchReq.builder()
.collectionName("tech_articles")
.data(Collections.singletonList(queryVector))
.annsField("chunks[emb]")
.filter(filterExpr)
.limit(5)
.outputFields(Arrays.asList("doc_id", "title", "chunks[text]", "chunks[section]", "chunks[page]", "chunks[quality_score]", "chunks[has_code]"))
.build());
for (List<SearchResp.SearchResult> hits : results.getSearchResults()) {
for (SearchResp.SearchResult hit : hits) {
System.out.println("doc_id: " + hit.getId() + ", distance: " + hit.getScore()
+ ", offset: " + hit.getElementOffset() + ", entity: " + hit.getEntity());
}
}
import (
"context"
"fmt"
"log"
"github.com/milvus-io/milvus/client/v3/entity"
milvusclient "github.com/milvus-io/milvus/client/v3/milvusclient"
)
ctx := context.Background()
cli, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
Address: "YOUR_CLUSTER_ENDPOINT",
APIKey: "YOUR_CLUSTER_TOKEN",
})
if err != nil {
log.Fatal(err)
}
queryVector := entity.FloatVector{0.19, 0.24, 0.30, 0.37}
filterExpr := "category == \"search\" && element_filter(chunks, $[section] == \"index\" && $[quality_score] > 0.9 && $[has_code] == true)"
results, err := cli.Search(ctx, milvusclient.NewSearchOption("tech_articles", 5, []entity.Vector{queryVector}).
WithANNSField("chunks[emb]").
WithFilter(filterExpr).
WithOutputFields("doc_id", "title", "chunks[text]", "chunks[section]", "chunks[page]", "chunks[quality_score]", "chunks[has_code]"))
if err != nil {
fmt.Println(err.Error())
}
fmt.Printf("returned %d result sets\n", len(results))
use milvus::v2::prelude::*;
let config = ConnectConfig::new().uri("YOUR_CLUSTER_ENDPOINT").token("YOUR_CLUSTER_TOKEN");
let client = ClientV2::new(&config).await?;
let query_vector = vec![0.19f32, 0.24, 0.30, 0.37];
let filter_expr = "category == \"search\" && element_filter(chunks, $[section] == \"index\" && $[quality_score] > 0.9 && $[has_code] == true)";
let request = SearchRequest::builder()
.collection_name("tech_articles")
.vector_field("chunks[emb]")
.vectors(SearchVectors::Float(vec![query_vector]))
.filter(filter_expr)
.limit(5)
.output_fields(vec!["doc_id", "title", "chunks[text]", "chunks[section]", "chunks[page]", "chunks[quality_score]", "chunks[has_code]"])
.build()?;
let results = client.search(request).await?;
#include <iostream>
#include <memory>
#include <vector>
#include "milvus/MilvusClientV2.h"
#include "milvus/request/dql/SearchRequest.h"
auto client = milvus::MilvusClientV2::Create();
auto status = client->Connect(milvus::ConnectParam("YOUR_CLUSTER_ENDPOINT").WithToken("YOUR_CLUSTER_TOKEN"));
if (!status.IsOk()) {
std::cout << status.Message() << std::endl;
}
std::vector<float> query_vector = {0.19f, 0.24f, 0.30f, 0.37f};
milvus::SearchRequest request;
request.WithCollectionName("tech_articles")
.WithAnnsField("chunks[emb]")
.WithLimit(5)
.WithFilter("category == \"search\" && element_filter(chunks, $[section] == \"index\" && $[quality_score] > 0.9 && $[has_code] == true)");
request.WithOutputFields({"doc_id", "title", "chunks[text]", "chunks[section]", "chunks[page]", "chunks[quality_score]", "chunks[has_code]"});
request.AddFloatVector(query_vector);
milvus::SearchResponse response;
status = client->Search(request, response);
if (!status.IsOk()) {
std::cout << status.Message() << std::endl;
}
const query_vector = [0.19, 0.24, 0.30, 0.37];
const filter_expr = 'category == "search" && element_filter(chunks, $[section] == "index" && $[quality_score] > 0.9 && $[has_code] == true)';
const results = await client.search({
collection_name: "tech_articles",
data: query_vector,
anns_field: "chunks[emb]",
filter: filter_expr,
limit: 5,
output_fields: ["doc_id", "title", "chunks[text]", "chunks[section]", "chunks[page]", "chunks[quality_score]", "chunks[has_code]"],
});
export CLUSTER_ENDPOINT="YOUR_CLUSTER_ENDPOINT"
export TOKEN="YOUR_CLUSTER_TOKEN"
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
"collectionName": "tech_articles",
"data": [[0.19, 0.24, 0.30, 0.37]],
"annsField": "chunks[emb]",
"filter": "category == \"search\" && element_filter(chunks, $[section] == \"index\" && $[quality_score] > 0.9 && $[has_code] == true)",
"limit": 5,
"outputFields": ["doc_id", "title", "chunks[text]", "chunks[section]", "chunks[page]", "chunks[quality_score]", "chunks[has_code]"]
}'
この例では、トップレベルの述語 category == "search" が候補エンティティを選択し、element_filter が要素レベルのベクトル検索を、section、quality_score、has_code がすべて同じ Struct 要素内で一致するチャンクに制限します。
トップレベルの述語と element_filter を組み合わせる場合は、element_filter を式の末尾に配置します。フィルター式に含めることができる element_filter は 1 つだけであり、別の StructArray 演算子の中に element_filter または MATCH_* をネストすることはできません。
MATCH 演算子でエンティティをフィルターする
フィルターが、親エンティティの Struct 要素に基づいてその親エンティティが条件を満たすかどうかを決定する必要がある場合は、MATCH_* 演算子を使用します。これらの演算子は行レベルのフィルターです。エンティティを選択しますが、要素のオフセット自体は返しません。
| 演算子 | 使用する場合 | 例 |
|---|---|---|
MATCH_ANY | 少なくとも 1 つの Struct 要素が述語を満たす必要があります。 | MATCH_ANY(chunks, $[section] == "index") |
MATCH_ALL | すべての Struct 要素が述語を満たす必要があります。 | MATCH_ALL(chunks, $[quality_score] > 0.5) |
MATCH_LEAST | 少なくとも N 個の Struct 要素が述語を満たす必要があります。 | MATCH_LEAST(chunks, $[has_code] == true, threshold=2) |
MATCH_MOST | 最大で N 個の Struct 要素が述語を満たす必要があります。 | MATCH_MOST(chunks, $[section] == "appendix", threshold=1) |
MATCH_EXACT | ちょうど N 個の Struct 要素が述語を満たす必要があります。 | MATCH_EXACT(chunks, $[section] == "summary", threshold=1) |
- Python
- Java
- Go
- Rust
- C++
- NodeJS
- cURL
from pymilvus.client.embedding_list import EmbeddingList
query = EmbeddingList()
query.add([0.12, 0.21, 0.32, 0.44])
query.add([0.18, 0.23, 0.29, 0.36])
filter_expr = (
'category == "search" && '
'MATCH_ANY(chunks, $[section] == "index" && $[quality_score] > 0.9)'
)
results = client.search(
collection_name="tech_articles",
data=[query],
anns_field="chunks[emb_list_vector]",
filter=filter_expr,
limit=3,
output_fields=[
"doc_id",
"title",
"category",
"chunks[text]",
"chunks[section]",
"chunks[quality_score]",
],
)
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.vector.request.SearchReq;
import io.milvus.v2.service.vector.request.data.EmbeddingList;
import io.milvus.v2.service.vector.request.data.FloatVec;
import io.milvus.v2.service.vector.response.SearchResp;
import java.util.Arrays;
import java.util.Collections;
ConnectConfig connectConfig = ConnectConfig.builder()
.uri("YOUR_CLUSTER_ENDPOINT")
.token("YOUR_CLUSTER_TOKEN")
.build();
MilvusClientV2 client = new MilvusClientV2(connectConfig);
EmbeddingList query = new EmbeddingList();
query.add(new FloatVec(Arrays.asList(0.12f, 0.21f, 0.32f, 0.44f)));
query.add(new FloatVec(Arrays.asList(0.18f, 0.23f, 0.29f, 0.36f)));
String filterExpr = "category == \"search\" && MATCH_ANY(chunks, $[section] == \"index\" && $[quality_score] > 0.9)";
SearchResp results = client.search(SearchReq.builder()
.collectionName("tech_articles")
.data(Collections.singletonList(query))
.annsField("chunks[emb_list_vector]")
.filter(filterExpr)
.limit(3)
.outputFields(Arrays.asList("doc_id", "title", "category", "chunks[text]", "chunks[section]", "chunks[quality_score]"))
.build());
import (
"context"
"fmt"
"log"
"github.com/milvus-io/milvus/client/v3/entity"
milvusclient "github.com/milvus-io/milvus/client/v3/milvusclient"
)
ctx := context.Background()
cli, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
Address: "YOUR_CLUSTER_ENDPOINT",
APIKey: "YOUR_CLUSTER_TOKEN",
})
if err != nil {
log.Fatal(err)
}
query := entity.FloatVectorArray{
entity.FloatVector{0.12, 0.21, 0.32, 0.44},
entity.FloatVector{0.18, 0.23, 0.29, 0.36},
}
filterExpr := "category == \"search\" && MATCH_ANY(chunks, $[section] == \"index\" && $[quality_score] > 0.9)"
results, err := cli.Search(ctx, milvusclient.NewSearchOption("tech_articles", 3, []entity.Vector{query}).
WithANNSField("chunks[emb_list_vector]").
WithFilter(filterExpr).
WithOutputFields("doc_id", "title", "category", "chunks[text]", "chunks[section]", "chunks[quality_score]"))
if err != nil {
fmt.Println(err.Error())
}
fmt.Printf("returned %d result sets\n", len(results))
use milvus::v2::prelude::*;
let config = ConnectConfig::new().uri("YOUR_CLUSTER_ENDPOINT").token("YOUR_CLUSTER_TOKEN");
let client = ClientV2::new(&config).await?;
let query = EmbeddingList::new()
.add_vector(vec![0.12f32, 0.21, 0.32, 0.44])
.add_vector(vec![0.18f32, 0.23, 0.29, 0.36]);
let filter_expr = "category == \"search\" && MATCH_ANY(chunks, $[section] == \"index\" && $[quality_score] > 0.9)";
let request = SearchRequest::builder()
.collection_name("tech_articles")
.vector_field("chunks[emb_list_vector]")
.vectors(SearchVectors::EmbeddingLists(vec![query]))
.filter(filter_expr)
.limit(3)
.output_fields(vec!["doc_id", "title", "category", "chunks[text]", "chunks[section]", "chunks[quality_score]"])
.build()?;
let results = client.search(request).await?;
#include <iostream>
#include <memory>
#include "milvus/MilvusClientV2.h"
#include "milvus/request/dql/SearchRequest.h"
#include "milvus/types/EmbeddingList.h"
auto client = milvus::MilvusClientV2::Create();
auto status = client->Connect(milvus::ConnectParam("YOUR_CLUSTER_ENDPOINT").WithToken("YOUR_CLUSTER_TOKEN"));
if (!status.IsOk()) {
std::cout << status.Message() << std::endl;
}
milvus::EmbeddingList query;
query.AddFloatVector({0.12f, 0.21f, 0.32f, 0.44f});
query.AddFloatVector({0.18f, 0.23f, 0.29f, 0.36f});
milvus::SearchRequest request;
request.WithCollectionName("tech_articles")
.WithAnnsField("chunks[emb_list_vector]")
.WithLimit(3)
.WithFilter("category == \"search\" && MATCH_ANY(chunks, $[section] == \"index\" && $[quality_score] > 0.9)");
request.WithOutputFields({"doc_id", "title", "category", "chunks[text]", "chunks[section]", "chunks[quality_score]"});
request.AddEmbeddingList(std::move(query));
milvus::SearchResponse response;
status = client->Search(request, response);
if (!status.IsOk()) {
std::cout << status.Message() << std::endl;
}
const query = [
[0.12, 0.21, 0.32, 0.44],
[0.18, 0.23, 0.29, 0.36],
];
const filter_expr = 'category == "search" && MATCH_ANY(chunks, $[section] == "index" && $[quality_score] > 0.9)';
const results = await client.search({
collection_name: "tech_articles",
data: query,
anns_field: "chunks[emb_list_vector]",
filter: filter_expr,
limit: 3,
output_fields: ["doc_id", "title", "category", "chunks[text]", "chunks[section]", "chunks[quality_score]"],
});
export CLUSTER_ENDPOINT="YOUR_CLUSTER_ENDPOINT"
export TOKEN="YOUR_CLUSTER_TOKEN"
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
"collectionName": "tech_articles",
"data": [[[0.12, 0.21, 0.32, 0.44], [0.18, 0.23, 0.29, 0.36]]],
"annsField": "chunks[emb_list_vector]",
"filter": "category == \"search\" && MATCH_ANY(chunks, $[section] == \"index\" && $[quality_score] > 0.9)",
"limit": 3,
"outputFields": ["doc_id", "title", "category", "chunks[text]", "chunks[section]", "chunks[quality_score]"]
}'
ここでは、EmbeddingList 検索結果がエンティティレベルであるため、MATCH_ANY を使用します。フィルターでは、エンティティ内の少なくとも 1 つのチャンクが高品質の "index" チャンクである必要がありますが、検索結果自体は引き続き親エンティティを表します。
ハイブリッド検索でフィルターを使用する
ハイブリッド検索では、条件を適用する場所に StructArray フィルターを適用します。トップレベルのフィルターは、ハイブリッド検索全体で共有できます。element_filter は、要素レベルの制約が必要な StructArray 要素レベルのリクエストにアタッチする必要があります。
- Python
- Java
- Go
- Rust
- C++
- NodeJS
- cURL
from pymilvus import AnnSearchRequest, RRFRanker
query_vector = [0.19, 0.24, 0.30, 0.37]
title_req = AnnSearchRequest(
data=[query_vector],
anns_field="title_vector",
param={},
limit=10,
expr='category == "search"',
)
chunk_req = AnnSearchRequest(
data=[query_vector],
anns_field="chunks[emb]",
param={},
limit=10,
expr='category == "search" && element_filter(chunks, $[section] == "index" && $[quality_score] > 0.9)',
)
results = client.hybrid_search(
collection_name="tech_articles",
reqs=[title_req, chunk_req],
ranker=RRFRanker(),
limit=5,
output_fields=[
"doc_id",
"title",
"category",
"chunks[text]",
"chunks[section]",
"chunks[quality_score]",
],
)
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.vector.request.AnnSearchReq;
import io.milvus.v2.service.vector.request.HybridSearchReq;
import io.milvus.v2.service.vector.request.data.FloatVec;
import io.milvus.v2.service.vector.request.ranker.RRFRanker;
import io.milvus.v2.service.vector.response.SearchResp;
import java.util.Arrays;
import java.util.Collections;
ConnectConfig connectConfig = ConnectConfig.builder()
.uri("YOUR_CLUSTER_ENDPOINT")
.token("YOUR_CLUSTER_TOKEN")
.build();
MilvusClientV2 client = new MilvusClientV2(connectConfig);
FloatVec queryVector = new FloatVec(Arrays.asList(0.19f, 0.24f, 0.30f, 0.37f));
AnnSearchReq titleReq = AnnSearchReq.builder()
.vectorFieldName("title_vector")
.vectors(Collections.singletonList(queryVector))
.limit(10)
.filter("category == \"search\"")
.build();
AnnSearchReq chunkReq = AnnSearchReq.builder()
.vectorFieldName("chunks[emb]")
.vectors(Collections.singletonList(queryVector))
.limit(10)
.filter("category == \"search\" && element_filter(chunks, $[section] == \"index\" && $[quality_score] > 0.9)")
.build();
SearchResp results = client.hybridSearch(HybridSearchReq.builder()
.collectionName("tech_articles")
.searchRequests(Arrays.asList(titleReq, chunkReq))
.ranker(RRFRanker.builder().k(60).build())
.limit(5)
.outFields(Arrays.asList("doc_id", "title", "category", "chunks[text]", "chunks[section]", "chunks[quality_score]"))
.build());
import (
"context"
"fmt"
"log"
"github.com/milvus-io/milvus/client/v3/entity"
milvusclient "github.com/milvus-io/milvus/client/v3/milvusclient"
)
ctx := context.Background()
cli, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
Address: "YOUR_CLUSTER_ENDPOINT",
APIKey: "YOUR_CLUSTER_TOKEN",
})
if err != nil {
log.Fatal(err)
}
queryVector := entity.FloatVector{0.19, 0.24, 0.30, 0.37}
titleReq := milvusclient.NewAnnRequest("title_vector", 10, queryVector).
WithFilter("category == \"search\"")
chunkReq := milvusclient.NewAnnRequest("chunks[emb]", 10, queryVector).
WithFilter("category == \"search\" && element_filter(chunks, $[section] == \"index\" && $[quality_score] > 0.9)")
results, err := cli.HybridSearch(ctx, milvusclient.NewHybridSearchOption("tech_articles", 5,
titleReq, chunkReq).
WithReranker(milvusclient.NewRRFReranker()).
WithOutputFields("doc_id", "title", "category", "chunks[text]", "chunks[section]", "chunks[quality_score]"))
if err != nil {
fmt.Println(err.Error())
}
fmt.Printf("returned %d result sets\n", len(results))
use milvus::v2::prelude::*;
let config = ConnectConfig::new().uri("YOUR_CLUSTER_ENDPOINT").token("YOUR_CLUSTER_TOKEN");
let client = ClientV2::new(&config).await?;
let query_vector = vec![0.19f32, 0.24, 0.30, 0.37];
let title_req = SubSearchRequest::builder()
.vector_field("title_vector")
.vectors(SearchVectors::Float(vec![query_vector.clone()]))
.limit(10)
.filter("category == \"search\"")
.build()?;
let chunk_req = SubSearchRequest::builder()
.vector_field("chunks[emb]")
.vectors(SearchVectors::Float(vec![query_vector]))
.limit(10)
.filter("category == \"search\" && element_filter(chunks, $[section] == \"index\" && $[quality_score] > 0.9)")
.build()?;
let request = HybridSearchRequest::builder()
.collection_name("tech_articles")
.sub_requests(vec![title_req, chunk_req])
.rerank(RRFRerank::new().k(60))
.limit(5)
.output_fields(vec!["doc_id", "title", "category", "chunks[text]", "chunks[section]", "chunks[quality_score]"])
.build()?;
let results = client.hybrid_search(request).await?;
#include <iostream>
#include <memory>
#include <vector>
#include "milvus/MilvusClientV2.h"
#include "milvus/request/dql/HybridSearchRequest.h"
#include "milvus/types/SubSearchRequest.h"
auto client = milvus::MilvusClientV2::Create();
auto status = client->Connect(milvus::ConnectParam("YOUR_CLUSTER_ENDPOINT").WithToken("YOUR_CLUSTER_TOKEN"));
if (!status.IsOk()) {
std::cout << status.Message() << std::endl;
}
std::vector<float> query_vector = {0.19f, 0.24f, 0.30f, 0.37f};
auto title_req = std::make_shared<milvus::SubSearchRequest>();
title_req->WithAnnsField("title_vector").WithLimit(10);
title_req->WithFilter("category == \"search\"");
title_req->AddFloatVector(query_vector);
auto chunk_req = std::make_shared<milvus::SubSearchRequest>();
chunk_req->WithAnnsField("chunks[emb]").WithLimit(10);
chunk_req->WithFilter("category == \"search\" && element_filter(chunks, $[section] == \"index\" && $[quality_score] > 0.9)");
chunk_req->AddFloatVector(query_vector);
milvus::HybridSearchRequest request;
request.WithCollectionName("tech_articles")
.WithLimit(5)
.AddSubRequest(title_req)
.AddSubRequest(chunk_req)
.WithRerank(std::make_shared<milvus::RRFRerank>(60));
request.WithOutputFields({"doc_id", "title", "category", "chunks[text]", "chunks[section]", "chunks[quality_score]"});
milvus::HybridSearchResponse response;
status = client->HybridSearch(request, response);
if (!status.IsOk()) {
std::cout << status.Message() << std::endl;
}
const query_vector = [0.19, 0.24, 0.30, 0.37];
const results = await client.search({
collection_name: "tech_articles",
data: [
{ anns_field: "title_vector", data: [query_vector], limit: 10, expr: 'category == "search"' },
{
anns_field: "chunks[emb]",
data: query_vector,
limit: 10,
expr: 'category == "search" && element_filter(chunks, $[section] == "index" && $[quality_score] > 0.9)',
},
],
rerank: { strategy: "rrf", params: { k: 60 } },
limit: 5,
output_fields: ["doc_id", "title", "category", "chunks[text]", "chunks[section]", "chunks[quality_score]"],
});
export CLUSTER_ENDPOINT="YOUR_CLUSTER_ENDPOINT"
export TOKEN="YOUR_CLUSTER_TOKEN"
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/hybrid_search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
"collectionName": "tech_articles",
"search": [
{
"data": [[0.19, 0.24, 0.30, 0.37]],
"annsField": "title_vector",
"limit": 10,
"filter": "category == \"search\""
},
{
"data": [[0.19, 0.24, 0.30, 0.37]],
"annsField": "chunks[emb]",
"limit": 10,
"filter": "category == \"search\" && element_filter(chunks, $[section] == \"index\" && $[quality_score] > 0.9)"
}
],
"rerank": { "strategy": "rrf", "params": { "k": 60 } },
"limit": 5,
"outputFields": ["doc_id", "title", "category", "chunks[text]", "chunks[section]", "chunks[quality_score]"]
}'
filter 引数はトップレベルのエンティティ条件を適用し、chunk_req の expr は StructArray 要素レベルのベクトルリクエストのみを制限します。サポートされるハイブリッド検索の組み合わせとバージョン固有の制限については、StructArray を使用したハイブリッド検索 および StructArray の制限 を参照してください。
述語のサポート概要
StructArray の述語ではスカラーサブフィールドを使用します。ベクトルサブフィールドはスカラー述語の入力ではありません。
| サブフィールドタイプ | 述語の一般的な例 |
|---|---|
BOOL | $[has_code] == true, !($[has_code] == true) |
| 整数型 | $[page] >= 2, $[page] in [1, 2, 3] |
FLOAT, DOUBLE | $[quality_score] > 0.9, 0.7 < $[quality_score] < 0.95 |
VARCHAR | $[section] == "index", $[text] like "range%" |
| ベクトルサブフィールド | $[...] のスカラー述語入力としてはサポートされません。代わりに、ベクトル検索を通じてベクトルサブフィールドを使用します。 |
JSON パス、配列コンテナ関数、テキスト一致関数、$[...] に対する null 述語、Geometry 関数、Timestamptz 式、汎用関数呼び出しなど、サポートされないケースについては、StructArray 演算子 を参照してください。
よくある間違い
-
element_filterまたはMATCH_*の外で$[subfield]を使用しないでください。 -
element_filter(chunks, $[section] == "index")などの StructArray 演算子構文の代わりにchunks.sectionを使用しないでください。 -
行レベルのフィルタリングのみが必要な場合は、
element_filterを使用しないでください。エンティティを選択するだけでよい場合は、代わりにMATCH_ANYを使用します。 -
MATCH_*が要素のオフセットを返すと期待しないでください。これらの演算子はエンティティを選択するものであり、単独では一致した 1 つの要素を特定しません。 -
$[has_code]のような裸のブール述語を記述しないでください。$[has_code] == trueのような明示的な比較を使用します。 -
同じフィルター式内で、トップレベルの述語より前に
element_filterを配置しないでください。
次のステップ
-
StructArray フィルターの完全な構文を確認するには、StructArray 演算子 を参照してください。
-
最初にフィルターなしのベクトル検索を実行するには、StructArray を使用した基本的なベクトル検索 を参照してください。
-
頻繁に使用する StructArray フィルターのスカラーインデックスを作成するには、StructArray フィールドのインデックス作成 を参照してください。
-
バージョン固有のフィルターと検索の制限を確認するには、StructArray の制限 を参照してください。