跳转至

搜索核心 (Search)

simai_search.search.RhythmSearcher 实现了基于节奏型的模糊搜索算法。

搜索流程

  1. Query 解析: 将用户的查询字符串(如 {16}1,1,)通过 SimaiParser 解析为相对时间序列(Deltas)。

    • 例如:[{'time': 0.0, 'delta': 0.0, 'is_star': False}, ...]
  2. 数据库获取: 根据筛选条件预取 note_data

  3. 匹配算法 (_match_pattern):

    • 遍历谱面中的每一个音符作为潜在的“起始点”。
    • BPM 检查: 如果指定了 BPM 范围,检查起始音符的 BPM 是否符合要求。
    • 星星检查: 如果查询的第一个音符要求是星星 (is_star=True),则起始音符必须是星星。
    • 序列匹配:
      • 对于查询序列中的每一个后续音符,检查谱面中是否存在对应时间点(Start + Delta)的音符。
      • 同时检查该时间点音符是否满足查询中指定的 BPM 和星星约束。
    • 容差 (Tolerance): 默认允许 0.01 拍的误差。

性能优化

内存缓存 (In-Memory Caching)

为了解决高频数据库读取带来的性能瓶颈,搜索模块实现了激进的缓存策略:

  1. 启动预热: 服务启动时,通过 _ensure_cache() 方法一次性加载所有谱面的元数据(ID、标题、难度、等级)和预解析的节奏事件 (events) 到内存中。
  2. 内存匹配: 搜索请求直接在内存中的列表进行遍历和匹配,完全避免了数据库 I/O 和 JSON 解析开销。
  3. 内存优化:
    • 为了防止内存溢出,原始谱面文本 (raw_content) 不包含在启动缓存中。
    • 仅当算法发现匹配项后,才会按需(On-Demand)从数据库获取原始文本以提取代码片段(Snippet)。
    • 这种"双层加载"机制确保了在低内存服务器(如 1GB RAM)上的稳定性。

算法优化

  • 使用 bisect_left (二分查找) 在有序的谱面事件列表中快速查找目标时间点,时间复杂度从 O(N) 降低到 O(log N) 对于单次匹配检查。