elastic_backend_fast/app/routes/tree/tree_base.py
2025-11-29 15:48:25 +03:30

347 lines
11 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ---------------------- modularity
from __future__ import annotations
from fastapi import APIRouter, Depends, HTTPException, Request
from app.core.map_index_reader import MapIndexReader
from app.core.response_helper import ResponseHelper
from app.routes.v1.models import (
SearchRequest,
InsertRequest,
UpdateByQueryRequest,
DeleteByQueryRequest,
)
from app.routes.tree.base_models import (
treeSearchRequest,
treeInsertRequest,
treeUpdateRequest,
)
from typing import Any, Dict, List, Optional
from app.routes.v1.elastic import (
search,
get_by_id,
insert,
update,
delete,
)
import uuid, requests, time, traceback
# ---------------------- global-params
router = APIRouter(tags=["tree"])
# ---------------------- base-Func
def get_elastic_helper(request: Request):
helper = getattr(request.app.state, "elastic_helper", None)
if helper is None:
raise RuntimeError("Elasticsearch helper not initialized")
return helper
# ---------------------- router-Func
# ----------- GET
@router.get("/ping")
async def helloworld(request: Request):
return {"message": " tree base hello-world"}
def sort_children(node):
node["children"].sort(key=lambda x: x["child_order"])
for child in node["children"]:
sort_children(child)
# خروجی بصورت تو در تو بر می گرداند ، یعنی بچه ها داخل فیلد پدر درج میشود
@router.post("/{type_name}/get")
async def search_tree(type_name: str, payload: treeSearchRequest, request: Request):
# print(f"type_name {type_name}", f"payload {payload}", sep="\n")
tcode = payload.tcode
version_key = payload.version_key
parent_id = payload.parent_id
nested = payload.nested
all_item = payload.all_item
query = payload.query
if parent_id == "0":
parent_id = 0
must = [
{"term": {"version_key": version_key}},
{"term": {"tcode": tcode}},
]
if not all_item :
must.append({"term": {"parent_id": parent_id}})
query = {
"size": 10000,
"_source": [
"id",
"title",
"parent_id",
"child_order",
"time_create",
"tcode",
"version_key",
"full_path",
"content",
],
"query": {
"bool": {
"must": must
}
},
"sort": [{"parent_id": "asc"}, {"child_order": "asc"}],
}
if not all_item:
query["query"]["bool"]["must"].append({"term": {"parent_id": parent_id}})
if tcode != "":
query["query"]["bool"]["must"].append({"term": {"tcode": tcode}})
reader = MapIndexReader(type_name) # mj_plan
helper = getattr(request.app.state, "elastic_helper", None)
if helper is None:
raise RuntimeError("Elasticsearch helper not initialized")
# "قانون تشکل‌های مدنی و احزاب"
try:
index_name = reader.get_index_name()
es_res = await helper.search(index_name, query)
# return es_res
es_res = dict(es_res)
items = []
for item in es_res["hits"]["hits"] :
item_new = {
"id": item["_id"],
"title": item["_source"]["title"],
"version_key": item["_source"]["version_key"],
"parent_id": item["_source"]["parent_id"],
"child_order": item["_source"]["child_order"],
"full_path": item["_source"]["full_path"],
"tcode": item["_source"]["tcode"],
"children": [],
}
if "content" in item["_source"] :
item_new["content"] = item["_source"]["content"]
items.append(item_new)
if not nested:
return items
# 2⃣ ساخت دیکشنری lookup بر اساس id برای دسترسی سریع
lookup = {item["id"]: item for item in items}
# 3⃣ ساخت ساختار درختی
root_nodes = []
p = 0
c = 0
for item in items:
parent_id = item["parent_id"]
if parent_id == 0:
# ریشه‌ها
root_nodes.append(item)
# print(f'parent {p}')
p += 1
else:
# print(f'parent_id {parent_id}')
parent = lookup.get(parent_id)
if parent:
parent["children"].append(item)
# print(f'children {c}')
c += 1
for root in root_nodes:
sort_children(root)
# print(f'es_res {result}', )
return root_nodes
except Exception as exc: # noqa: BLE001
raise HTTPException(status_code=404, detail=str(traceback.print_exc()))
@router.get("/{type_name}/get/{id}")
async def get_tree(type_name: str, id: str, request: Request):
doc_id = id
response = await get_by_id(type_name, doc_id, request )
return response
@router.post("/{type_name}/insert")
async def insert_tree(type_name: str, payload:treeInsertRequest, request: Request):
reader = MapIndexReader(type_name)
helper = get_elastic_helper(request)
index_name = reader.get_index_name()
if not payload.id or payload.id == None or payload.id == "None" :
payload.id = 'mp_' + uuid.uuid4().hex[:8]
if not payload.parent_id or payload.parent_id == None:
payload.parent_id = "0"
if not payload.child_order or payload.child_order == -1 :
payload.child_order = get_max_child_order(helper, index_name, payload )
else :
update_next_child_order(helper, index_name, payload )
#?????
if not payload.full_path :
payload.full_path = ''
payload_new = InsertRequest.model_construct()
payload_new.id = payload.id
payload_new.document = payload.model_dump(exclude_none=True)
response = await insert(type_name, payload_new, request )
return response
@router.post("/{type_name}/update/{id}")
async def update_tree(type_name: str, id: str, payload:treeUpdateRequest, request: Request):
# print("################################update_tree ", id)
reader = MapIndexReader(type_name)
helper = get_elastic_helper(request)
index_name = reader.get_index_name()
# print("################################update_tree ", payload)
if payload.child_order and payload.child_order == -1 :
payload.child_order = get_max_child_order(helper, index_name, payload )
payload_new = InsertRequest.model_construct()
payload_new.id = id
payload_new.document = payload.model_dump(exclude_none=True)
# print("####################################### payload_new ", payload_new)
response = await update(type_name, id, payload_new, request )
return response
return ""
@router.post("/{type_name}/delete/{id}")
async def delete_tree(type_name: str, id: str, request: Request):
response = await delete(type_name, id, request )
return response
@router.post("/{type_name}/move_prev/{to_id}/{id}")
async def move_prev_tree(type_name: str, id: str, to_id: str, request: Request):
reader = MapIndexReader(type_name)
helper = get_elastic_helper(request)
index_name = reader.get_index_name()
# node1 = await helper.get_by_id(index_name, id)
node2 = await helper.get_by_id(index_name, to_id)
#??????
if id == to_id :
message = " error id "
raise HTTPException(status_code=404, detail=message)
if not node2 :
message = "not found id "
raise HTTPException(status_code=404, detail=message)
payload=node2["_source"]
child_order = get_max_child_order(helper, index_name, payload )
payload_new = InsertRequest()
payload_new.id = id
payload_new.document = {
"parent_id" : to_id,
"child_order" : child_order
}
response = await update(type_name, id, payload_new, request )
return response
@router.post("/{type_name}/move_in/{to_id}/{id}")
async def move_in_tree(type_name: str, id: str, to_id: str, request: Request):
reader = MapIndexReader(type_name)
helper = get_elastic_helper(request)
index_name = reader.get_index_name()
# node1 = await helper.get_by_id(index_name, id)
node2 = await helper.get_by_id(index_name, to_id)
#??????
if id == to_id :
message = " error id "
raise HTTPException(status_code=404, detail=message)
if not node2 :
message = "not found id "
raise HTTPException(status_code=404, detail=message)
# child_order1 = node1["_source"]["child_order"]
child_order2 = node2["_source"]["child_order"]
# parent_id1 = node1["_source"]["parent_id"]
parent_id2 = node2["_source"]["parent_id"]
payload=node2["_source"]
update_next_child_order(helper, index_name, payload )
payload_new = InsertRequest.model_construct()
payload_new.id = id
payload_new.document = {
"parent_id" : parent_id2,
"child_order" : child_order2
}
response = await update(type_name, id, payload_new, request )
return response
async def get_max_child_order(helper, index_name, payload:treeInsertRequest) :
must = [
{"term": {"version_key": payload.version_key}},
{"term": {"tcode": payload.tcode}},
{"term": {"parent_id": payload.parent_id}}
]
query = {
"size": 1,
"_source": ["child_order"],
"query": {
"bool": {
"must": must
}
},
"sort": [{"child_order": "asc"}],
}
es_res = await helper.search(index_name, query)
child_order = 0
if len(es_res["hits"]["hits"]) > 0 :
child_order = es_res["hits"]["hits"][0]["_source"]["child_order"]
return child_order + 1
async def update_next_child_order(helper, index_name, payload:treeInsertRequest) :
must = [
{"term": {"version_key": payload.version_key}},
{"term": {"tcode": payload.tcode}},
{"term": {"parent_id": payload.parent_id}},
{"range": {"child_order": { "gte": payload.child_order}}}
]
query = {
"query": {
"bool": {
"must": must
}
},
"script": {
"source": "ctx._source.child_order = ctx._source.child_order + 1; ",
"lang": "painless"
}
}
es_res = await helper.update_by_query(index_name, query, True)
return es_res