From eb07576567b26c1000102b1929cdc7d5ba3be093 Mon Sep 17 00:00:00 2001 From: Defeng Date: Tue, 7 Jul 2026 16:17:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20app.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 减少索引的冗余代码 --- app.py | 197 +++++++++++++++++++++++---------------------------------- 1 file changed, 79 insertions(+), 118 deletions(-) diff --git a/app.py b/app.py index ab2da2b..e22a992 100644 --- a/app.py +++ b/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 索引创建接口结束 # ========== ========== ========== ========== ========== ==========