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