crewplus 0.2.38__py3-none-any.whl → 0.2.40__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of crewplus might be problematic. Click here for more details.
- crewplus/vectorstores/milvus/schema_milvus.py +349 -63
- {crewplus-0.2.38.dist-info → crewplus-0.2.40.dist-info}/METADATA +1 -1
- {crewplus-0.2.38.dist-info → crewplus-0.2.40.dist-info}/RECORD +6 -6
- {crewplus-0.2.38.dist-info → crewplus-0.2.40.dist-info}/WHEEL +0 -0
- {crewplus-0.2.38.dist-info → crewplus-0.2.40.dist-info}/entry_points.txt +0 -0
- {crewplus-0.2.38.dist-info → crewplus-0.2.40.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from typing import List, Optional
|
|
2
2
|
import logging
|
|
3
3
|
import json
|
|
4
|
+
import asyncio
|
|
4
5
|
|
|
5
6
|
from pymilvus import DataType
|
|
6
7
|
from langchain_milvus import Milvus
|
|
@@ -177,31 +178,106 @@ class SchemaMilvus(Milvus):
|
|
|
177
178
|
continue
|
|
178
179
|
|
|
179
180
|
if isinstance(value, dict):
|
|
180
|
-
# If
|
|
181
|
-
if
|
|
182
|
-
|
|
181
|
+
# If it's a JSON object field (e.g., plant_metadata)
|
|
182
|
+
# Check if the existing value is a string, and if so, try to parse it as a dictionary
|
|
183
|
+
if key in existing_metadata and isinstance(existing_metadata[key], str):
|
|
184
|
+
try:
|
|
185
|
+
existing_metadata[key] = json.loads(existing_metadata[key])
|
|
186
|
+
except json.JSONDecodeError:
|
|
187
|
+
# If the parsing fails, it may not be a valid JSON string, treat it as a regular string
|
|
188
|
+
self.logger.warning(f"Field '{key}' could not be parsed as JSON. Overwriting as a new dict.")
|
|
189
|
+
existing_metadata[key] = {}
|
|
190
|
+
|
|
191
|
+
if key not in existing_metadata:
|
|
192
|
+
# If the field does not exist, add it
|
|
183
193
|
existing_metadata[key] = value
|
|
184
|
-
|
|
185
|
-
# If
|
|
194
|
+
elif isinstance(existing_metadata[key], dict):
|
|
195
|
+
# If the field exists and is a dictionary, recursively update the sub-fields
|
|
186
196
|
for sub_key, sub_value in value.items():
|
|
187
|
-
if isinstance(sub_value, dict)
|
|
188
|
-
|
|
197
|
+
if isinstance(sub_value, dict):
|
|
198
|
+
# If the sub-field is also a dictionary, recursively process it
|
|
199
|
+
if sub_key not in existing_metadata[key]:
|
|
200
|
+
existing_metadata[key][sub_key] = sub_value
|
|
201
|
+
else:
|
|
202
|
+
existing_metadata[key][sub_key].update(sub_value)
|
|
189
203
|
else:
|
|
204
|
+
# If the sub-field is a regular value, update it
|
|
190
205
|
existing_metadata[key][sub_key] = sub_value
|
|
206
|
+
else:
|
|
207
|
+
# If the field exists but is not a dictionary (e.g., a number or string), overwrite with the new dictionary
|
|
208
|
+
existing_metadata[key] = value
|
|
191
209
|
else:
|
|
192
|
-
#
|
|
210
|
+
# If it's a regular field, update the value
|
|
193
211
|
existing_metadata[key] = value
|
|
194
212
|
|
|
213
|
+
# Update the document's metadata
|
|
195
214
|
doc.metadata = existing_metadata
|
|
215
|
+
|
|
216
|
+
return doc
|
|
217
|
+
|
|
218
|
+
def _process_document_update(self, doc: Document, metadata_dict: dict, action: Action) -> Document:
|
|
219
|
+
"""
|
|
220
|
+
Applies the specified update operation to a single document.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
doc: The Document object to be updated.
|
|
224
|
+
metadata_dict: A dictionary containing the new data.
|
|
225
|
+
action: The type of operation to perform (UPSERT, DELETE, UPDATE, INSERT).
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
The updated Document object.
|
|
229
|
+
"""
|
|
230
|
+
pk_value = doc.metadata.get(self.primary_field)
|
|
231
|
+
text_value = doc.metadata.get(self.text_field)
|
|
232
|
+
|
|
233
|
+
if action == Action.UPSERT:
|
|
234
|
+
doc = self._handle_upsert(doc, metadata_dict)
|
|
235
|
+
elif action == Action.DELETE:
|
|
236
|
+
keys_to_delete = metadata_dict.keys()
|
|
237
|
+
doc = SchemaDocumentUpdater.delete_document_metadata(doc, list(keys_to_delete))
|
|
238
|
+
elif action == Action.UPDATE:
|
|
239
|
+
existing_metadata = doc.metadata
|
|
240
|
+
update_dict = {}
|
|
241
|
+
for key, value in metadata_dict.items():
|
|
242
|
+
if key in existing_metadata:
|
|
243
|
+
if isinstance(value, dict) and isinstance(existing_metadata.get(key), dict):
|
|
244
|
+
merged = existing_metadata[key].copy()
|
|
245
|
+
for sub_key, sub_value in value.items():
|
|
246
|
+
if sub_key in merged:
|
|
247
|
+
merged[sub_key] = sub_value
|
|
248
|
+
update_dict[key] = merged
|
|
249
|
+
else:
|
|
250
|
+
update_dict[key] = value
|
|
251
|
+
doc = SchemaDocumentUpdater.update_document_metadata(doc, update_dict)
|
|
252
|
+
elif action == Action.INSERT:
|
|
253
|
+
existing_metadata = doc.metadata
|
|
254
|
+
for key, value in metadata_dict.items():
|
|
255
|
+
if key in ['pk', 'text']:
|
|
256
|
+
continue
|
|
257
|
+
|
|
258
|
+
if isinstance(value, dict) and key in existing_metadata and isinstance(existing_metadata.get(key), dict):
|
|
259
|
+
existing_metadata[key] = {}
|
|
260
|
+
existing_metadata[key] = value
|
|
261
|
+
else:
|
|
262
|
+
existing_metadata[key] = value
|
|
263
|
+
doc.metadata = existing_metadata
|
|
264
|
+
|
|
265
|
+
if pk_value is not None:
|
|
266
|
+
doc.metadata[self.primary_field] = pk_value
|
|
267
|
+
if text_value is not None:
|
|
268
|
+
doc.metadata[self.text_field] = text_value
|
|
269
|
+
|
|
196
270
|
return doc
|
|
197
271
|
|
|
198
|
-
def update_documents_metadata(self, expr: str, metadata: str,action:Action=Action.UPSERT) -> List[Document]:
|
|
272
|
+
def update_documents_metadata(self, expr: str, metadata: str, action: Action = Action.UPSERT) -> List[Document]:
|
|
199
273
|
"""
|
|
200
274
|
Updates the metadata of documents in the Milvus vector store based on the provided expression.
|
|
275
|
+
This method uses a direct client upsert to avoid re-embedding vectors.
|
|
201
276
|
|
|
202
277
|
Args:
|
|
203
278
|
expr: Expression to filter the target documents.
|
|
204
279
|
metadata: New metadata to update the documents with.
|
|
280
|
+
action: The action to perform on the document metadata.
|
|
205
281
|
|
|
206
282
|
Returns:
|
|
207
283
|
List of updated documents.
|
|
@@ -211,67 +287,277 @@ class SchemaMilvus(Milvus):
|
|
|
211
287
|
except json.JSONDecodeError:
|
|
212
288
|
raise ValueError("Invalid JSON string for metadata")
|
|
213
289
|
|
|
214
|
-
# Retrieve documents that match the filter expression.
|
|
215
290
|
fields = self.get_fields()
|
|
291
|
+
if not fields:
|
|
292
|
+
fields = []
|
|
293
|
+
|
|
294
|
+
if isinstance(self._vector_field, list):
|
|
295
|
+
fields.extend(self._vector_field)
|
|
296
|
+
else:
|
|
297
|
+
fields.append(self._vector_field)
|
|
298
|
+
|
|
216
299
|
documents = self.search_by_metadata(expr, fields=fields, limit=5000)
|
|
217
300
|
|
|
218
|
-
updated_documents = []
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
doc = SchemaDocumentUpdater.delete_document_metadata(doc, list(keys_to_delete))
|
|
230
|
-
elif action == Action.UPDATE:
|
|
231
|
-
existing_metadata = doc.metadata
|
|
232
|
-
update_dict = {}
|
|
233
|
-
for key, value in metadata_dict.items():
|
|
234
|
-
if key in existing_metadata:
|
|
235
|
-
if isinstance(value, dict) and isinstance(existing_metadata[key], dict):
|
|
236
|
-
merged = existing_metadata[key].copy()
|
|
237
|
-
for sub_key, sub_value in value.items():
|
|
238
|
-
if sub_key in merged:
|
|
239
|
-
merged[sub_key] = sub_value
|
|
240
|
-
update_dict[key] = merged
|
|
241
|
-
else:
|
|
242
|
-
update_dict[key] = value
|
|
243
|
-
doc = SchemaDocumentUpdater.update_document_metadata(doc, update_dict)
|
|
244
|
-
elif action == Action.INSERT:
|
|
245
|
-
existing_metadata = doc.metadata
|
|
246
|
-
for key, value in metadata_dict.items():
|
|
247
|
-
if key in ['pk', 'text']:
|
|
248
|
-
continue
|
|
249
|
-
|
|
250
|
-
if isinstance(value, dict) and key in existing_metadata and isinstance(existing_metadata[key], dict):
|
|
251
|
-
existing_metadata[key] = {}
|
|
252
|
-
existing_metadata[key] = value
|
|
253
|
-
else:
|
|
254
|
-
existing_metadata[key] = value
|
|
255
|
-
doc.metadata = existing_metadata
|
|
301
|
+
updated_documents = [self._process_document_update(doc, metadata_dict, action) for doc in documents]
|
|
302
|
+
|
|
303
|
+
if updated_documents:
|
|
304
|
+
self.logger.debug(f"Upserting {len(updated_documents)} documents using direct client upsert.")
|
|
305
|
+
upsert_data = [doc.metadata for doc in updated_documents]
|
|
306
|
+
self.client.upsert(
|
|
307
|
+
collection_name=self.collection_name,
|
|
308
|
+
data=upsert_data
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
return updated_documents
|
|
256
312
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
doc.metadata[self.text_field] = text_value
|
|
313
|
+
async def aupdate_documents_metadata(self, expr: str, metadata: str, action: Action = Action.UPSERT) -> List[Document]:
|
|
314
|
+
"""
|
|
315
|
+
Asynchronously updates the metadata of documents in the Milvus vector store.
|
|
316
|
+
This method uses a direct client upsert to avoid re-embedding vectors.
|
|
262
317
|
|
|
263
|
-
|
|
318
|
+
Args:
|
|
319
|
+
expr: Expression to filter the target documents.
|
|
320
|
+
metadata: New metadata to update the documents with.
|
|
321
|
+
action: The action to perform on the document metadata.
|
|
264
322
|
|
|
265
|
-
|
|
266
|
-
|
|
323
|
+
Returns:
|
|
324
|
+
List of updated documents.
|
|
325
|
+
"""
|
|
326
|
+
try:
|
|
327
|
+
metadata_dict = json.loads(metadata)
|
|
328
|
+
except json.JSONDecodeError:
|
|
329
|
+
raise ValueError("Invalid JSON string for metadata")
|
|
267
330
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
331
|
+
fields = self.get_fields()
|
|
332
|
+
if not fields:
|
|
333
|
+
fields = []
|
|
334
|
+
|
|
335
|
+
if isinstance(self._vector_field, list):
|
|
336
|
+
fields.extend(self._vector_field)
|
|
337
|
+
else:
|
|
338
|
+
fields.append(self._vector_field)
|
|
339
|
+
|
|
340
|
+
documents = self.search_by_metadata(expr, fields=fields, limit=5000)
|
|
273
341
|
|
|
274
|
-
|
|
275
|
-
|
|
342
|
+
updated_documents = [self._process_document_update(doc, metadata_dict, action) for doc in documents]
|
|
343
|
+
|
|
344
|
+
if updated_documents:
|
|
345
|
+
self.logger.debug(f"Upserting {len(updated_documents)} documents using direct client upsert.")
|
|
346
|
+
upsert_data = [doc.metadata for doc in updated_documents]
|
|
347
|
+
|
|
348
|
+
await asyncio.to_thread(
|
|
349
|
+
self.client.upsert,
|
|
350
|
+
collection_name=self.collection_name,
|
|
351
|
+
data=upsert_data
|
|
352
|
+
)
|
|
276
353
|
|
|
277
354
|
return updated_documents
|
|
355
|
+
|
|
356
|
+
def update_documents_metadata_by_iterator(self, expr: str, metadata: str, action:Action=Action.UPSERT) -> List[Document]:
|
|
357
|
+
"""
|
|
358
|
+
【官方推荐版】
|
|
359
|
+
使用 pymilvus.Collection.query_iterator 官方推荐的迭代方式更新元数据。
|
|
360
|
+
本方法的业务逻辑(UPSERT/DELETE等)与 update_documents_metadata 方法完全一致,
|
|
361
|
+
仅数据获取方式遵循官方标准迭代器模式。
|
|
362
|
+
"""
|
|
363
|
+
try:
|
|
364
|
+
metadata_dict = json.loads(metadata)
|
|
365
|
+
except json.JSONDecodeError:
|
|
366
|
+
raise ValueError("Invalid JSON string for metadata")
|
|
367
|
+
|
|
368
|
+
fields = self.get_fields() or []
|
|
369
|
+
# 确保主键和文本字段在输出字段中
|
|
370
|
+
if 'pk' not in fields:
|
|
371
|
+
fields.append('pk')
|
|
372
|
+
text_field = getattr(self, "_text_field", "text")
|
|
373
|
+
if text_field not in fields:
|
|
374
|
+
fields.append(text_field)
|
|
375
|
+
|
|
376
|
+
# 【关键修正】: 确保在查询时也获取向量字段。
|
|
377
|
+
# self.client.upsert 操作要求提供所有非 nullable 字段,包括 vector。
|
|
378
|
+
# 因此,我们必须在迭代查询时获取它,以便在更新时能够一并传回。
|
|
379
|
+
vector_fields_to_add = self._vector_field if isinstance(self._vector_field, list) else [self._vector_field]
|
|
380
|
+
for vf in vector_fields_to_add:
|
|
381
|
+
if vf not in fields:
|
|
382
|
+
fields.append(vf)
|
|
383
|
+
|
|
384
|
+
total_updated_documents = []
|
|
385
|
+
batch_size = 1000 # 您可以根据需要调整批次大小
|
|
386
|
+
|
|
387
|
+
self.logger.info(f"Starting metadata update using 'collection.query_iterator' with batch size {batch_size}.")
|
|
388
|
+
|
|
389
|
+
# # 1. 【关键保险】: 在查询前,显式地确保集合已被加载。
|
|
390
|
+
# logger.info(f"Ensuring collection '{self.collection_name}' is loaded before querying.")
|
|
391
|
+
# self.col.load()
|
|
392
|
+
|
|
393
|
+
# 2. 【官方用法】: 创建官方推荐的迭代器
|
|
394
|
+
iterator = self.col.query_iterator(
|
|
395
|
+
batch_size=batch_size,
|
|
396
|
+
expr=expr,
|
|
397
|
+
output_fields=fields
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
batch_i = 0
|
|
401
|
+
try:
|
|
402
|
+
while True:
|
|
403
|
+
# 3. 【官方用法】: 获取下一批次
|
|
404
|
+
batch_results = iterator.next()
|
|
405
|
+
if not batch_results:
|
|
406
|
+
break # 迭代完成,正常退出
|
|
407
|
+
|
|
408
|
+
batch_i += 1
|
|
409
|
+
self.logger.info(f"Processing batch {batch_i} of {len(batch_results)} documents.")
|
|
410
|
+
|
|
411
|
+
# 4. 将 Milvus 返回的 dict 列表转换为 Langchain Document 对象
|
|
412
|
+
documents = [
|
|
413
|
+
Document(page_content=result.get(text_field, ""), metadata=result)
|
|
414
|
+
for result in batch_results
|
|
415
|
+
]
|
|
416
|
+
|
|
417
|
+
# 5. 【核心业务逻辑】: 使用公共方法处理批次中的每个文档
|
|
418
|
+
updated_documents_in_batch = [self._process_document_update(doc, metadata_dict, action) for doc in documents]
|
|
419
|
+
|
|
420
|
+
# 6. 【Upsert逻辑】:
|
|
421
|
+
if updated_documents_in_batch:
|
|
422
|
+
self.logger.debug(f"Upserting batch of {len(updated_documents_in_batch)} documents using direct client upsert.")
|
|
423
|
+
# 从更新后的 Document 对象中提取元数据字典列表
|
|
424
|
+
upsert_data = [doc.metadata for doc in updated_documents_in_batch]
|
|
425
|
+
self.client.upsert(
|
|
426
|
+
collection_name=self.collection_name,
|
|
427
|
+
data=upsert_data
|
|
428
|
+
)
|
|
429
|
+
total_updated_documents.extend(updated_documents_in_batch)
|
|
430
|
+
|
|
431
|
+
finally:
|
|
432
|
+
# 7. 【官方用法】: 确保迭代器被关闭
|
|
433
|
+
self.logger.info("Closing iterator.")
|
|
434
|
+
iterator.close()
|
|
435
|
+
|
|
436
|
+
self.logger.info(f"Iterator processing complete. Total batches processed: {batch_i}.")
|
|
437
|
+
return total_updated_documents
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def update_documents_metadata_folder_path(self, old_expr: str, metadata: str, action:Action=Action.UPSERT) -> List[Document]:
|
|
441
|
+
"""
|
|
442
|
+
专门用于更新 version_metadata.folder_path 字段的方法。
|
|
443
|
+
|
|
444
|
+
它执行一个“目录移动”逻辑:
|
|
445
|
+
1. 使用 old_expr 找出所有路径匹配的文档。
|
|
446
|
+
2. 从 metadata 中获取新的基础路径。
|
|
447
|
+
3. 将文档中 folder_path 的 old_expr 前缀替换为新的基础路径,并保留后续的子路径。
|
|
448
|
+
"""
|
|
449
|
+
# 1. 根据 old_expr 构造一个 "starts with" 查询
|
|
450
|
+
# Milvus JSON 'like' 操作符需要转义内部的双引号
|
|
451
|
+
# 但由于我们这里是变量,直接用 f-string 插入是安全的
|
|
452
|
+
expr = f"version_metadata[\"folder_path\"] like \"{old_expr}%\""
|
|
453
|
+
|
|
454
|
+
try:
|
|
455
|
+
metadata_dict = json.loads(metadata)
|
|
456
|
+
except json.JSONDecodeError:
|
|
457
|
+
raise ValueError("Invalid JSON string for metadata")
|
|
458
|
+
|
|
459
|
+
fields = self.get_fields() or []
|
|
460
|
+
# 确保关键字段都被查询出来
|
|
461
|
+
required_fields = ['pk', getattr(self, "_text_field", "text")]
|
|
462
|
+
vector_fields = self._vector_field if isinstance(self._vector_field, list) else [self._vector_field]
|
|
463
|
+
required_fields.extend(vector_fields)
|
|
464
|
+
|
|
465
|
+
for f in required_fields:
|
|
466
|
+
if f not in fields:
|
|
467
|
+
fields.append(f)
|
|
468
|
+
|
|
469
|
+
total_updated_documents = []
|
|
470
|
+
batch_size = 1000
|
|
471
|
+
|
|
472
|
+
self.logger.info(f"Starting folder path update using 'collection.query_iterator' with expr: {expr}")
|
|
473
|
+
|
|
474
|
+
# self.col.load() # 确保集合已加载
|
|
475
|
+
|
|
476
|
+
iterator = self.col.query_iterator(
|
|
477
|
+
batch_size=batch_size,
|
|
478
|
+
expr=expr,
|
|
479
|
+
output_fields=fields
|
|
480
|
+
)
|
|
481
|
+
|
|
482
|
+
batch_i = 0
|
|
483
|
+
try:
|
|
484
|
+
while True:
|
|
485
|
+
batch_results = iterator.next()
|
|
486
|
+
if not batch_results:
|
|
487
|
+
break
|
|
488
|
+
|
|
489
|
+
batch_i += 1
|
|
490
|
+
self.logger.info(f"Processing batch {batch_i} of {len(batch_results)} documents for folder path update.")
|
|
491
|
+
|
|
492
|
+
documents = [
|
|
493
|
+
Document(page_content=result.get(getattr(self, "_text_field", "text"), ""), metadata=result)
|
|
494
|
+
for result in batch_results
|
|
495
|
+
]
|
|
496
|
+
|
|
497
|
+
updated_documents_in_batch = []
|
|
498
|
+
for doc in documents:
|
|
499
|
+
# 沿用标准的 UPSERT 逻辑,但在处理 folder_path 时应用特殊规则
|
|
500
|
+
if action == Action.UPSERT:
|
|
501
|
+
existing_metadata = doc.metadata
|
|
502
|
+
for key, value in metadata_dict.items():
|
|
503
|
+
# ... (此处省略了标准的深层合并逻辑,与您已有的 update_documents_metadata 方法一致)
|
|
504
|
+
# 仅展示与 folder_path 相关的特殊处理部分
|
|
505
|
+
if isinstance(value, dict):
|
|
506
|
+
# ... (处理从数据库读出的可能是字符串的JSON)
|
|
507
|
+
if key in existing_metadata and isinstance(existing_metadata[key], str):
|
|
508
|
+
try:
|
|
509
|
+
existing_metadata[key] = json.loads(existing_metadata[key])
|
|
510
|
+
except json.JSONDecodeError:
|
|
511
|
+
existing_metadata[key] = {}
|
|
512
|
+
|
|
513
|
+
if key not in existing_metadata or not isinstance(existing_metadata[key], dict):
|
|
514
|
+
existing_metadata[key] = value
|
|
515
|
+
else:
|
|
516
|
+
# 递归更新,在这里注入我们的特殊逻辑
|
|
517
|
+
for sub_key, sub_value in value.items():
|
|
518
|
+
# 【核心特殊逻辑】
|
|
519
|
+
if key == 'version_metadata' and sub_key == 'folder_path':
|
|
520
|
+
new_folder_path_base = sub_value
|
|
521
|
+
current_folder_path = existing_metadata.get(key, {}).get(sub_key)
|
|
522
|
+
|
|
523
|
+
if current_folder_path and current_folder_path.startswith(old_expr):
|
|
524
|
+
# 移除旧前缀,保留子路径
|
|
525
|
+
sub_path = current_folder_path[len(old_expr):]
|
|
526
|
+
# 拼接新路径(确保斜杠正确)
|
|
527
|
+
new_full_path = f"{new_folder_path_base.rstrip('/')}/{sub_path.lstrip('/')}"
|
|
528
|
+
existing_metadata[key][sub_key] = new_full_path
|
|
529
|
+
self.logger.debug(f"Rewrote folder path from '{current_folder_path}' to '{new_full_path}'")
|
|
530
|
+
else:
|
|
531
|
+
# 如果不匹配,则按普通逻辑直接覆盖
|
|
532
|
+
existing_metadata[key][sub_key] = new_folder_path_base
|
|
533
|
+
|
|
534
|
+
# 其他所有字段按原逻辑递归更新
|
|
535
|
+
elif isinstance(sub_value, dict):
|
|
536
|
+
if sub_key not in existing_metadata[key]:
|
|
537
|
+
existing_metadata[key][sub_key] = sub_value
|
|
538
|
+
else:
|
|
539
|
+
existing_metadata[key][sub_key].update(sub_value)
|
|
540
|
+
else:
|
|
541
|
+
existing_metadata[key][sub_key] = sub_value
|
|
542
|
+
else:
|
|
543
|
+
existing_metadata[key] = value
|
|
544
|
+
doc.metadata = existing_metadata
|
|
545
|
+
|
|
546
|
+
# (此处可以添加对 DELETE, UPDATE, INSERT 的处理,如果需要的话)
|
|
547
|
+
|
|
548
|
+
updated_documents_in_batch.append(doc)
|
|
549
|
+
|
|
550
|
+
if updated_documents_in_batch:
|
|
551
|
+
self.logger.debug(f"Upserting batch of {len(updated_documents_in_batch)} documents with updated folder paths.")
|
|
552
|
+
upsert_data = [d.metadata for d in updated_documents_in_batch]
|
|
553
|
+
self.client.upsert(
|
|
554
|
+
collection_name=self.collection_name,
|
|
555
|
+
data=upsert_data
|
|
556
|
+
)
|
|
557
|
+
total_updated_documents.extend(updated_documents_in_batch)
|
|
558
|
+
finally:
|
|
559
|
+
self.logger.info("Closing folder path update iterator.")
|
|
560
|
+
iterator.close()
|
|
561
|
+
|
|
562
|
+
self.logger.info(f"Folder path update complete. Total batches processed: {batch_i}.")
|
|
563
|
+
return total_updated_documents
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
crewplus-0.2.
|
|
2
|
-
crewplus-0.2.
|
|
3
|
-
crewplus-0.2.
|
|
4
|
-
crewplus-0.2.
|
|
1
|
+
crewplus-0.2.40.dist-info/METADATA,sha256=x0AhiJFRYYKJ74q6ooMCAXpD3l7UAyNRz7EvaKIOOOc,5362
|
|
2
|
+
crewplus-0.2.40.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90
|
|
3
|
+
crewplus-0.2.40.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
|
|
4
|
+
crewplus-0.2.40.dist-info/licenses/LICENSE,sha256=2_NHSHRTKB_cTcT_GXgcenOCtIZku8j343mOgAguTfc,1087
|
|
5
5
|
crewplus/__init__.py,sha256=m46HkZL1Y4toD619NL47Sn2Qe084WFFSFD7e6VoYKZc,284
|
|
6
6
|
crewplus/services/__init__.py,sha256=V1CG8b2NOmRzNgQH7BPl4KVxWSYJH5vfEsW1wVErKNE,375
|
|
7
7
|
crewplus/services/azure_chat_model.py,sha256=WMSf4BDO8UcP7ZASNGRJxdTEnuWBmCRSY_4yx_VMbok,5499
|
|
@@ -14,10 +14,10 @@ crewplus/utils/schema_action.py,sha256=GDaBoVFQD1rXqrLVSMTfXYW1xcUu7eDcHsn57XBSn
|
|
|
14
14
|
crewplus/utils/schema_document_updater.py,sha256=frvffxn2vbi71fHFPoGb9hq7gH2azmmdq17p-Fumnvg,7322
|
|
15
15
|
crewplus/vectorstores/milvus/__init__.py,sha256=OeYv2rdyG7tcREIjBJPyt2TbE54NvyeRoWMe7LwopRE,245
|
|
16
16
|
crewplus/vectorstores/milvus/milvus_schema_manager.py,sha256=2IZT61LVui21Pt5Z3y8YYS2dYcwzkgUKxMq2NA0-lQE,9222
|
|
17
|
-
crewplus/vectorstores/milvus/schema_milvus.py,sha256=
|
|
17
|
+
crewplus/vectorstores/milvus/schema_milvus.py,sha256=DtHP8jHRSpLqt9ixAnJE5R4CId9NLYXxOVqRxPCEyv4,26131
|
|
18
18
|
crewplus/vectorstores/milvus/vdb_service.py,sha256=CaUMLIMeOCm2R4t5EKtAupIddFXQu0NSb8RpTkInGd4,22498
|
|
19
19
|
docs/GeminiChatModel.md,sha256=zZYyl6RmjZTUsKxxMiC9O4yV70MC4TD-IGUmWhIDBKA,8677
|
|
20
20
|
docs/ModelLoadBalancer.md,sha256=aGHES1dcXPz4c7Y8kB5-vsCNJjriH2SWmjBkSGoYKiI,4398
|
|
21
21
|
docs/VDBService.md,sha256=Dw286Rrf_fsi13jyD3Bo4Sy7nZ_G7tYm7d8MZ2j9hxk,9375
|
|
22
22
|
docs/index.md,sha256=3tlc15uR8lzFNM5WjdoZLw0Y9o1P1gwgbEnOdIBspqc,1643
|
|
23
|
-
crewplus-0.2.
|
|
23
|
+
crewplus-0.2.40.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|