88)
99from neumai .SinkConnectors .SinkConnector import SinkConnector
1010from typing import List , Optional
11+ from neumai .SinkConnectors .filter_utils import FilterCondition , FilterOperator
1112from qdrant_client .http .models import Distance , VectorParams
1213from qdrant_client .http .models import PointStruct
1314from qdrant_client .http .models import UpdateStatus
1415from qdrant_client import QdrantClient
16+ from qdrant_client .http .models import Filter
1517from pydantic import Field
1618
1719class QdrantSink (SinkConnector ):
@@ -85,10 +87,57 @@ def store(self, vectors_to_store:List[NeumVector]) -> int:
8587 return len (points )
8688 raise QdrantInsertionException ("Qdrant storing failed. Try again later." )
8789
88- def search (self , vector : List [float ], number_of_results : int , filter :dict = {}) -> List :
90+ def filter_conditions_to_qdrant_filter (filters : List [FilterCondition ]) -> dict :
91+ if len (filters ) > 1 :
92+ weaviate_filter = {
93+ "operator" :"And" ,
94+ "operands" : []
95+ }
96+ for filter in filters :
97+ weaviate_filter = {
98+ "path" :[filter .field ],
99+ "operator" : filter .operator ,
100+ "valueText" : filter .value
101+ }
102+ weaviate_filter ["operands" ].append (weaviate_filter )
103+ else :
104+ neum_filter = filters [0 ]
105+ weaviate_filter = {
106+ "path" :[neum_filter .field ],
107+ "operator" : neum_filter .operator ,
108+ "valueText" : neum_filter .value
109+ }
110+ return weaviate_filter
111+
112+ def translate_to_qdrant (filter_conditions :List [FilterCondition ]):
113+ qdrant_filter = {"must" : []}
114+
115+ for condition in filter_conditions :
116+ if condition .operator == FilterOperator .EQUAL :
117+ qdrant_filter ["must" ].append ({"key" : condition .field , "match" : {"value" : condition .value }})
118+ elif condition .operator == FilterOperator .NOT_EQUAL :
119+ # Qdrant doesn't have a direct "not equal" filter, so it's handled with must_not
120+ qdrant_filter .setdefault ("must_not" , []).append ({"key" : condition .field , "match" : {"value" : condition .value }})
121+ elif condition .operator in [FilterOperator .LESS_THAN , FilterOperator .LESS_THAN_OR_EQUAL ,
122+ FilterOperator .GREATER_THAN , FilterOperator .GREATER_THAN_OR_EQUAL ]:
123+ range_filter = {"key" : condition .field , "range" : {}}
124+ if condition .operator == FilterOperator .LESS_THAN :
125+ range_filter ["range" ]["lt" ] = condition .value
126+ elif condition .operator == FilterOperator .LESS_THAN_OR_EQUAL :
127+ range_filter ["range" ]["lte" ] = condition .value
128+ elif condition .operator == FilterOperator .GREATER_THAN :
129+ range_filter ["range" ]["gt" ] = condition .value
130+ elif condition .operator == FilterOperator .GREATER_THAN_OR_EQUAL :
131+ range_filter ["range" ]["gte" ] = condition .value
132+ qdrant_filter ["must" ].append (range_filter )
133+
134+ return qdrant_filter
135+
136+ def search (self , vector : List [float ], number_of_results : int , filter :List [FilterCondition ]= []) -> List :
89137 url = self .url
90138 api_key = self .api_key
91139 collection_name = self .collection_name
140+ filters = self .translate_to_qdrant (filter )
92141
93142 try :
94143 qdrant_client = QdrantClient (
@@ -100,6 +149,7 @@ def search(self, vector: List[float], number_of_results: int, filter:dict = {})
100149 query_vector = vector ,
101150 with_payload = True ,
102151 limit = number_of_results ,
152+ query_filter = Filter (** filters )
103153 )
104154 except Exception as e :
105155 raise QdrantQueryException (f"Failed to query Qdrant. Exception - { e } " )
0 commit comments