# Summary
# Cues
# Notes
执行链路
1. HTTP 请求入口 - api_server.py:29-67
@app.route("/search", methods=["GET"])
def search_routes():
query = request.args.get("q", "") # 获取查询参数 "兰州路线"
size = int(request.args.get("size", 10))
results = search_engine.search(query, size) # 调用搜索引擎
2. 搜索引擎主函数 - route_search.py:171-183
def search(self, query: str, size: int = 10) -> List[Dict]:
search_body = self.build_search_query(query) # 构建查询
response = self.es.search(index=self.index_name, body=search_body, size=size)
3. 构建搜索查询 - route_search.py:102-169
def build_search_query(self, query: str) -> Dict:
# 3.1 提取目的地信息
destination_name, destination_code = self.extract_destination_from_query(query)
# 返回: ("兰州", "620100")
4. 目的地提取 - route_search.py:95-100
def extract_destination_from_query(self, query: str) -> tuple:
for city_name, city_code in self.config.city_codes.items():
if city_name in query: # "兰州" in "兰州路线"
return city_name, city_code
return None, None
5. 构建多字段查询 - route_search.py:106-128
# 对每个字段构建查询
for field, weight in self.config.field_weights.items():
# 字段权重配置
# 'destinations.abstract': 3.0
# 'destinations.city.name': 2.5
# 'route_name': 2.0
#..
6. 添加专注度加分 - route_search.py:146-167
if destination_code: # 识别到"兰州",添加专注度加分
# 6.1 专注度字段加分
search_body["query"]["function_score"]["functions"].append({
"field_value_factor": {
"field": f"focus_scores.620100", # 兰州的专注度分数
"factor": 2.0,
"modifier": "sqrt"
}
})
# 6.2 短途路线加分
search_body["query"]["function_score"]["functions"].append({
"exp": {
"metrics.duration_days": {
"origin": 2, # 2天是理想值
"scale": 3, # 衰减范围
"decay": 0.5
}
}
})
7. 专注度预计算 - route_search.py:48-71
def calculate_focus_score(self, route: Dict, target_city_code: str) -> float:
# 计算POI占比
lanzhou_pois = sum(1 for poi in route['destinations']['poi']
if poi.get('city_code') == '620100')
poi_ratio = lanzhou_pois / total_pois
# 计算天数占比
lanzhou_days = sum(1 for day in route['itinerary']
if day.get('overnight_city_code') == '620100')
day_ratio = lanzhou_days / total_days
return poi_ratio * 0.6 + day_ratio * 0.4
8. 发送到 Elasticsearch
最终构建的查询体会发送到 ES,大致结构如下:
{
"query": {
"function_score": {
"query": {
"bool": {
"should":[
{"match": {"destinations.abstract": {"query": "兰州路线", "boost": 3.0}}},
{"nested": {"path": "destinations.city",
"query": {"match": {"destinations.city.name": {"query": "兰州路线", "boost": 2.5}}}}},
{"match": {"route_name": {"query": "兰州路线", "boost": 2.0}}}
//... 其他字段
]
}
},
"functions":[
{
"field_value_factor": {
"field": "focus_scores.620100",
"factor": 2.0,
"modifier": "sqrt"
}
},
{
"exp": {
"metrics.duration_days": {
"origin": 2,
"scale": 3,
"decay": 0.5
}
}
}
]
}
}
}
评分计算示例
兰州城市经典2日游:
- BM25 基础分:~10分(路线名、抽象目的地、城市都匹配)
- 专注度加分:sqrt(1.0) * 2.0 = 2.0
- 短途加分:exp(0) * 1.5 = 1.5(2天正好是期望值)
- 最终得分:10 * (1 + 2.0 + 1.5) = 15.81
甘青大环线:
- BM25 基础分:~2分(仅城市字段部分匹配)
- 专注度加分:sqrt(0.19) * 2.0 = 0.87
- 短途减分:exp(-2.33) * 1.5 = 0.15(10天远离期望值)
- 最终得分:2 * (1 + 0.87 + 0.15) = 2.44