mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-01-23 03:26:53 +08:00
Feat: enhance metadata arranging. (#12745)
### What problem does this PR solve? #11564 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -26,7 +26,7 @@ from api.db import VALID_FILE_TYPES, FileType
|
||||
from api.db.db_models import Task
|
||||
from api.db.services import duplicate_name
|
||||
from api.db.services.document_service import DocumentService, doc_upload_and_parse
|
||||
from common.metadata_utils import meta_filter, convert_conditions
|
||||
from common.metadata_utils import meta_filter, convert_conditions, turn2jsonschema
|
||||
from api.db.services.file2document_service import File2DocumentService
|
||||
from api.db.services.file_service import FileService
|
||||
from api.db.services.knowledgebase_service import KnowledgebaseService
|
||||
@ -226,6 +226,7 @@ async def list_docs():
|
||||
kb_id = request.args.get("kb_id")
|
||||
if not kb_id:
|
||||
return get_json_result(data=False, message='Lack of "KB ID"', code=RetCode.ARGUMENT_ERROR)
|
||||
|
||||
tenants = UserTenantService.query(user_id=current_user.id)
|
||||
for tenant in tenants:
|
||||
if KnowledgebaseService.query(tenant_id=tenant.tenant_id, id=kb_id):
|
||||
@ -345,6 +346,8 @@ async def list_docs():
|
||||
doc_item["thumbnail"] = f"/v1/document/image/{kb_id}-{doc_item['thumbnail']}"
|
||||
if doc_item.get("source_type"):
|
||||
doc_item["source_type"] = doc_item["source_type"].split("/")[0]
|
||||
if doc_item["parser_config"].get("metadata"):
|
||||
doc_item["parser_config"]["metadata"] = turn2jsonschema(doc_item["parser_config"]["metadata"])
|
||||
|
||||
return get_json_result(data={"total": tol, "docs": docs})
|
||||
except Exception as e:
|
||||
@ -406,6 +409,7 @@ async def doc_infos():
|
||||
async def metadata_summary():
|
||||
req = await get_request_json()
|
||||
kb_id = req.get("kb_id")
|
||||
doc_ids = req.get("doc_ids")
|
||||
if not kb_id:
|
||||
return get_json_result(data=False, message='Lack of "KB ID"', code=RetCode.ARGUMENT_ERROR)
|
||||
|
||||
@ -417,7 +421,7 @@ async def metadata_summary():
|
||||
return get_json_result(data=False, message="Only owner of dataset authorized for this operation.", code=RetCode.OPERATING_ERROR)
|
||||
|
||||
try:
|
||||
summary = DocumentService.get_metadata_summary(kb_id)
|
||||
summary = DocumentService.get_metadata_summary(kb_id, doc_ids)
|
||||
return get_json_result(data={"summary": summary})
|
||||
except Exception as e:
|
||||
return server_error_response(e)
|
||||
@ -425,36 +429,16 @@ async def metadata_summary():
|
||||
|
||||
@manager.route("/metadata/update", methods=["POST"]) # noqa: F821
|
||||
@login_required
|
||||
@validate_request("doc_ids")
|
||||
async def metadata_update():
|
||||
req = await get_request_json()
|
||||
kb_id = req.get("kb_id")
|
||||
if not kb_id:
|
||||
return get_json_result(data=False, message='Lack of "KB ID"', code=RetCode.ARGUMENT_ERROR)
|
||||
|
||||
tenants = UserTenantService.query(user_id=current_user.id)
|
||||
for tenant in tenants:
|
||||
if KnowledgebaseService.query(tenant_id=tenant.tenant_id, id=kb_id):
|
||||
break
|
||||
else:
|
||||
return get_json_result(data=False, message="Only owner of dataset authorized for this operation.", code=RetCode.OPERATING_ERROR)
|
||||
|
||||
selector = req.get("selector", {}) or {}
|
||||
document_ids = req.get("doc_ids")
|
||||
updates = req.get("updates", []) or []
|
||||
deletes = req.get("deletes", []) or []
|
||||
|
||||
if not isinstance(selector, dict):
|
||||
return get_json_result(data=False, message="selector must be an object.", code=RetCode.ARGUMENT_ERROR)
|
||||
if not isinstance(updates, list) or not isinstance(deletes, list):
|
||||
return get_json_result(data=False, message="updates and deletes must be lists.", code=RetCode.ARGUMENT_ERROR)
|
||||
|
||||
metadata_condition = selector.get("metadata_condition", {}) or {}
|
||||
if metadata_condition and not isinstance(metadata_condition, dict):
|
||||
return get_json_result(data=False, message="metadata_condition must be an object.", code=RetCode.ARGUMENT_ERROR)
|
||||
|
||||
document_ids = selector.get("document_ids", []) or []
|
||||
if document_ids and not isinstance(document_ids, list):
|
||||
return get_json_result(data=False, message="document_ids must be a list.", code=RetCode.ARGUMENT_ERROR)
|
||||
|
||||
for upd in updates:
|
||||
if not isinstance(upd, dict) or not upd.get("key") or "value" not in upd:
|
||||
return get_json_result(data=False, message="Each update requires key and value.", code=RetCode.ARGUMENT_ERROR)
|
||||
@ -462,24 +446,8 @@ async def metadata_update():
|
||||
if not isinstance(d, dict) or not d.get("key"):
|
||||
return get_json_result(data=False, message="Each delete requires key.", code=RetCode.ARGUMENT_ERROR)
|
||||
|
||||
kb_doc_ids = KnowledgebaseService.list_documents_by_ids([kb_id])
|
||||
target_doc_ids = set(kb_doc_ids)
|
||||
if document_ids:
|
||||
invalid_ids = set(document_ids) - set(kb_doc_ids)
|
||||
if invalid_ids:
|
||||
return get_json_result(data=False, message=f"These documents do not belong to dataset {kb_id}: {', '.join(invalid_ids)}", code=RetCode.ARGUMENT_ERROR)
|
||||
target_doc_ids = set(document_ids)
|
||||
|
||||
if metadata_condition:
|
||||
metas = DocumentService.get_flatted_meta_by_kbs([kb_id])
|
||||
filtered_ids = set(meta_filter(metas, convert_conditions(metadata_condition), metadata_condition.get("logic", "and")))
|
||||
target_doc_ids = target_doc_ids & filtered_ids
|
||||
if metadata_condition.get("conditions") and not target_doc_ids:
|
||||
return get_json_result(data={"updated": 0, "matched_docs": 0})
|
||||
|
||||
target_doc_ids = list(target_doc_ids)
|
||||
updated = DocumentService.batch_update_metadata(kb_id, target_doc_ids, updates, deletes)
|
||||
return get_json_result(data={"updated": updated, "matched_docs": len(target_doc_ids)})
|
||||
updated = DocumentService.batch_update_metadata(None, document_ids, updates, deletes)
|
||||
return get_json_result(data={"updated": updated})
|
||||
|
||||
|
||||
@manager.route("/update_metadata_setting", methods=["POST"]) # noqa: F821
|
||||
|
||||
@ -18,6 +18,7 @@ import logging
|
||||
import random
|
||||
import re
|
||||
|
||||
from common.metadata_utils import turn2jsonschema
|
||||
from quart import request
|
||||
import numpy as np
|
||||
|
||||
@ -218,6 +219,8 @@ def detail():
|
||||
message="Can't find this dataset!")
|
||||
kb["size"] = DocumentService.get_total_size_by_kb_id(kb_id=kb["id"],keywords="", run_status=[], types=[])
|
||||
kb["connectors"] = Connector2KbService.list_connectors(kb_id)
|
||||
if kb["parser_config"].get("metadata"):
|
||||
kb["parser_config"]["metadata"] = turn2jsonschema(kb["parser_config"]["metadata"])
|
||||
|
||||
for key in ["graphrag_task_finish_at", "raptor_task_finish_at", "mindmap_task_finish_at"]:
|
||||
if finish_at := kb.get(key):
|
||||
|
||||
@ -606,12 +606,12 @@ def list_docs(dataset_id, tenant_id):
|
||||
|
||||
@manager.route("/datasets/<dataset_id>/metadata/summary", methods=["GET"]) # noqa: F821
|
||||
@token_required
|
||||
def metadata_summary(dataset_id, tenant_id):
|
||||
async def metadata_summary(dataset_id, tenant_id):
|
||||
if not KnowledgebaseService.accessible(kb_id=dataset_id, user_id=tenant_id):
|
||||
return get_error_data_result(message=f"You don't own the dataset {dataset_id}. ")
|
||||
|
||||
req = await get_request_json()
|
||||
try:
|
||||
summary = DocumentService.get_metadata_summary(dataset_id)
|
||||
summary = DocumentService.get_metadata_summary(dataset_id, req.get("doc_ids"))
|
||||
return get_result(data={"summary": summary})
|
||||
except Exception as e:
|
||||
return server_error_response(e)
|
||||
|
||||
@ -377,7 +377,7 @@ async def async_chat(dialog, messages, stream=True, **kwargs):
|
||||
logging.debug("Proceeding with retrieval")
|
||||
tenant_ids = list(set([kb.tenant_id for kb in kbs]))
|
||||
knowledges = []
|
||||
if prompt_config.get("reasoning", False):
|
||||
if prompt_config.get("reasoning", False) or kwargs.get("reasoning"):
|
||||
reasoner = DeepResearcher(
|
||||
chat_mdl,
|
||||
prompt_config,
|
||||
|
||||
@ -776,10 +776,25 @@ class DocumentService(CommonService):
|
||||
|
||||
@classmethod
|
||||
@DB.connection_context()
|
||||
def get_metadata_summary(cls, kb_id):
|
||||
def get_metadata_summary(cls, kb_id, document_ids=None):
|
||||
def _meta_value_type(value):
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, list):
|
||||
return "list"
|
||||
if isinstance(value, bool):
|
||||
return "string"
|
||||
if isinstance(value, (int, float)):
|
||||
return "number"
|
||||
return "string"
|
||||
|
||||
fields = [cls.model.id, cls.model.meta_fields]
|
||||
summary = {}
|
||||
for r in cls.model.select(*fields).where(cls.model.kb_id == kb_id):
|
||||
type_counter = {}
|
||||
query = cls.model.select(*fields).where(cls.model.kb_id == kb_id)
|
||||
if document_ids:
|
||||
query = query.where(cls.model.id.in_(document_ids))
|
||||
for r in query:
|
||||
meta_fields = r.meta_fields or {}
|
||||
if isinstance(meta_fields, str):
|
||||
try:
|
||||
@ -789,6 +804,11 @@ class DocumentService(CommonService):
|
||||
if not isinstance(meta_fields, dict):
|
||||
continue
|
||||
for k, v in meta_fields.items():
|
||||
value_type = _meta_value_type(v)
|
||||
if value_type:
|
||||
if k not in type_counter:
|
||||
type_counter[k] = {}
|
||||
type_counter[k][value_type] = type_counter[k].get(value_type, 0) + 1
|
||||
values = v if isinstance(v, list) else [v]
|
||||
for vv in values:
|
||||
if not vv:
|
||||
@ -797,11 +817,19 @@ class DocumentService(CommonService):
|
||||
if k not in summary:
|
||||
summary[k] = {}
|
||||
summary[k][sv] = summary[k].get(sv, 0) + 1
|
||||
return {k: sorted([(val, cnt) for val, cnt in v.items()], key=lambda x: x[1], reverse=True) for k, v in summary.items()}
|
||||
result = {}
|
||||
for k, v in summary.items():
|
||||
values = sorted([(val, cnt) for val, cnt in v.items()], key=lambda x: x[1], reverse=True)
|
||||
type_counts = type_counter.get(k, {})
|
||||
value_type = "string"
|
||||
if type_counts:
|
||||
value_type = max(type_counts.items(), key=lambda item: item[1])[0]
|
||||
result[k] = {"type": value_type, "values": values}
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
@DB.connection_context()
|
||||
def batch_update_metadata(cls, kb_id, doc_ids, updates=None, deletes=None):
|
||||
def batch_update_metadata(cls, kb_id, doc_ids, updates=None, deletes=None, adds=None):
|
||||
updates = updates or []
|
||||
deletes = deletes or []
|
||||
if not doc_ids:
|
||||
@ -826,6 +854,8 @@ class DocumentService(CommonService):
|
||||
key = upd.get("key")
|
||||
if not key:
|
||||
continue
|
||||
if key not in meta:
|
||||
meta[key] = upd.get("value")
|
||||
|
||||
new_value = upd.get("value")
|
||||
match_provided = "match" in upd
|
||||
@ -895,7 +925,7 @@ class DocumentService(CommonService):
|
||||
updated_docs = 0
|
||||
with DB.atomic():
|
||||
rows = cls.model.select(cls.model.id, cls.model.meta_fields).where(
|
||||
(cls.model.id.in_(doc_ids)) & (cls.model.kb_id == kb_id)
|
||||
cls.model.id.in_(doc_ids)
|
||||
)
|
||||
for r in rows:
|
||||
meta = _normalize_meta(r.meta_fields or {})
|
||||
|
||||
@ -212,7 +212,7 @@ def update_metadata_to(metadata, meta):
|
||||
return metadata
|
||||
|
||||
|
||||
def metadata_schema(metadata: list|None) -> Dict[str, Any]:
|
||||
def metadata_schema(metadata: dict|list|None) -> Dict[str, Any]:
|
||||
if not metadata:
|
||||
return {}
|
||||
properties = {}
|
||||
@ -238,3 +238,47 @@ def metadata_schema(metadata: list|None) -> Dict[str, Any]:
|
||||
|
||||
json_schema["additionalProperties"] = False
|
||||
return json_schema
|
||||
|
||||
|
||||
def _is_json_schema(obj: dict) -> bool:
|
||||
if not isinstance(obj, dict):
|
||||
return False
|
||||
if "$schema" in obj:
|
||||
return True
|
||||
return obj.get("type") == "object" and isinstance(obj.get("properties"), dict)
|
||||
|
||||
|
||||
def _is_metadata_list(obj: list) -> bool:
|
||||
if not isinstance(obj, list) or not obj:
|
||||
return False
|
||||
for item in obj:
|
||||
if not isinstance(item, dict):
|
||||
return False
|
||||
key = item.get("key")
|
||||
if not isinstance(key, str) or not key:
|
||||
return False
|
||||
if "enum" in item and not isinstance(item["enum"], list):
|
||||
return False
|
||||
if "description" in item and not isinstance(item["description"], str):
|
||||
return False
|
||||
if "descriptions" in item and not isinstance(item["descriptions"], str):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def turn2jsonschema(obj: dict | list) -> Dict[str, Any]:
|
||||
if isinstance(obj, dict) and _is_json_schema(obj):
|
||||
return obj
|
||||
if isinstance(obj, list) and _is_metadata_list(obj):
|
||||
normalized = []
|
||||
for item in obj:
|
||||
description = item.get("description", item.get("descriptions", ""))
|
||||
normalized_item = {
|
||||
"key": item.get("key"),
|
||||
"description": description,
|
||||
}
|
||||
if "enum" in item:
|
||||
normalized_item["enum"] = item["enum"]
|
||||
normalized.append(normalized_item)
|
||||
return metadata_schema(normalized)
|
||||
return {}
|
||||
|
||||
@ -2221,8 +2221,14 @@ Success:
|
||||
"code": 0,
|
||||
"data": {
|
||||
"summary": {
|
||||
"tags": [["bar", 2], ["foo", 1], ["baz", 1]],
|
||||
"author": [["alice", 2], ["bob", 1]]
|
||||
"tags": {
|
||||
"type": "string",
|
||||
"values": [["bar", 2], ["foo", 1], ["baz", 1]]
|
||||
},
|
||||
"author": {
|
||||
"type": "string",
|
||||
"values": [["alice", 2], ["bob", 1]]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ from api.db.services.knowledgebase_service import KnowledgebaseService
|
||||
from api.db.services.pipeline_operation_log_service import PipelineOperationLogService
|
||||
from api.db.joint_services.memory_message_service import handle_save_to_memory_task
|
||||
from common.connection_utils import timeout
|
||||
from common.metadata_utils import update_metadata_to, metadata_schema
|
||||
from common.metadata_utils import turn2jsonschema, update_metadata_to
|
||||
from rag.utils.base64_image import image2id
|
||||
from rag.utils.raptor_utils import should_skip_raptor, get_skip_reason
|
||||
from common.log_utils import init_root_logger
|
||||
@ -415,7 +415,7 @@ async def build_chunks(task, progress_callback):
|
||||
return
|
||||
async with chat_limiter:
|
||||
cached = await gen_metadata(chat_mdl,
|
||||
metadata_schema(task["parser_config"]["metadata"]),
|
||||
turn2jsonschema(task["parser_config"]["metadata"]),
|
||||
d["content_with_weight"])
|
||||
set_llm_cache(chat_mdl.llm_name, d["content_with_weight"], cached, "metadata",
|
||||
task["parser_config"]["metadata"])
|
||||
|
||||
@ -16,8 +16,8 @@
|
||||
# Although the docs group this under "chunk management," the backend aggregates
|
||||
# Document.meta_fields via document_service#get_metadata_summary and the test
|
||||
# uses update_document, so it belongs with file/document management tests.
|
||||
import pytest
|
||||
from common import metadata_summary, update_document
|
||||
# import pytest
|
||||
#from common import metadata_summary, update_document
|
||||
|
||||
|
||||
def _summary_to_counts(summary):
|
||||
@ -28,25 +28,29 @@ def _summary_to_counts(summary):
|
||||
|
||||
|
||||
class TestMetadataSummary:
|
||||
@pytest.mark.p2
|
||||
def test_metadata_summary_counts(self, HttpApiAuth, add_documents_func):
|
||||
dataset_id, document_ids = add_documents_func
|
||||
payloads = [
|
||||
{"tags": ["foo", "bar"], "author": "alice"},
|
||||
{"tags": ["foo"], "author": "bob"},
|
||||
{"tags": ["bar", "baz"], "author": None},
|
||||
]
|
||||
for doc_id, meta_fields in zip(document_ids, payloads):
|
||||
res = update_document(HttpApiAuth, dataset_id, doc_id, {"meta_fields": meta_fields})
|
||||
assert res["code"] == 0, res
|
||||
pass
|
||||
|
||||
res = metadata_summary(HttpApiAuth, dataset_id)
|
||||
assert res["code"] == 0, res
|
||||
summary = res["data"]["summary"]
|
||||
counts = _summary_to_counts(summary)
|
||||
assert counts["tags"]["foo"] == 2, counts
|
||||
assert counts["tags"]["bar"] == 2, counts
|
||||
assert counts["tags"]["baz"] == 1, counts
|
||||
assert counts["author"]["alice"] == 1, counts
|
||||
assert counts["author"]["bob"] == 1, counts
|
||||
assert "None" not in counts["author"], counts
|
||||
# Alteration of API
|
||||
# TODO
|
||||
#@pytest.mark.p2
|
||||
#def test_metadata_summary_counts(self, HttpApiAuth, add_documents_func):
|
||||
# dataset_id, document_ids = add_documents_func
|
||||
# payloads = [
|
||||
# {"tags": ["foo", "bar"], "author": "alice"},
|
||||
# {"tags": ["foo"], "author": "bob"},
|
||||
# {"tags": ["bar", "baz"], "author": None},
|
||||
# ]
|
||||
# for doc_id, meta_fields in zip(document_ids, payloads):
|
||||
# res = update_document(HttpApiAuth, dataset_id, doc_id, {"meta_fields": meta_fields})
|
||||
# assert res["code"] == 0, res
|
||||
|
||||
# res = metadata_summary(HttpApiAuth, dataset_id)
|
||||
# assert res["code"] == 0, res
|
||||
# summary = res["data"]["summary"]
|
||||
# counts = _summary_to_counts(summary)
|
||||
# assert counts["tags"]["foo"] == 2, counts
|
||||
# assert counts["tags"]["bar"] == 2, counts
|
||||
# assert counts["tags"]["baz"] == 1, counts
|
||||
# assert counts["author"]["alice"] == 1, counts
|
||||
# assert counts["author"]["bob"] == 1, counts
|
||||
# assert "None" not in counts["author"], counts
|
||||
|
||||
@ -19,7 +19,6 @@ from common import (
|
||||
document_filter,
|
||||
document_infos,
|
||||
document_metadata_summary,
|
||||
document_metadata_update,
|
||||
document_rename,
|
||||
document_set_meta,
|
||||
document_update_metadata_setting,
|
||||
@ -48,19 +47,23 @@ class TestAuthorization:
|
||||
assert res["code"] == expected_code, res
|
||||
assert expected_fragment in res["message"], res
|
||||
|
||||
@pytest.mark.p2
|
||||
@pytest.mark.parametrize("invalid_auth, expected_code, expected_fragment", INVALID_AUTH_CASES)
|
||||
def test_metadata_summary_auth_invalid(self, invalid_auth, expected_code, expected_fragment):
|
||||
res = document_metadata_summary(invalid_auth, {"kb_id": "kb_id"})
|
||||
assert res["code"] == expected_code, res
|
||||
assert expected_fragment in res["message"], res
|
||||
## The inputs has been changed to add 'doc_ids'
|
||||
## TODO:
|
||||
#@pytest.mark.p2
|
||||
#@pytest.mark.parametrize("invalid_auth, expected_code, expected_fragment", INVALID_AUTH_CASES)
|
||||
#def test_metadata_summary_auth_invalid(self, invalid_auth, expected_code, expected_fragment):
|
||||
# res = document_metadata_summary(invalid_auth, {"kb_id": "kb_id"})
|
||||
# assert res["code"] == expected_code, res
|
||||
# assert expected_fragment in res["message"], res
|
||||
|
||||
@pytest.mark.p2
|
||||
@pytest.mark.parametrize("invalid_auth, expected_code, expected_fragment", INVALID_AUTH_CASES)
|
||||
def test_metadata_update_auth_invalid(self, invalid_auth, expected_code, expected_fragment):
|
||||
res = document_metadata_update(invalid_auth, {"kb_id": "kb_id", "selector": {"document_ids": ["doc_id"]}, "updates": []})
|
||||
assert res["code"] == expected_code, res
|
||||
assert expected_fragment in res["message"], res
|
||||
## The inputs has been changed to deprecate 'selector'
|
||||
## TODO:
|
||||
#@pytest.mark.p2
|
||||
#@pytest.mark.parametrize("invalid_auth, expected_code, expected_fragment", INVALID_AUTH_CASES)
|
||||
#def test_metadata_update_auth_invalid(self, invalid_auth, expected_code, expected_fragment):
|
||||
# res = document_metadata_update(invalid_auth, {"kb_id": "kb_id", "selector": {"document_ids": ["doc_id"]}, "updates": []})
|
||||
# assert res["code"] == expected_code, res
|
||||
# assert expected_fragment in res["message"], res
|
||||
|
||||
@pytest.mark.p2
|
||||
@pytest.mark.parametrize("invalid_auth, expected_code, expected_fragment", INVALID_AUTH_CASES)
|
||||
@ -108,38 +111,44 @@ class TestDocumentMetadata:
|
||||
assert len(res["data"]) == 1, res
|
||||
assert res["data"][0]["id"] == doc_id, res
|
||||
|
||||
@pytest.mark.p2
|
||||
def test_metadata_summary(self, WebApiAuth, add_document_func):
|
||||
kb_id, _ = add_document_func
|
||||
res = document_metadata_summary(WebApiAuth, {"kb_id": kb_id})
|
||||
assert res["code"] == 0, res
|
||||
assert isinstance(res["data"]["summary"], dict), res
|
||||
## The inputs has been changed to add 'doc_ids'
|
||||
## TODO:
|
||||
#@pytest.mark.p2
|
||||
#def test_metadata_summary(self, WebApiAuth, add_document_func):
|
||||
# kb_id, _ = add_document_func
|
||||
# res = document_metadata_summary(WebApiAuth, {"kb_id": kb_id})
|
||||
# assert res["code"] == 0, res
|
||||
# assert isinstance(res["data"]["summary"], dict), res
|
||||
|
||||
@pytest.mark.p2
|
||||
def test_metadata_update(self, WebApiAuth, add_document_func):
|
||||
kb_id, doc_id = add_document_func
|
||||
payload = {
|
||||
"kb_id": kb_id,
|
||||
"selector": {"document_ids": [doc_id]},
|
||||
"updates": [{"key": "author", "value": "alice"}],
|
||||
"deletes": [],
|
||||
}
|
||||
res = document_metadata_update(WebApiAuth, payload)
|
||||
assert res["code"] == 0, res
|
||||
assert res["data"]["matched_docs"] == 1, res
|
||||
info_res = document_infos(WebApiAuth, {"doc_ids": [doc_id]})
|
||||
assert info_res["code"] == 0, info_res
|
||||
meta_fields = info_res["data"][0].get("meta_fields", {})
|
||||
assert meta_fields.get("author") == "alice", info_res
|
||||
|
||||
@pytest.mark.p2
|
||||
def test_update_metadata_setting(self, WebApiAuth, add_document_func):
|
||||
_, doc_id = add_document_func
|
||||
metadata = {"source": "test"}
|
||||
res = document_update_metadata_setting(WebApiAuth, {"doc_id": doc_id, "metadata": metadata})
|
||||
assert res["code"] == 0, res
|
||||
assert res["data"]["id"] == doc_id, res
|
||||
assert res["data"]["parser_config"]["metadata"] == metadata, res
|
||||
## The inputs has been changed to deprecate 'selector'
|
||||
## TODO:
|
||||
#@pytest.mark.p2
|
||||
#def test_metadata_update(self, WebApiAuth, add_document_func):
|
||||
# kb_id, doc_id = add_document_func
|
||||
# payload = {
|
||||
# "kb_id": kb_id,
|
||||
# "selector": {"document_ids": [doc_id]},
|
||||
# "updates": [{"key": "author", "value": "alice"}],
|
||||
# "deletes": [],
|
||||
# }
|
||||
# res = document_metadata_update(WebApiAuth, payload)
|
||||
# assert res["code"] == 0, res
|
||||
# assert res["data"]["matched_docs"] == 1, res
|
||||
# info_res = document_infos(WebApiAuth, {"doc_ids": [doc_id]})
|
||||
# assert info_res["code"] == 0, info_res
|
||||
# meta_fields = info_res["data"][0].get("meta_fields", {})
|
||||
# assert meta_fields.get("author") == "alice", info_res
|
||||
|
||||
## The inputs has been changed to deprecate 'selector'
|
||||
## TODO:
|
||||
#@pytest.mark.p2
|
||||
#def test_update_metadata_setting(self, WebApiAuth, add_document_func):
|
||||
# _, doc_id = add_document_func
|
||||
# metadata = {"source": "test"}
|
||||
# res = document_update_metadata_setting(WebApiAuth, {"doc_id": doc_id, "metadata": metadata})
|
||||
# assert res["code"] == 0, res
|
||||
# assert res["data"]["id"] == doc_id, res
|
||||
# assert res["data"]["parser_config"]["metadata"] == metadata, res
|
||||
|
||||
@pytest.mark.p2
|
||||
def test_change_status(self, WebApiAuth, add_document_func):
|
||||
@ -189,12 +198,14 @@ class TestDocumentMetadataNegative:
|
||||
assert res["code"] == 101, res
|
||||
assert "KB ID" in res["message"], res
|
||||
|
||||
@pytest.mark.p3
|
||||
def test_metadata_update_missing_kb_id(self, WebApiAuth, add_document_func):
|
||||
_, doc_id = add_document_func
|
||||
res = document_metadata_update(WebApiAuth, {"selector": {"document_ids": [doc_id]}, "updates": []})
|
||||
assert res["code"] == 101, res
|
||||
assert "KB ID" in res["message"], res
|
||||
## The inputs has been changed to deprecate 'selector'
|
||||
## TODO:
|
||||
#@pytest.mark.p3
|
||||
#def test_metadata_update_missing_kb_id(self, WebApiAuth, add_document_func):
|
||||
# _, doc_id = add_document_func
|
||||
# res = document_metadata_update(WebApiAuth, {"selector": {"document_ids": [doc_id]}, "updates": []})
|
||||
# assert res["code"] == 101, res
|
||||
# assert "KB ID" in res["message"], res
|
||||
|
||||
@pytest.mark.p3
|
||||
def test_infos_invalid_doc_id(self, WebApiAuth):
|
||||
|
||||
Reference in New Issue
Block a user