更新 app.py

减少索引的冗余代码
This commit is contained in:
Defeng 2026-07-07 16:17:03 +08:00
parent 0dad49a050
commit eb07576567

197
app.py
View File

@ -106,7 +106,7 @@ _resources: Dict[str, Any] = {
# 图谱检索和索引生成 # 图谱检索和索引生成
try: try:
from graph_search.graph_service import GraphService from graph_search.graph_service import GraphService
from neo4j_indexing import create_all_indexes, create_global_indexes, build_hybrid_indexes from neo4j_indexing import build_hybrid_indexes
INDEXING_AVAILABLE = True INDEXING_AVAILABLE = True
GRAPH_AVAILABLE = True GRAPH_AVAILABLE = True
except ImportError as e: except ImportError as e:
@ -2325,153 +2325,114 @@ class Neo4jIndexingRequest(BaseModel):
force_refresh: Optional[bool] = ( force_refresh: Optional[bool] = (
False # 是否强制刷新所有索引True=清空重建False=增量更新,只处理新节点或缺失的索引) False # 是否强制刷新所有索引True=清空重建False=增量更新,只处理新节点或缺失的索引)
) )
@app.post("/neo4j_indexing") @app.post("/neo4j_indexing")
async def neo4j_indexing(request: Neo4jIndexingRequest): async def neo4j_indexing(request: Neo4jIndexingRequest):
""" """
========== ========== ========== ========== ========== ========== Neo4j知识图谱混合索引创建接口
Neo4j 知识图谱索引创建接口
功能 Neo4j 知识图谱中的所有节点标签创建向量索引和全文索引 当前版本
并构建跨标签的全局混合(Searchable)索引 只创建 Searchable 混合索引体系
请求参数均为可选 1. searchable_vector
- neo4j_uri: Neo4j 连接 URI可选优先使用 .env 中的 NEO4J_URI - embedding语义检索
- neo4j_username: Neo4j 用户名可选优先使用 .env 中的 NEO4J_USERNAME
- neo4j_password: Neo4j 密码可选优先使用 .env 中的 NEO4J_PASSWORD
- force_refresh: 是否强制刷新所有索引默认 False
* False增量更新只处理新节点或缺失索引的节点保留现有索引
* True强制刷新清空所有索引和约束后重新创建
返回格式 2. searchable_name_fulltext
{ - 名称关键词检索
"code": 200 / 207 / 409 / 500,
"message": "...",
"data": {
"success": bool,
"message": "...",
"mode": "force_refresh" / "incremental",
"labels": [...],
"labels_count": int,
"vector_index_errors": [...],
"fulltext_index_errors": [...],
"hybrid_index_status": "success" / "failed",
"hybrid_index_error": str / null,
"elapsed_seconds": float
}
}
并发说明 3. searchable_fulltext
- 索引创建为重操作接口通过全局锁串行化 - 内容关键词检索
已有任务在跑时新请求立即返回 409不会排队等待
========== ========== ========== ========== ========== ==========
""" """
start_time = time.time() start_time = time.time()
logger.info("[neo4j_indexing] 收到索引创建请求") logger.info("[neo4j_indexing] 收到索引创建请求")
# ========== 并发保护:已有索引任务在跑时直接拒绝 ==========
# ==============================
# 防止并发创建索引
# ==============================
if _neo4j_indexing_lock.locked(): if _neo4j_indexing_lock.locked():
logger.warning("[neo4j_indexing] 已有索引任务在执行,拒绝并发请求") logger.warning(
"[neo4j_indexing] 已有任务执行,拒绝请求"
)
return JSONResponse( return JSONResponse(
status_code=409, status_code=409,
content={ content={
"code": 409, "code":409,
"message": "已有索引创建任务正在执行,请稍后再试", "message":"已有Neo4j索引创建任务正在执行",
"data": None, "data":None
}, }
) )
async with _neo4j_indexing_lock: async with _neo4j_indexing_lock:
try: try:
# ========== 检查依赖是否可用 ==========
if not INDEXING_AVAILABLE: if not INDEXING_AVAILABLE:
raise HTTPException( raise HTTPException(status_code=503,detail="Neo4j索引模块不可用")
status_code=503, kwargs = {
detail="Neo4j索引创建功能不可用请确保已正确安装相关依赖", "drop_old": True,
) }
# 如果接口传入连接参数,则覆盖
# force_refresh 在 Pydantic 模型中已有默认值 False不会是 None
force_refresh = request.force_refresh
# 构建函数调用参数,只传递非 None 的参数
kwargs = {"force_refresh": force_refresh}
if request.neo4j_uri: if request.neo4j_uri:
kwargs["neo4j_uri"] = request.neo4j_uri kwargs["uri"] = request.neo4j_uri
if request.neo4j_username: if request.neo4j_username:
kwargs["neo4j_username"] = request.neo4j_username kwargs["user"] = request.neo4j_username
if request.neo4j_password: if request.neo4j_password:
kwargs["neo4j_password"] = request.neo4j_password kwargs["password"] = request.neo4j_password
# ========== 第一步:按标签创建向量/全文索引 ========== result = await asyncio.to_thread(build_hybrid_indexes,**kwargs)
# 同步操作放到线程池执行,避免阻塞事件循环 elapsed = time.time()-start_time
result = await asyncio.to_thread(create_all_indexes, **kwargs) if result.get("success"):
logger.info(
# ========== 第二步:构建全局混合(Searchable)索引 ========== f"[neo4j_indexing] 创建成功 "
# 单独捕获异常:即使这一步失败,也不能丢掉 create_all_indexes 的成功结果 f"耗时:{elapsed:.2f}s")
hybrid_status = "success" return {
hybrid_error = None "code":200,
try: "message":"success",
hybrid_result = await asyncio.to_thread(build_hybrid_indexes, driver) "data":{
if not hybrid_result.get("success", False): "success":True,
hybrid_status = "failed" "message":
hybrid_error = hybrid_result.get("message", "未知错误") result.get("message","混合索引创建完成"),
logger.error(f"[neo4j_indexing] build_hybrid_indexes 失败: {hybrid_error}") "searchable_added":
except Exception as e: result.get("searchable_added",0),
hybrid_status = "failed" "elapsed_seconds":
hybrid_error = str(e) round(elapsed,2)
logger.error(f"[neo4j_indexing] build_hybrid_indexes 异常: {e}", exc_info=True) }
}
elapsed = time.time() - start_time else:
logger.error(
# ========== 构建响应 ========== f"[neo4j_indexing] 创建失败:"
overall_success = result["success"] and hybrid_status == "success" f"{result.get('message')}"
response_data = { )
"success": overall_success, return {
"message": result["message"], "code":207,
"mode": result.get("mode", "unknown"), "message":"partial success",
"labels": result["labels"], "data":{
"labels_count": len(result["labels"]), "success":False,
"vector_index_errors": result["vector_index_errors"], "message":
"fulltext_index_errors": result["fulltext_index_errors"], result.get("message","未知错误"),
"hybrid_index_status": hybrid_status, "searchable_added":0,
"hybrid_index_error": hybrid_error, "elapsed_seconds":round(elapsed,2)
"elapsed_seconds": round(elapsed, 2), }
} }
logger.info(f"[neo4j_indexing] 索引创建完成,耗时: {elapsed:.2f}")
return {
"code": 200 if overall_success else 207, # 207 表示部分成功
"message": "success",
"data": response_data,
}
except HTTPException: except HTTPException:
raise raise
except Exception as e: except Exception as e:
elapsed = time.time() - start_time elapsed=time.time()-start_time
error_msg = f"索引创建失败: {str(e)}" error_msg=f"索引创建失败:{str(e)}"
logger.error(f"[neo4j_indexing] {error_msg} (耗时: {elapsed:.2f}秒)", exc_info=True) logger.error(error_msg,exc_info=True)
return { return {
"code": 500, "code":500,
"message": "索引创建失败", "message":"索引创建失败",
"data": { "data":{
"success": False, "success":False,
"message": error_msg, "message":error_msg,
"mode": "error", "elapsed_seconds":
"labels": [], round(elapsed,2)
"labels_count": 0, }
"vector_index_errors": [],
"fulltext_index_errors": [],
"hybrid_index_status": "error",
"hybrid_index_error": error_msg,
"elapsed_seconds": round(elapsed, 2),
},
} }
# ========== ========== ========== ========== ========== ========== # ========== ========== ========== ========== ========== ==========
# Neo4j 索引创建接口结束 # Neo4j 索引创建接口结束
# ========== ========== ========== ========== ========== ========== # ========== ========== ========== ========== ========== ==========