mirror of
https://github.com/qdrant/qdrant-client.git
synced 2026-09-21 21:47:45 -05:00
fix(local): isolate payload values across wildcard targets (#1438)
* fix(local): isolate payload values across wildcard targets * refactor: copy once per wildcard element, move the tests to local --------- Co-authored-by: George Panchuk <george.panchuk@qdrant.tech>
This commit is contained in:
co-authored by
George Panchuk
parent
e603c2d5c9
commit
9c826ce9b3
@@ -1,3 +1,4 @@
|
||||
from copy import deepcopy
|
||||
from typing import Any, Type
|
||||
|
||||
from qdrant_client.local.json_path_parser import JsonPathItem, JsonPathItemType
|
||||
@@ -279,18 +280,21 @@ class WildcardIndexSetter(_ListSetter):
|
||||
k_list: list[JsonPathItem],
|
||||
value: dict[str, Any],
|
||||
) -> None:
|
||||
if len(k_list) == 0:
|
||||
for i, item in enumerate(data):
|
||||
for i, item in enumerate(data):
|
||||
# a wildcard is the one path item that writes the same value to several places,
|
||||
# so each element takes its own copy. Sharing a single object would make a later
|
||||
# write scoped to one element, e.g. set_payload(key="arr[0].x"), reach them all.
|
||||
item_value = deepcopy(value)
|
||||
if len(k_list) == 0:
|
||||
if isinstance(item, dict):
|
||||
data[i].update(value)
|
||||
item.update(item_value)
|
||||
else:
|
||||
data[i] = value
|
||||
else:
|
||||
for i, item in enumerate(data):
|
||||
data[i] = item_value
|
||||
else:
|
||||
cls.set(
|
||||
item,
|
||||
k_list.copy(),
|
||||
value,
|
||||
item_value,
|
||||
data,
|
||||
JsonPathItem(item_type=JsonPathItemType.INDEX, index=i),
|
||||
)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
from qdrant_client import QdrantClient, models
|
||||
|
||||
|
||||
def test_set_payload_keeps_array_elements_independent() -> None:
|
||||
client = QdrantClient(":memory:")
|
||||
client.create_collection("test", vectors_config={})
|
||||
client.upsert(
|
||||
"test",
|
||||
[models.PointStruct(id=1, vector={}, payload={"items": [{"id": 1}, {"id": 2}]})],
|
||||
)
|
||||
client.set_payload("test", payload={"metadata": {"color": "blue"}}, points=[1], key="items[]")
|
||||
client.set_payload("test", payload={"color": "red"}, points=[1], key="items[0].metadata")
|
||||
|
||||
assert client.retrieve("test", [1])[0].payload == {
|
||||
"items": [
|
||||
{"id": 1, "metadata": {"color": "red"}},
|
||||
{"id": 2, "metadata": {"color": "blue"}},
|
||||
]
|
||||
}
|
||||
@@ -11,6 +11,40 @@ from qdrant_client.local.payload_value_extractor import value_by_key
|
||||
from qdrant_client.local.payload_value_setter import delete_value_by_key, set_value_by_key
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"payload,key,target_key,expected_colors",
|
||||
[
|
||||
({"items": [{}, {}]}, "items[]", "items[0].metadata", ["red", "blue"]),
|
||||
({"items": [None, None]}, "items[]", "items[0].metadata", ["red", "blue"]),
|
||||
(
|
||||
{"items": [{}, {}]},
|
||||
"items[].nested",
|
||||
"items[0].nested.metadata",
|
||||
["red", "blue"],
|
||||
),
|
||||
(
|
||||
{"items": [[{}, {}], [{}, {}]]},
|
||||
"items[][]",
|
||||
"items[0][0].metadata",
|
||||
["red", "blue", "blue", "blue"],
|
||||
),
|
||||
(
|
||||
{"items": [{"sub": [{}, {}]}, {"sub": [{}]}]},
|
||||
"items[].sub[]",
|
||||
"items[0].sub[0].metadata",
|
||||
["red", "blue", "blue"],
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_wildcard_set_keeps_array_elements_independent(
|
||||
payload: dict[str, Any], key: str, target_key: str, expected_colors: list[str]
|
||||
) -> None:
|
||||
set_value_by_key(payload, parse_json_path(key), {"metadata": {"color": "blue"}})
|
||||
set_value_by_key(payload, parse_json_path(target_key), {"color": "red"})
|
||||
|
||||
assert value_by_key(payload, f"{key}.metadata.color") == expected_colors
|
||||
|
||||
|
||||
def test_parse_json_path() -> None:
|
||||
jp_key = "a"
|
||||
keys = parse_json_path(jp_key)
|
||||
|
||||
Reference in New Issue
Block a user