我在 Qdrant 上有一个数据结构,在有效负载中,我有这样的东西:
{
"attributes": [
{
"attribute_value_id": 22003,
"id": 1252,
"key": "Environment",
"value": "Casual/Daily",
},
{
"attribute_value_id": 98763,
"id": 1254,
"key": "Color",
"value": "Multicolored",
},
{
"attribute_value_id": 22040,
"id": 1255,
"key": "Material",
"value": "Polyester",
},
],
"brand": {
"id": 114326,
"logo": None,
"slug": "happiness-istanbul-114326",
"title": "Happiness Istanbul",
},
}
根据Qdrant文档,我实现了这样的品牌过滤:
filters_list = []
if param_filters:
brands = param_filters.get("brand_params")
if brands:
filter = models.FieldCondition(
key="brand.id",
match=models.MatchAny(any=[int(brand) for brand in brands]),
)
filters_list.append(filter)
search_results = qd_client.search(
query_filter=models.Filter(must=filters_list),
collection_name=f"lang{lang}_products",
query_vector=query_vector,
search_params=models.SearchParams(hnsw_ef=128, exact=False),
limit=limit,
)
到目前为止有效。但是当我尝试过滤“属性”字段时,事情变得复杂。如您所见,它是一个字典列表,包含以下字典:
{
"attribute_value_id": 22040,
"id": 1255,
"key": "Material",
"value": "Polyester",
}
而前端发送过来的
attrs
过滤器就是这样的结构:
attrs structure: {"attr_id": [attr_value_ids], "attr_id": [att_value_ids]}
>>> example: {'1237': ['21727', '21759'], '1254': ['52776']}
如何过滤以查看查询过滤器参数中提供的
attr_id
(此处为 1237
或 1254
)是否存在于 attributes
字段中并且具有提供的 attr_value_id
之一在列表中(例如此处的['21727', '21759']
)?
这是我迄今为止尝试过的:
if attrs:
# attrs structure: {"attr_id": [attr_value_ids], "attr_id": [att_value_ids]}
print("attrs from search function:", attrs)
for attr_id, attr_value_ids in attrs.items():
# Convert attribute value IDs to integers
attr_value_ids = [
int(attr_value_id) for attr_value_id in attr_value_ids
]
# Add a filter for each attribute ID and its values
filter = models.FieldCondition(
key=f"attributes.{attr_id}.attr_value_id",
match=models.MatchAny(any=attr_value_ids),
)
filters_list.append(filter)
问题是
key=f"attributes.{attr_id}.attr_value_id",
是错误的,我不知道如何实现这一点。
更新:也许更接近一步:
我决定展平数据库中的数据,也许可以做得更好。首先,我创建了一个名为 flattened_attributes 的新字段,如下所示:
[
{
"1237": 21720
},
{
"1254": 52791
},
{
"1255": 22044
},
]
此外,在过滤之前,我对前端发送的 attr 过滤器采用了相同的方法:
if attrs:
# attrs structure: {"attr_id": [attr_value_ids], "attr_id": [att_value_ids]}
# we need to flatten attrs to filter on payloads
flattened_attr = []
for attr_id, attr_value_ids in attrs.items():
for attr_value_id in attr_value_ids:
flattened_attr.append({attr_id:int(attr_value_id)})
现在,我有两个类似的字典列表,我想过滤那些至少有一个是从前端收到的(
flattened_attr
)。
有一种类型的过滤,如果键的值存在于值列表中,我们会进行过滤,如文档中提到的。但我不知道如何检查数据库中的 flattened_attributes
字段中是否存在字典。
注意主要问题中
attributes
字段的结构,我们看到有一个
attribute_value_id
键,对于不同的属性,该键可能不同(例如,“颜色”为 1254,“材质”为 1255)。因此,在
search
函数中,我编写了以下代码(我将详细介绍它):
attrs = param_filters.get("attr_params")
if attrs:
# attrs structure: {"attr_id": [attr_value_ids], "attr_id": [attr_value_ids]}
# we need to flatten attrs to filter on payloads
for attr_id, attr_value_ids in attrs.items():
flattened_attr = []
for attr_value_id in attr_value_ids:
flattened_attr.append(int(attr_value_id))
filter = models.FieldCondition(
key="attributes[].attribute_value_id",
match=models.MatchAny(any=flattened_attr),
)
filters_list.append(filter)
search_results = qd_client.search(
query_filter=models.Filter(must=filters_list),
collection_name=f"lang{lang}_products",
query_vector=query_vector,
search_params=models.SearchParams(hnsw_ef=128, exact=False),
limit=limit,
)
首先,对于每个attr_id
,我创建了一个包含
attr_value_ids
的单独列表(我必须将它们转换为
int
)。然后,使用 Qdrant 文档(
here),我使用 key="attributes[].attribute_value_id"
浏览
attributes
字段内的列表项,并在每个列表项(每个都是字典)内查找
attribute_value_id
键,并匹配它与发送的值。另请注意,我正在为每个 attr_id 创建一个单独的过滤器:
for attr_id, attr_value_ids in attrs.items():
flattened_attr = []
for attr_value_id in attr_value_ids:
flattened_attr.append(int(attr_value_id))
filter = models.FieldCondition(
key="attributes[].attribute_value_id",
match=models.MatchAny(any=flattened_attr),
)
filters_list.append(filter)
这是因为,当发送一个 attr_id 的多个值时,至少其中一个应该为 true(attribute_value_id 之间的OR
),但是当发送另一个 attr_id 时,这个新的值和前一个值都应该为 true(每个 attr_id 之间的
AND
)。另外,请注意,我在主过滤条件中使用
must
,因此每个过滤器分别应为
True
,而在每个过滤器内,任何 value_ids 都是可接受的。
qd_client.search(
query_filter=models.Filter(must=filters_list),
collection_name=f"lang{lang}_products",
query_vector=query_vector,
search_params=models.SearchParams(hnsw_ef=128, exact=False),
limit=limit,
)