dict-server/app/api/search_dict/routes.py

353 lines
11 KiB
Python
Raw 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.

import asyncio
from typing import Literal, List
from fastapi import APIRouter, Depends, HTTPException, Request, Form
from app.api.search_dict import service
from app.api.search_dict.search_schemas import SearchRequest, WordSearchResponse, SearchItemFr, SearchItemJp, \
ProverbSearchRequest
from app.api.word_comment.word_comment_schemas import CommentSet
from app.models import DefinitionJp, CommentFr, CommentJp, WordlistFr
from app.models.fr import DefinitionFr, ProverbFr
from app.models.jp import IdiomJp, WordlistJp
from app.utils.all_kana import all_in_kana
from app.utils.security import get_current_user
from app.utils.textnorm import normalize_text
dict_search = APIRouter()
async def __get_comments(
__query_word: str,
language: Literal["jp", "fr"]
) -> CommentSet:
if language == "fr":
comments = await (
CommentFr
.filter(comment_word__word=__query_word)
.select_related("user")
.order_by("-created_at")
)
commentlist = CommentSet(
comments=[
(
comment.user.id,
comment.user.name,
comment.comment_text
) for comment in comments
]
)
return commentlist
else:
comments = await (
CommentJp
.filter(comment_word__word=__query_word)
.select_related("user")
.order_by("-created_at")
)
commentlist = CommentSet(
comments=[
(
comment.user.id,
comment.user.name,
comment.comment_text,
) for comment in comments
]
)
return commentlist
@dict_search.post("/search/word", response_model=WordSearchResponse)
async def search(request: Request, body: SearchRequest, user=Depends(get_current_user)):
"""
精确搜索
:param request:
:param body: 单词是依据list返回清单中的内容动态更新对数据库text字段进行精确匹配的
:param user:
:return:
"""
redis = request.app.state.redis
query = body.query
if body.language == 'fr':
query = normalize_text(query)
word_contents = await (
DefinitionFr
.filter(word__text=query)
.prefetch_related("word")
)
if not word_contents:
raise HTTPException(status_code=404, detail="Word not found")
# 修改freq
first_word = word_contents[0].word
current_freq = first_word.freq
first_word.freq = current_freq + 1
await first_word.save()
pos_seen = set()
pos_contents = []
contents: List[SearchItemFr] = []
for wc in word_contents:
if wc.pos not in pos_seen:
pos_seen.add(wc.pos)
pos_contents.append(wc.pos)
contents.append(
SearchItemFr(
pos=wc.pos,
chi_exp=wc.meaning,
example=wc.example,
eng_explanation=wc.eng_explanation,
)
)
await service.search_time_updates(redis)
return WordSearchResponse(
query=query,
pos=pos_contents,
contents=contents,
)
else:
query_kana = all_in_kana(query)
print(query)
word_content = await DefinitionJp.filter(
word__text=query,
word__hiragana=query_kana,
).prefetch_related("word", "pos")
if not word_content:
raise HTTPException(status_code=404, detail="Word not found")
first_def = word_content[0]
first_word = first_def.word
first_word.freq = first_word.freq + 1
await first_word.save()
pos_list = await first_def.pos
pos_contents = [p.pos_type for p in pos_list]
contents: List[SearchItemJp] = []
for wc in word_content:
contents.append(
SearchItemJp(
chi_exp=wc.meaning,
example=wc.example,
)
)
await service.search_time_updates(redis)
return WordSearchResponse(
query=query,
pos=pos_contents,
contents=contents,
hiragana=query_kana,
)
# TODO 相关度排序(转换为模糊匹配)
# TODO 输入搜索框时反馈内容
@dict_search.post("/search/list/word")
async def search_word_list(query_word: SearchRequest, user=Depends(get_current_user)):
"""
检索时的提示接口
:param query_word: 用户输入的内容
:param user:
:return: 待选列表
"""
# print(query_word.query, query_word.language, query_word.sort, query_word.order)
query = query_word.query
lang = query_word.language
query, search_lang, transable = await service.detect_language(text=query)
word_contents = []
if lang == "fr":
if search_lang == "fr":
word_contents = await service.suggest_autocomplete(
query=query,
dict_lang="fr",
model=WordlistFr,
)
if not transable:
word_contents.extend(
await service.search_definition_by_meaning(
query=query,
model=DefinitionFr,
lang="en",
)
)
else:
word_contents = await service.search_definition_by_meaning(
query=query_word.query,
model=DefinitionFr,
lang="zh",
)
else:
if search_lang == "jp":
word_contents = await service.suggest_autocomplete(
query=query,
dict_lang="jp",
model=WordlistJp,
)
elif search_lang == "zh":
word_contents = []
word_contents.extend(
await service.search_definition_by_meaning(
query=query_word.query,
model=DefinitionJp,
lang="zh",
)
)
if transable:
word_contents = await service.suggest_autocomplete(
query=query,
dict_lang="jp",
model=WordlistJp,
)
else:
word_contents = await service.suggest_autocomplete(
query=query,
dict_lang="jp",
model=WordlistJp,
)
suggest_list = service.merge_word_results(word_contents)
return {"list": suggest_list}
@dict_search.post("/search/list/proverb")
async def search_proverb_list(query_word: ProverbSearchRequest, user=Depends(get_current_user)):
query, lang, transable = await service.detect_language(text=query_word.query)
query = normalize_text(query_word.query) if lang == "fr" else query_word.query
suggest_proverbs = await service.suggest_proverb(
query=query_word.query,
lang=lang,
model=ProverbFr,
search_field="search_text",
)
return {"list": suggest_proverbs}
@dict_search.post("/search/proverb")
async def search_proverb(proverb_id: int = Form(...), user=Depends(get_current_user)):
result = await service.accurate_idiom_proverb(search_id=proverb_id, model=ProverbFr,
only_fields=["id", "text", "chi_exp"])
return {"result": result}
@dict_search.post("/search/list/idiom")
async def search_idiom_list(
query_idiom: ProverbSearchRequest,
user=Depends(get_current_user)
):
"""日语成语检索接口(带语言检测与分类逻辑)"""
if query_idiom.dict_language == "fr":
raise HTTPException(status_code=400, detail="Dict language Error")
# 语言检测
mapping_query, lang, is_kangji = await service.detect_language(text=query_idiom.query)
query = query_idiom.query
# 初始化任务列表
tasks = []
# ✅ 分类逻辑入口
# --- 1⃣ 日语输入 ---
if lang == "jp":
if is_kangji:
# 有汉字的日语词 → 进行 text + search_text 双字段匹配
tasks.append(
service.suggest_proverb(
query=query,
lang="jp",
model=IdiomJp,
search_field="text",
)
)
tasks.append(
service.suggest_proverb(
query=all_in_kana(query),
lang="jp",
model=IdiomJp,
search_field="search_text",
target_field="text",
)
)
else:
# 平假名或片假名纯日文
tasks.append(
service.suggest_proverb(
query=query,
lang="jp",
model=IdiomJp,
search_field="search_text",
target_field="text",
)
)
# --- 2⃣ 中文输入 ---
elif lang == "zh":
tasks.append(
service.suggest_proverb(
query=query,
lang="zh",
model=IdiomJp,
target_field="text",
)
)
# 若输入为汉字可能存在对应的日语原型mapping_query
if is_kangji and mapping_query:
tasks.append(
service.suggest_proverb(
query=mapping_query,
lang="jp",
model=IdiomJp,
search_field="text",
)
)
tasks.append(
service.suggest_proverb(
query=all_in_kana(mapping_query),
lang="jp",
model=IdiomJp,
search_field="search_text",
)
)
# --- 3⃣ 其他语言(默认回退) ---
else:
tasks.append(
service.suggest_proverb(
query=query,
lang="jp",
model=IdiomJp,
search_field="search_text",
target_field="text",
)
)
# ✅ 并发执行任务
results = await asyncio.gather(*tasks)
# ✅ 结果合并
if not results:
return {"list": []}
merged = []
for res in results:
merged.extend(res)
# ✅ 去重(如果你希望返回唯一成语)
unique_list = {item["proverb"]: item for item in merged}.values()
return {"list": list(unique_list)}
@dict_search.post("/search/idiom")
async def search_idiom(query_id: int, user=Depends(get_current_user)):
result = await service.accurate_idiom_proverb(search_id=query_id, model=IdiomJp,
only_fields=["id", "text", "search_text", "chi_exp", "example"])
return {"result": result}