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

如何通过slug化地区值在MongoDB中匹配地址文档?

问题描述

我定义了如下Mongoose地址Schema:

const adresseSchema = mongoose.Schema({
  adresseComplete: {
    type: String,
    required: true,
    trim: true,
  },
  ville: {
    type: String,
    required: false,
  },
  departement: {
    type: String,
    required: false,
  },
  region: {
    type: String,
    required: true,
  },
  pays: {
    type: String,
    required: true,
  },
});

示例文档:

"adresse": {
    "_id": "642156ad876e0c4328e193e7",
    "ville": "Paris",
    "departement": "Département de Paris",
    "region": "Île-de-France",
    "pays": "France",
  },

前端会传入slug化的地区值(比如ile-de-france),对应的slug化函数如下:

const slugify = (str) => {
  const map = {
    '-': ' |_',
    a: 'á|à|ã|â|ä|À|Á|Ã|Â|Ä',
    e: 'é|è|ê|ë|É|È|Ê|Ë',
    i: 'í|ì|î|ï|Í|Ì|Î|Ï',
    o: 'ó|ò|ô|õ|ö|Ó|Ò|Ô|Õ|Ö',
    u: 'ú|ù|û|ü|Ú|Ù|Û|Ü',
    c: 'ç|Ç',
    n: 'ñ|Ñ',
  };

  for (const pattern in map) str = str.replace(new RegExp(map[pattern], 'g'), pattern);

  return str.replace(/[^a-z\-0-9\s]/gi, '').toLowerCase();
};

需要在MongoDB中查询所有匹配该slug化地区值的地址,但无法使用$where(MongoDB SaaS集群限制)。

解决方案

方法一:预存slug字段(最优方案)

直接在Schema中添加regionSlug字段,在创建或更新文档时自动生成并存储对应的slug值,查询时直接匹配该字段,性能最高。

步骤1:修改Schema

const adresseSchema = mongoose.Schema({
  adresseComplete: {
    type: String,
    required: true,
    trim: true,
  },
  ville: {
    type: String,
    required: false,
  },
  departement: {
    type: String,
    required: false,
  },
  region: {
    type: String,
    required: true,
  },
  regionSlug: { // 新增slug字段
    type: String,
    required: true,
    index: true, // 添加索引提升查询性能
  },
  pays: {
    type: String,
    required: true,
  },
});

步骤2:添加预保存钩子自动生成slug

adresseSchema.pre('save', function(next) {
  // 如果region字段有修改,重新生成slug
  if (this.isModified('region')) {
    this.regionSlug = slugify(this.region);
  }
  next();
});

// 支持更新操作的钩子
adresseSchema.pre('findOneAndUpdate', function(next) {
  const update = this.getUpdate();
  if (update.region) {
    update.regionSlug = slugify(update.region);
  }
  next();
});

步骤3:查询匹配

const targetSlug = 'ile-de-france';
const addresses = await AdresseModel.find({ regionSlug: targetSlug });

方法二:使用聚合管道实时处理(无Schema修改)

如果无法修改现有Schema,可以用MongoDB聚合管道复制前端的slug逻辑,实时处理region字段后匹配。

查询代码

const targetSlug = 'ile-de-france';

const addresses = await AdresseModel.aggregate([
  {
    $addFields: {
      // 复制前端slugify的处理逻辑
      processedRegion: {
        $toLowerCase: {
          $replaceAll: {
            input: {
              $replaceAll: {
                input: {
                  $replaceAll: {
                    input: {
                      $replaceAll: {
                        input: {
                          $replaceAll: {
                            input: {
                              $replaceAll: {
                                input: {
                                  $replaceAll: {
                                    input: {
                                      $replaceAll: {
                                        input: {
                                          $replaceAll: {
                                            input: "$region",
                                            find: /[áàãâäÀÁÃÂÄ]/g,
                                            replacement: "a"
                                          }
                                        },
                                        find: /[éèêëÉÈÊË]/g,
                                        replacement: "e"
                                      }
                                    },
                                    find: /[íìîïÍÌÎÏ]/g,
                                    replacement: "i"
                                  }
                                },
                                find: /[óòôõöÓÒÔÕÖ]/g,
                                replacement: "o"
                              }
                            },
                            find: /[úùûüÚÙÛÜ]/g,
                            replacement: "u"
                          }
                        },
                        find: /[çÇ]/g,
                        replacement: "c"
                      }
                    },
                    find: /[ñÑ]/g,
                    replacement: "n"
                  }
                },
                find: /[ |_]/g,
                replacement: "-"
              }
            },
            find: /[^a-z\-0-9\s]/gi,
            replacement: ""
          }
        }
      }
    }
  },
  { $match: { processedRegion: targetSlug } },
  // 可选:移除临时字段,返回原文档结构
  { $project: { processedRegion: 0 } }
]);

注意:这种方法需要对每个文档做实时处理,数据量大时性能会受影响,建议仅在无法修改Schema时使用,且尽量给region字段添加索引。

方法三:构建正则表达式匹配(简易场景)

如果地区的slug转换规则相对简单,可以构建反向正则,匹配原始region字段。

示例代码

const targetSlug = 'ile-de-france';

// 反向处理slug,生成匹配原始region的正则
const regexStr = targetSlug
  .replace(/-/g, '[ |_]?')
  .replace(/a/g, '[aáàãâäÀÁÃÂÄ]')
  .replace(/e/g, '[eéèêëÉÈÊË]')
  .replace(/i/g, '[iíìîïÍÌÎÏ]')
  .replace(/o/g, '[oóòôõöÓÒÔÕÖ]')
  .replace(/u/g, '[uúùûüÚÙÛÜ]')
  .replace(/c/g, '[cçÇ]')
  .replace(/n/g, '[nñÑ]');

const regionRegex = new RegExp(`^${regexStr}$`, 'i');
const addresses = await AdresseModel.find({ region: regionRegex });

注意:这种方法可能存在匹配不准确的情况(比如特殊字符处理不全),且正则查询无法利用普通索引,性能不如预存slug字段。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 21:17:45