{a:1,b:2} 和 {b:2,a:1} 是同一个文档吗?这篇用同样内容、不同顺序的子文档,验证整体匹配、点号路径、$expr、分组和唯一索引的行为,再看更新操作怎么移动字段。
test> db.t.find({sub: {a: 1, b: 2}}).toArray()
[ { _id: 1, sub: { a: 1, b: 2 } } ]
test> db.t.find({sub: {b: 2, a: 1}}).toArray()
[ { _id: 2, sub: { b: 2, a: 1 } } ]子文档作为一个值比较时,字段顺序是值的一部分:{sub: {a:1, b:2}} 只匹配字段顺序相同的文档。
test> db.t.find({"sub.a": 1, "sub.b": 2}).toArray().map(d => d._id)
[ 1, 2 ]
test> db.t.find({$and: [{"sub.b": 2}, {"sub.a": 1}]}).toArray().map(d => d._id)
[ 1, 2 ]
test> db.t.find({$expr: {$eq: ["$sub", {a: 1, b: 2}]}}).toArray().map(d => d._id)
[ 1 ]
test> db.t.aggregate([{$group: {_id: "$sub", n: {$sum: 1}}}, {$sort: {n: -1}}])
[ { _id: { b: 2, a: 1 }, n: 1 }, { _id: { a: 1, b: 2 }, n: 1 } ]{"sub.a": 1, "sub.b": 2} 分别匹配字段,两个顺序都命中;但 $expr 里 $eq 比较整个子文档、$group 用子文档做分组键,都仍然看顺序。
test> db.u.updateOne({_id: 1}, {$set: {b: 20}}); db.u.findOne()
{ _id: 1, a: 1, b: 20, c: 3 }
test> db.u.updateOne({_id: 1}, {$set: {z: 26}}); db.u.findOne()
{ _id: 1, a: 1, b: 20, c: 3, z: 26 }
test> db.u.updateOne({_id: 1}, {$unset: {a: 1}}); db.u.updateOne({_id: 1}, {$set: {a: 1}}); db.u.findOne()
{ _id: 1, b: 20, c: 3, z: 26, a: 1 }
test> db.u.updateOne({_id: 1}, {$rename: {z: "y"}}); db.u.findOne()
{ _id: 1, b: 20, c: 3, a: 1, y: 26 }$set 已有字段保持原位,新字段追加到末尾;$unset 再 $set 或 $rename 会让字段出现在末尾。
发送的字段顺序: a, _id, b (用原始 insert 命令)
test> db.idpos.findOne()
{ _id: 99, a: 1, b: 2 }
test> db.idpos.replaceOne({_id: 99}, {b: 5, a: 4, _id: 99}); db.idpos.findOne()
{ _id: 99, b: 5, a: 4 }不管插入或替换时 _id 写在哪,服务端都把它放在文档第一位,其它字段保持发送的顺序。
test> db.js.insertOne({_id: 1, b: 1, "2": 2, "1": 1, a: 0}); db.js.findOne()
{ '1': 1, '2': 2, _id: 1, b: 1, a: 0 }
pymongo 读回 mongosh 插入的文档,字段顺序: ['_id', '1', '2', 'b', 'a']
pymongo 用 SON 按 b, "2", "1", a 的顺序插入,再用 pymongo 读回,字段顺序: ['_id', 'b', '2', '1', 'a']
test> db.py.findOne() // 同一个文档,mongosh 显示时又把整数键排到最前
{ '1': 1, '2': 2, _id: 1, b: 1, a: 0 }mongosh 里的 JavaScript 对象会把像整数的键排到最前面,所以顺序在客户端构造时就变了;Python 用 SON 或保序的字典则不会。
唯一索引上先后插入 k={a:1,b:2} 和 k={b:2,a:1}:都成功,集合里 2 个文档
再插入一次 k={a:1,b:2}: E11000 duplicate key error collection: test.g index: k_1 dup key: { k: { a: 1, b: 2 } }, full error: {'index':唯一索引上,字段顺序不同的子文档是不同的键,不会触发重复键错误。需要唯一性的组合,拆成多个字段建复合唯一索引更可靠。