更新 app.py
减少索引的冗余代码
This commit is contained in:
parent
0dad49a050
commit
eb07576567
197
app.py
197
app.py
@ -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 索引创建接口结束
|
||||
# ========== ========== ========== ========== ========== ==========
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user