@@ -92,6 +92,20 @@ def _list_op(mv: Any, fv: Any, *, negate: bool) -> bool:
9292 return (mv not in fv ) if negate else (mv in fv )
9393
9494
95+ def _metadata_index_key (value : Any ) -> Any :
96+ if isinstance (value , dict ):
97+ items = ((str (key ), _metadata_index_key (item )) for key , item in value .items ())
98+ return ("dict" , tuple (sorted (items )))
99+ if isinstance (value , list ):
100+ return ("list" , tuple (_metadata_index_key (item ) for item in value ))
101+
102+ try :
103+ hash (value )
104+ except TypeError :
105+ return (type (value ).__qualname__ , repr (value ))
106+ return value
107+
108+
95109_COMPARISON_OPS : dict [str , Any ] = {
96110 "eq" : lambda mv , fv : mv == fv ,
97111 "ne" : lambda mv , fv : mv != fv ,
@@ -157,7 +171,8 @@ async def initialize(self) -> None:
157171 # Rebuild inverted metadata index from loaded chunks
158172 for pos , chunk in self .chunk_by_index .items ():
159173 for key , val in chunk .metadata .items ():
160- self ._meta_index .setdefault (key , {}).setdefault (val , set ()).add (pos )
174+ index_key = _metadata_index_key (val )
175+ self ._meta_index .setdefault (key , {}).setdefault (index_key , set ()).add (pos )
161176 except Exception as e :
162177 logger .debug ("Failed to deserialize Faiss index" , error = str (e ), exc_info = True )
163178 raise ValueError (
@@ -204,7 +219,8 @@ async def add_chunks(self, embedded_chunks: list[EmbeddedChunk]):
204219 faiss_pos = indexlen + i
205220 self .chunk_by_index [faiss_pos ] = embedded_chunk
206221 for key , val in embedded_chunk .metadata .items ():
207- self ._meta_index .setdefault (key , {}).setdefault (val , set ()).add (faiss_pos )
222+ index_key = _metadata_index_key (val )
223+ self ._meta_index .setdefault (key , {}).setdefault (index_key , set ()).add (faiss_pos )
208224
209225 async with self .chunk_id_lock :
210226 self .index .add (embeddings )
@@ -269,16 +285,16 @@ def _resolve_filter_positions(self, filter_obj: Filter) -> set[int]:
269285 # ComparisonFilter
270286 key , value , op_type = filter_obj .key , filter_obj .value , filter_obj .type
271287 if op_type == "eq" :
272- return self ._meta_index .get (key , {}).get (value , set ()).copy ()
288+ return self ._meta_index .get (key , {}).get (_metadata_index_key ( value ) , set ()).copy ()
273289 if op_type == "in" :
274290 result : set [int ] = set ()
275291 for v in value :
276- result |= self ._meta_index .get (key , {}).get (v , set ())
292+ result |= self ._meta_index .get (key , {}).get (_metadata_index_key ( v ) , set ())
277293 return result
278294 if op_type == "nin" :
279295 excluded : set [int ] = set ()
280296 for v in value :
281- excluded |= self ._meta_index .get (key , {}).get (v , set ())
297+ excluded |= self ._meta_index .get (key , {}).get (_metadata_index_key ( v ) , set ())
282298 all_positions = {pos for s in self ._meta_index .get (key , {}).values () for pos in s }
283299 return all_positions - excluded
284300 # Range ops and ne: linear scan over chunk_by_index metadata
0 commit comments