155 lines
4.2 KiB
Python
155 lines
4.2 KiB
Python
from datetime import datetime
|
|
from typing import List
|
|
|
|
from config.logging import logger
|
|
|
|
from elasticsearch.helpers import bulk
|
|
from sqlalchemy import select
|
|
from sqlalchemy.orm import Session
|
|
|
|
from config.elastic import es, BLOG_INDEX
|
|
from config.setting import settings
|
|
from models.blog import Blog, BlogCategory, BlogContent
|
|
from schemas.blog_elastic import BlogElastic, BlogSearch
|
|
|
|
|
|
def sync_all_blog(db: Session) -> bool:
|
|
if settings.ENVIRONMENT == 'dev':
|
|
return True
|
|
|
|
query = select(
|
|
Blog.id,
|
|
Blog.title,
|
|
BlogCategory.name.label("category"),
|
|
BlogContent.content.label("content"),
|
|
Blog.create_time.label("createTime"),
|
|
Blog.update_time.label("updateTime"),
|
|
).select_from(Blog).outerjoin(
|
|
BlogCategory, Blog.category_id == BlogCategory.id
|
|
).outerjoin(
|
|
BlogContent, Blog.content_id == BlogContent.id
|
|
)
|
|
results = db.execute(query).fetchall()
|
|
|
|
actions = [
|
|
{
|
|
"_op_type": "index", # 如果存在则更新,不存在则创建
|
|
"_index": BLOG_INDEX,
|
|
"_id": blog.id,
|
|
"_source": {
|
|
"title": blog.title,
|
|
"category": blog.category,
|
|
"content": blog.content.decode("utf-8"),
|
|
"createTime": blog.createTime.strftime("%Y-%m-%d %H:%M:%S"),
|
|
"updateTime": blog.updateTime.strftime("%Y-%m-%d %H:%M:%S")
|
|
}
|
|
}
|
|
for blog in results
|
|
]
|
|
|
|
success_count, errors = bulk(es, actions=actions)
|
|
|
|
if errors:
|
|
logger.error(f"部分文档同步失败,成功同步 {success_count} 条")
|
|
return False
|
|
|
|
logger.info(f"成功同步 {success_count} 条博客到 Elasticsearch")
|
|
return True
|
|
|
|
|
|
def add_blog_elastic(blog: BlogElastic) -> bool:
|
|
if settings.ENVIRONMENT == 'dev':
|
|
return True
|
|
|
|
now = datetime.now()
|
|
blog.createTime = now.strftime("%Y-%m-%d %H:%M:%S")
|
|
blog.updateTime = now.strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
es.index(
|
|
index=BLOG_INDEX,
|
|
id=blog.id,
|
|
body=blog.model_dump()
|
|
)
|
|
|
|
logger.info(f"成功新增博客 {blog.id} 到 Elasticsearch")
|
|
return True
|
|
|
|
|
|
def update_blog_elastic(blog: BlogElastic) -> bool:
|
|
if settings.ENVIRONMENT == 'dev':
|
|
return True
|
|
|
|
now = datetime.now()
|
|
blog.updateTime = now.strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
es.index(
|
|
index=BLOG_INDEX,
|
|
id=blog.id,
|
|
body=blog.model_dump()
|
|
)
|
|
|
|
logger.info(f"成功修改博客 {blog.id} 到 Elasticsearch")
|
|
return True
|
|
|
|
|
|
def delete_blog(blog_id: int) -> bool:
|
|
if settings.ENVIRONMENT == 'dev':
|
|
return True
|
|
|
|
es.delete(index=BLOG_INDEX, id=blog_id)
|
|
|
|
logger.info(f"成功删除博客 {blog_id} 到 Elasticsearch")
|
|
return True
|
|
|
|
|
|
def search_blog(keyword: str) -> List[BlogSearch]:
|
|
# 构造查询请求
|
|
body = {
|
|
"query": {
|
|
"multi_match": {
|
|
"query": keyword,
|
|
"fields": ["title^3", "content"],
|
|
"type": "phrase"
|
|
}
|
|
},
|
|
"highlight": {
|
|
"pre_tags": ["<mark>"],
|
|
"post_tags": ["</mark>"],
|
|
"fields": {
|
|
"title": {},
|
|
"content": {
|
|
"fragment_size": 150,
|
|
"number_of_fragments": 2,
|
|
"no_match_size": 100
|
|
}
|
|
}
|
|
},
|
|
"_source": ["title", "content"]
|
|
}
|
|
|
|
response = es.search(index=BLOG_INDEX, body=body)
|
|
|
|
results = []
|
|
for hit in response["hits"]["hits"]:
|
|
highlight = hit.get("highlight", {})
|
|
|
|
# 合并所有高亮片段(标题+内容)
|
|
all_highlights = []
|
|
if "title" in highlight:
|
|
all_highlights.extend(highlight["title"])
|
|
if "content" in highlight:
|
|
all_highlights.extend(highlight["content"])
|
|
|
|
# 优先使用高亮内容,否则截取内容开头
|
|
displayed_content = " ".join(all_highlights) if all_highlights \
|
|
else hit["_source"].get("content", "")[:200] + "..."
|
|
|
|
results.append(BlogSearch(
|
|
id=hit["_id"],
|
|
title=hit["_source"]["title"],
|
|
content=displayed_content,
|
|
highlight=" | ".join(all_highlights) # 合并所有高亮片段
|
|
))
|
|
|
|
return results
|