属性模式是唯一兼顾扩展性、查询性能和存储效率的方案,通过将语言作为数组元素的键、值扁平化进title等字段,并用复合多键索引支持高效查询,避免硬编码字段导致的schema僵化、索引失效与存储膨胀。

直接用属性模式(Attribute Pattern)——不是嵌套对象,也不是硬编码字段,这是唯一能兼顾扩展性、查询性能和存储效率的方案。
为什么不能用 name_en、name_zh 这类字段?
看似省事,实际会卡死在三个地方:
- 新增语言必须改 Schema、加字段、重建索引——生产环境不敢动
- 查“所有中文标题”得写
{ $or: [{ "name_zh": { $exists: true } }] },无法命中单一索引 - 90% 文档只填了英文,其余字段全是
null,BSON 存储膨胀 + 压缩率下降 + 全表扫描变慢
属性模式怎么建文档结构?
把语言作为数组元素的键,值扁平化进 title、description 这样的数组字段里:
{
"_id": "course-101",
"title": [
{ "lang": "en", "value": "Introduction to MongoDB" },
{ "lang": "zh-CN", "value": "MongoDB 入门指南" },
{ "lang": "ja", "value": "MongoDB入門ガイド" }
],
"description": [
{ "lang": "en", "value": "Learn core concepts" },
{ "lang": "zh-CN", "value": "掌握核心概念" }
]
}
这样设计后,只需一个复合多键索引就能覆盖全部语言查询:
db.courses.createIndex({"title.lang": 1, "title.value": 1}, { background: true })
查中文标题就用:db.courses.find({ "title.lang": "zh-CN", "title.value": "MongoDB 入门指南" }),完全走索引。
更新和聚合时最容易踩的坑
属性模式不是写完就完事,几个关键操作不注意就会出错:
- 别用
$elemMatch做等值查询——它不走多键索引,应拆成两个独立条件 - 更新单个语言项时,别用
$set: { "title.0.value": "new" }——下标不稳定;改用$set: { "title.$[elem].value": "new" }+arrayFilters - 聚合时
$unwind多语言字段会爆炸式复制文档,记得加$group或用$first控制输出 - 如果 90% 文档只有英文,5% 含中日,考虑混合建模:高频语言(如 en/zh)单独字段,低频语言走属性数组
真正难的不是定义结构,而是判断哪些字段值得用属性模式——比如 title 和 description 必须支持任意语言增删,而 slug 或 seo_title 通常只维护一两种语言,就该单独建字段。











