You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

塞尔维亚拉丁字母模糊搜索实现方案咨询(NestJS+React)

实现塞尔维亚拉丁字母同源字符搜索的基础思路

方案1:动态生成同源字符正则(无需修改存储结构)

  • 步骤1:建立同源字符映射表
    定义基础字符到所有变体的映射,覆盖塞尔维亚拉丁字母的特殊字符:
    const serbianCharMap: Record<string, string[]> = {
      'c': ['c', 'č', 'ć'],
      'd': ['d', 'đ'],
      's': ['s', 'š'],
      'z': ['z', 'ž'],
    };
    
  • 步骤2:实现搜索词归一化工具函数
    先转义正则特殊字符,再把每个基础字符替换为对应变体的正则字符组:
    // 转义正则特殊字符(避免用户输入破坏正则结构)
    function escapeRegExp(str: string): string {
      return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    }
    
    // 归一化搜索词,生成匹配所有同源字符的正则表达式字符串
    function normalizeSerbianSearchTerm(term: string): string {
      const escapedLowerTerm = escapeRegExp(term.toLowerCase());
      let normalizedTerm = escapedLowerTerm;
      
      Object.entries(serbianCharMap).forEach(([baseChar, variants]) => {
        normalizedTerm = normalizedTerm.replace(
          new RegExp(baseChar, 'g'),
          `[${variants.join('')}]`
        );
      });
      
      return normalizedTerm;
    }
    
  • 步骤3:在NestJS服务层构建查询
    结合ORM(如TypeORM)生成包含正则匹配的查询条件,以匹配多列为例:
    async searchEntities(searchTerm: string) {
      const normalizedTerm = normalizeSerbianSearchTerm(searchTerm);
      
      return this.entityRepository.find({
        where: Raw(alias => 
          `LOWER(${alias}.column1) REGEXP '${normalizedTerm}' OR LOWER(${alias}.column2) REGEXP '${normalizedTerm}'`
        ),
      });
    }
    
    注意:不同数据库的正则语法有差异,比如PostgreSQL用~*替代REGEXP,MongoDB直接用$regex操作符。

方案2:预归一化存储字段(性能更优,适合大数据量)

  • 步骤1:在实体中添加归一化搜索字段
    新增一个专门用于搜索的字段,存储去掉变音符号的基础字符版本:
    @Entity()
    export class YourEntity {
      @Column()
      name: string;
      
      @Column()
      description: string;
      
      // 归一化搜索字段,存储无变音的字符串
      @Column({ name: 'search_normalized' })
      searchNormalized: string;
    }
    
  • 步骤2:写入数据时自动生成归一化字段
    在实体的BeforeInsert和BeforeUpdate钩子中处理:
    @BeforeInsert()
    @BeforeUpdate()
    generateSearchNormalized() {
      // 把所有变音字符替换为基础字符
      this.searchNormalized = this.name.toLowerCase()
        .replace(/[čć]/g, 'c')
        .replace(/đ/g, 'd')
        .replace(/š/g, 's')
        .replace(/ž/g, 'z')
        + ' ' + // 拼接多列内容,实现多列搜索
        this.description.toLowerCase()
        .replace(/[čć]/g, 'c')
        .replace(/đ/g, 'd')
        .replace(/š/g, 's')
        .replace(/ž/g, 'z');
    }
    
  • 步骤3:搜索时直接匹配归一化字段
    这种方式可以利用数据库索引,性能远高于正则匹配:
    async searchEntities(searchTerm: string) {
      const normalizedSearchTerm = searchTerm.toLowerCase()
        .replace(/[čć]/g, 'c')
        .replace(/đ/g, 'd')
        .replace(/š/g, 's')
        .replace(/ž/g, 'z');
      
      return this.entityRepository.find({
        where: {
          searchNormalized: Like(`%${normalizedSearchTerm}%`),
        },
      });
    }
    

方案对比

  • 方案1:无需修改存储结构,实现简单,但正则匹配无法利用索引,数据量大时性能下降明显。
  • 方案2:需要额外维护字段,但搜索性能优异,适合数据量较大或搜索频繁的场景。

内容的提问来源于stack exchange,提问作者StrahinjaL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 13:30:52