フィルターテンプレート
Zilliz Cloud では、多数の要素を含む複雑なフィルター式、特に CJK 文字のような非 ASCII 文字を含むものは、クエリ性能に大きな影響を与える可能性があります。これに対処するため、Zilliz Cloud では、複雑な式の解析に費やす時間を削減して効率を向上させるよう設計されたフィルター式テンプレート化メカニズムを導入しています。このページでは、search、query、および delete 操作でフィルター式テンプレート化を使用する方法について説明します。
フィルタリング式の左辺にあるリテラルには、以下の例で使用されている age、city などのコレクションフィールド名、または filter = 'struct[0][subfield] > {var}' のように特定の要素インデックスにある StructArray サブフィールドの名前を指定できます。
StructArray フィールドにおけるスカラーフィルタリングの詳細については、StructArray Operators を参照してください。
概要
フィルター式テンプレート化を使用すると、クエリ実行時に値へ動的に置き換えられるプレースホルダー付きのフィルター式を作成できます。テンプレート化を使うことで、大きな配列や複雑な式を直接フィルターに埋め込む必要がなくなり、解析時間を短縮してクエリ性能を向上させることができます。
たとえば、age と city という 2 つのフィールドを含むフィルター式があり、年齢が 25 より大きく、かつ "北京"(Beijing)または "上海"(Shanghai)に住んでいるすべての人を見つけたいとします。値をフィルター式に直接埋め込む代わりに、テンプレートを使用できます。
- Python
- Java
- Go
- Rust
- C++
- NodeJS
- cURL
filter = "age > {age} AND city IN {city}"
filter_params = {"age": 25, "city": ["北京", "上海"]}
import java.util.*;
String filter = "age > {age} AND city IN {city}";
Map<String, Object> filterTemplateValues = new HashMap<>();
filterTemplateValues.put("age", 25);
filterTemplateValues.put("city", Arrays.asList("北京", "上海"));
filter := "age > {age} AND city IN {city}"
filterTemplates := map[string]any{
"age": 25,
"city": []string{"北京", "上海"},
}
use milvus::v2::prelude::*;
use std::collections::HashMap;
let filter = "age > {age} AND city IN {city}";
let filter_templates: HashMap<String, FilterTemplateValue> = HashMap::from([
("age".into(), FilterTemplateValue::Int64(25)),
("city".into(), FilterTemplateValue::StringArray(vec!["北京".to_string(), "上海".to_string()])),
]);
std::string filter = "age > {age} AND city IN {city}";
std::unordered_map<std::string, nlohmann::json> filterTemplates = {
{"age", 25},
{"city", nlohmann::json::array({"北京", "上海"})}
};
const filter = "age > {age} AND city IN {city}";
const exprValues = { age: 25, city: ["北京", "上海"] };
# restful
export TOKEN="YOUR_CLUSTER_TOKEN"
export CLUSTER_ENDPOINT="YOUR_CLUSTER_ENDPOINT"
filter="age > {age} AND city IN {city}"
filter_params='{"age": 25, "city": ["北京", "上海"]}'
ここで、{age} と {city} はプレースホルダーであり、クエリ実行時に filter_params 内の実際の値に置き換えられます。
Zilliz Cloud でフィルター式テンプレート化を使用することには、いくつかの重要な利点があります。
-
解析時間の短縮: 大きいまたは複雑なフィルター式をプレースホルダーに置き換えることで、システムがフィルターを解析および処理する時間を削減できます。
-
クエリ性能の向上: 解析オーバーヘッドが減ることでクエリ性能が向上し、より高い QPS とより速い応答時間が実現します。
-
スケーラビリティ: データセットが大きくなり、フィルター式がより複雑になっても、テンプレート化によって性能を効率的かつスケーラブルに維持できます。
検索操作
Zilliz Cloud の search 操作では、filter 式を使用してフィルタリング条件を定義し、filter_params パラメーターを使用してプレースホルダーの値を指定します。filter_params ディクショナリには、Zilliz Cloud がフィルター式に代入する動的な値が含まれます。
- Python
- Java
- Go
- Rust
- C++
- NodeJS
- cURL
expr = "age > {age} AND city IN {city}"
filter_params = {"age": 25, "city": ["北京", "上海"]}
res = client.search(
"hello_milvus",
[[0.1, 0.2]],
filter=expr,
limit=10,
output_fields=["age", "city"],
search_params={"params": {"search_list": 100}},
filter_params=filter_params,
)
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.*;
String expr = "age > {age} AND city IN {city}";
Map<String, Object> filterTemplateValues = new HashMap<>();
filterTemplateValues.put("age", 25);
filterTemplateValues.put("city", Arrays.asList("北京", "上海"));
SearchReq searchReq = SearchReq.builder()
.collectionName("hello_milvus")
.data(Collections.singletonList(new FloatVec(new float[]{0.1f, 0.2f})))
.filter(expr)
.topK(10)
.outputFields(Arrays.asList("age", "city"))
.filterTemplateValues(filterTemplateValues)
.build();
SearchResp searchResp = client.search(searchReq);
import (
"context"
"fmt"
"github.com/milvus-io/milvus/client/v3/entity"
"github.com/milvus-io/milvus/client/v3/milvusclient"
)
expr := "age > {age} AND city IN {city}"
resultSets, err := client.Search(ctx, milvusclient.NewSearchOption(
"hello_milvus", // collectionName
10, // limit
[]entity.Vector{entity.FloatVector([]float32{0.1, 0.2})},
).WithANNSField("vector").
WithFilter(expr).
WithTemplateParam("age", 25).
WithTemplateParam("city", []string{"北京", "上海"}).
WithOutputFields("age", "city"))
if err != nil {
fmt.Println(err.Error())
// handle error
}
use milvus::v2::prelude::*;
use std::collections::HashMap;
let expr = "age > {age} AND city IN {city}";
let filter_templates: HashMap<String, FilterTemplateValue> = HashMap::from([
("age".into(), FilterTemplateValue::Int64(25)),
("city".into(), FilterTemplateValue::StringArray(vec!["北京".to_string(), "上海".to_string()])),
]);
let res = client
.search(
SearchRequest::builder()
.collection_name("hello_milvus")
.vector_field("vector")
.vectors(SearchVectors::Float(vec![vec![0.1, 0.2]]))
.filter(expr)
.limit(10)
.output_fields(["age", "city"])
.filter_templates(filter_templates)
.build()?,
)
.await?;
std::string expr = "age > {age} AND city IN {city}";
std::unordered_map<std::string, nlohmann::json> filterTemplates = {
{"age", 25},
{"city", nlohmann::json::array({"北京", "上海"})}
};
auto searchRequest = milvus::SearchRequest()
.WithCollectionName("hello_milvus")
.WithAnnsField("vector")
.WithLimit(10)
.WithOutputFields({"age", "city"})
.WithFilter(expr)
.WithFilterTemplates(std::move(filterTemplates))
.AddFloatVector({0.1f, 0.2f});
milvus::SearchResponse searchResponse;
client->Search(searchRequest, searchResponse);
const expr = "age > {age} AND city IN {city}";
const exprValues = { age: 25, city: ["北京", "上海"] };
const res = await client.search({
collection_name: "hello_milvus",
data: [[0.1, 0.2]],
filter: expr,
limit: 10,
output_fields: ["age", "city"],
exprValues
});
filter="age > {age} AND city IN {city}"
filter_params='{"age": 25, "city": ["北京", "上海"]}'
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--data '{
"collectionName": "hello_milvus",
"data": [[0.1, 0.2]],
"annsField": "vector",
"limit": 10,
"filter": "age > {age} AND city IN {city}",
"exprParams": {"age": 25, "city": ["北京", "上海"]},
"outputFields": ["age", "city"]
}'
この例では、Zilliz Cloud は search の実行時に {age} を 25 に、{city} を ["北京", "上海"] に動的に置き換えます。
クエリ操作
同じテンプレート化メカニズムは、Zilliz Cloud の query 操作にも適用できます。query 関数では、フィルター式を定義し、filter_params を使用して置き換える値を指定します。
- Python
- Java
- Go
- Rust
- C++
- NodeJS
- cURL
expr = "age > {age} AND city IN {city}"
filter_params = {"age": 25, "city": ["北京", "上海"]}
res = client.query(
"hello_milvus",
filter=expr,
output_fields=["age", "city"],
filter_params=filter_params
)
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.vector.request.QueryReq;
import io.milvus.v2.service.vector.response.QueryResp;
import java.util.*;
String expr = "age > {age} AND city IN {city}";
Map<String, Object> filterTemplateValues = new HashMap<>();
filterTemplateValues.put("age", 25);
filterTemplateValues.put("city", Arrays.asList("北京", "上海"));
QueryReq queryReq = QueryReq.builder()
.collectionName("hello_milvus")
.filter(expr)
.outputFields(Arrays.asList("age", "city"))
.filterTemplateValues(filterTemplateValues)
.build();
QueryResp queryResp = client.query(queryReq);
import (
"context"
"fmt"
"github.com/milvus-io/milvus/client/v3/milvusclient"
)
expr := "age > {age} AND city IN {city}"
resultSet, err := client.Query(ctx, milvusclient.NewQueryOption("hello_milvus").
WithFilter(expr).
WithTemplateParam("age", 25).
WithTemplateParam("city", []string{"北京", "上海"}).
WithOutputFields("age", "city"))
if err != nil {
fmt.Println(err.Error())
// handle error
}
use milvus::v2::prelude::*;
use std::collections::HashMap;
let expr = "age > {age} AND city IN {city}";
let filter_templates: HashMap<String, FilterTemplateValue> = HashMap::from([
("age".into(), FilterTemplateValue::Int64(25)),
("city".into(), FilterTemplateValue::StringArray(vec!["北京".to_string(), "上海".to_string()])),
]);
let res = client
.query(
QueryRequest::builder()
.collection_name("hello_milvus")
.filter(expr)
.output_fields(["age", "city"])
.filter_templates(filter_templates)
.build()?,
)
.await?;
std::string expr = "age > {age} AND city IN {city}";
std::unordered_map<std::string, nlohmann::json> filterTemplates = {
{"age", 25},
{"city", nlohmann::json::array({"北京", "上海"})}
};
auto queryRequest = milvus::QueryRequest()
.WithCollectionName("hello_milvus")
.WithFilter(expr)
.WithOutputFields({"age", "city"})
.WithFilterTemplates(std::move(filterTemplates));
milvus::QueryResponse queryResponse;
client->Query(queryRequest, queryResponse);
const expr = "age > {age} AND city IN {city}";
const exprValues = { age: 25, city: ["北京", "上海"] };
const res = await client.query({
collection_name: "hello_milvus",
filter: expr,
output_fields: ["age", "city"],
exprValues
});
filter="age > {age} AND city IN {city}"
filter_params='{"age": 25, "city": ["北京", "上海"]}'
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/query" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--data '{
"collectionName": "hello_milvus",
"filter": "age > {age} AND city IN {city}",
"exprParams": {"age": 25, "city": ["北京", "上海"]},
"outputFields": ["age", "city"]
}'
filter_params を使用することで、Zilliz Cloud は値の動的な挿入を効率的に処理し、query 実行速度を向上させます。
削除操作
delete 操作でもフィルター式テンプレート化を使用できます。search や query と同様に、filter 式で条件を定義し、filter_params でプレースホルダー用の動的な値を提供します。
- Python
- Java
- Go
- Rust
- C++
- NodeJS
- cURL
expr = "age > {age} AND city IN {city}"
filter_params = {"age": 25, "city": ["北京", "上海"]}
res = client.delete(
"hello_milvus",
filter=expr,
filter_params=filter_params
)
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.vector.request.DeleteReq;
import java.util.*;
String expr = "age > {age} AND city IN {city}";
Map<String, Object> filterTemplateValues = new HashMap<>();
filterTemplateValues.put("age", 25);
filterTemplateValues.put("city", Arrays.asList("北京", "上海"));
DeleteReq deleteReq = DeleteReq.builder()
.collectionName("hello_milvus")
.filter(expr)
.filterTemplateValues(filterTemplateValues)
.build();
client.delete(deleteReq);
import (
"context"
"fmt"
"github.com/milvus-io/milvus/client/v3/milvusclient"
)
expr := "age > {age} AND city IN {city}"
result, err := client.Delete(ctx, milvusclient.NewDeleteOption("hello_milvus").
WithFilter(expr).
WithTemplateParam("age", 25).
WithTemplateParam("city", []string{"北京", "上海"}))
if err != nil {
fmt.Println(err.Error())
// handle error
}
use milvus::v2::prelude::*;
use std::collections::HashMap;
let expr = "age > {age} AND city IN {city}";
let filter_templates: HashMap<String, FilterTemplateValue> = HashMap::from([
("age".into(), FilterTemplateValue::Int64(25)),
("city".into(), FilterTemplateValue::StringArray(vec!["北京".to_string(), "上海".to_string()])),
]);
let res = client
.delete(
DeleteRequest::builder()
.collection_name("hello_milvus")
.filter(expr)
.filter_templates(filter_templates)
.build()?,
)
.await?;
std::string expr = "age > {age} AND city IN {city}";
std::unordered_map<std::string, nlohmann::json> filterTemplates = {
{"age", 25},
{"city", nlohmann::json::array({"北京", "上海"})}
};
auto deleteRequest = milvus::DeleteRequest()
.WithCollectionName("hello_milvus")
.WithFilter(expr)
.WithFilterTemplates(std::move(filterTemplates));
milvus::DeleteResponse deleteResponse;
client->Delete(deleteRequest, deleteResponse);
const expr = "age > {age} AND city IN {city}";
const exprValues = { age: 25, city: ["北京", "上海"] };
const res = await client.delete({
collection_name: "hello_milvus",
filter: expr,
exprValues
});
filter="age > {age} AND city IN {city}"
filter_params='{"age": 25, "city": ["北京", "上海"]}'
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/delete" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--data '{
"collectionName": "hello_milvus",
"filter": "age > {age} AND city IN {city}",
"exprParams": {"age": 25, "city": ["北京", "上海"]}
}'
このアプローチは、特に複雑なフィルター条件を扱う場合に、delete 操作の性能を向上させます。
正規表現フィルターテンプレート
正規表現フィルターでもフィルター式テンプレート化を使用できます。これは、正規表現パターンがリクエスト時に提供される場合に便利です。
- Python
- Java
- Go
- Rust
- C++
- NodeJS
- cURL
expr = "message =~ {pattern}"
filter_params = {"pattern": "E[0-9]{4}"}
res = client.query(
"hello_milvus",
filter=expr,
output_fields=["message"],
filter_params=filter_params,
)
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.vector.request.QueryReq;
import io.milvus.v2.service.vector.response.QueryResp;
import java.util.*;
String expr = "message =~ {pattern}";
Map<String, Object> filterTemplateValues = new HashMap<>();
filterTemplateValues.put("pattern", "E[0-9]{4}");
QueryReq queryReq = QueryReq.builder()
.collectionName("hello_milvus")
.filter(expr)
.outputFields(Arrays.asList("message"))
.filterTemplateValues(filterTemplateValues)
.build();
QueryResp queryResp = client.query(queryReq);
import (
"context"
"fmt"
"github.com/milvus-io/milvus/client/v3/milvusclient"
)
expr := "message =~ {pattern}"
resultSet, err := client.Query(ctx, milvusclient.NewQueryOption("hello_milvus").
WithFilter(expr).
WithTemplateParam("pattern", "E[0-9]{4}").
WithOutputFields("message"))
if err != nil {
fmt.Println(err.Error())
// handle error
}
use milvus::v2::prelude::*;
use std::collections::HashMap;
let expr = "message =~ {pattern}";
let filter_templates: HashMap<String, FilterTemplateValue> = HashMap::from([
("pattern".into(), FilterTemplateValue::String("E[0-9]{4}".to_string())),
]);
let res = client
.query(
QueryRequest::builder()
.collection_name("hello_milvus")
.filter(expr)
.output_fields(["message"])
.filter_templates(filter_templates)
.build()?,
)
.await?;
std::string expr = "message =~ {pattern}";
std::unordered_map<std::string, nlohmann::json> filterTemplates = {
{"pattern", "E[0-9]{4}"}
};
auto queryRequest = milvus::QueryRequest()
.WithCollectionName("hello_milvus")
.WithFilter(expr)
.WithOutputFields({"message"})
.WithFilterTemplates(std::move(filterTemplates));
milvus::QueryResponse queryResponse;
client->Query(queryRequest, queryResponse);
const expr = "message =~ {pattern}";
const exprValues = { pattern: "E[0-9]{4}" };
const res = await client.query({
collection_name: "hello_milvus",
filter: expr,
output_fields: ["message"],
exprValues
});
filter="message =~ {pattern}"
filter_params='{"pattern": "E[0-9]{4}"}'
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/query" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--data '{
"collectionName": "hello_milvus",
"filter": "message =~ {pattern}",
"exprParams": {"pattern": "E[0-9]{4}"},
"outputFields": ["message"]
}'
!~ でもテンプレートパラメーターを使用できます。
- Python
- Java
- Go
- Rust
- C++
- NodeJS
- cURL
expr = "message !~ {pattern}"
filter_params = {"pattern": "^DEBUG"}
import java.util.*;
String expr = "message !~ {pattern}";
Map<String, Object> filterTemplateValues = new HashMap<>();
filterTemplateValues.put("pattern", "^DEBUG");
expr := "message !~ {pattern}"
filterTemplates := map[string]any{
"pattern": "^DEBUG",
}
use milvus::v2::prelude::*;
use std::collections::HashMap;
let expr = "message !~ {pattern}";
let filter_templates: HashMap<String, FilterTemplateValue> = HashMap::from([
("pattern".into(), FilterTemplateValue::String("^DEBUG".to_string())),
]);
std::string expr = "message !~ {pattern}";
std::unordered_map<std::string, nlohmann::json> filterTemplates = {
{"pattern", "^DEBUG"}
};
const expr = "message !~ {pattern}";
const exprValues = { pattern: "^DEBUG" };
filter="message !~ {pattern}"
filter_params='{"pattern": "^DEBUG"}'
テンプレート値は、有効な RE2 正規表現パターンを含む文字列である必要があります。Zilliz Cloud はフィルターを実行する前にそのパターンを検証します。
フィルターテンプレートでは、正規表現パターンをフィルター式に連結するのではなく、値として渡します。これにより、式解析のオーバーヘッドが削減され、パターンに引用符や演算子が含まれている場合でも誤ってフィルター構造が変更されるのを防げます。
まとめ
フィルター式テンプレート化は、Zilliz Cloud におけるクエリ性能最適化のための重要なツールです。プレースホルダーと filter_params ディクショナリを使用することで、複雑なフィルター式の解析に費やす時間を大幅に削減できます。これにより、クエリ実行が高速化され、全体的な性能が向上します。