fix: pin keyword index prefix=False round-trip behavior over gRPC (#1356)

grpc.KeywordPrefixParams is an empty message: presence is the only
signal, so an explicit prefix=False cannot be represented in gRPC.
It is sent as absent (same server-side semantics, disabled) and is
recovered as None. Document this at both conversion sites and pin
the behavior with a reverse-direction (rest->grpc->rest) test.
This commit is contained in:
2sumtech
2026-09-16 00:25:31 +07:00
committed by George Panchuk
parent 334e9cbf8c
commit 9a01291535
2 changed files with 32 additions and 0 deletions
+5
View File
@@ -1689,6 +1689,8 @@ class GrpcToRest:
on_disk=model.on_disk if model.HasField("on_disk") else None,
memory=cls.convert_memory(model.memory) if model.HasField("memory") else None,
enable_hnsw=model.enable_hnsw if model.HasField("enable_hnsw") else None,
# presence of grpc.KeywordPrefixParams is the only signal, an explicit `prefix=False`
# cannot be represented in grpc and comes back as `None`
prefix=True if model.HasField("prefix") else None,
)
@@ -4383,6 +4385,9 @@ class RestToGrpc:
is_tenant=model.is_tenant,
on_disk=model.on_disk,
enable_hnsw=model.enable_hnsw,
# grpc.KeywordPrefixParams is an empty message, its presence enables prefix matching,
# so an explicit `prefix=False` is sent as absent (which the server also treats as
# disabled) and is recovered as `None` when converting back
prefix=grpc.KeywordPrefixParams() if model.prefix else None,
memory=cls.convert_memory(model.memory) if model.memory is not None else None,
)
@@ -643,3 +643,30 @@ def test_convert_points_update_operation_falsy_shard_key():
"shard_key_selector"
), f"{type(operation).__name__} dropped shard_key={shard_key!r}"
assert GrpcToRest.convert_shard_key_selector(inner.shard_key_selector) == shard_key
def test_convert_keyword_index_params_prefix():
from qdrant_client import models
from qdrant_client.conversions.conversion import GrpcToRest, RestToGrpc
def round_trip(prefix):
rest_params = models.KeywordIndexParams(
type=models.KeywordIndexType.KEYWORD, prefix=prefix
)
grpc_params = RestToGrpc.convert_keyword_index_params(rest_params)
return grpc_params, GrpcToRest.convert_keyword_index_params(grpc_params)
grpc_params, recovered = round_trip(True)
assert grpc_params.HasField("prefix")
assert recovered.prefix is True
grpc_params, recovered = round_trip(None)
assert not grpc_params.HasField("prefix")
assert recovered.prefix is None
# grpc.KeywordPrefixParams is an empty message whose presence enables prefix matching,
# so an explicit `prefix=False` cannot be represented in grpc: it is sent as absent
# (which the server also treats as disabled) and is recovered as `None`
grpc_params, recovered = round_trip(False)
assert not grpc_params.HasField("prefix")
assert recovered.prefix is None