MongoDB聚合操作中实现按位与运算的可行方案问询
解决方案:MongoDB聚合中实现按位与运算
好问题!确实MongoDB的$bit操作符仅支持文档更新场景,没法直接在聚合管道里执行按位运算,但从MongoDB 4.2版本开始,我们有了完美的解决方案——用聚合表达式原生支持的$bitAnd运算符来实现你要的按位与计算。
最优方案(MongoDB 4.2+)
我们可以通过聚合管道先获取目标文档(_id:1)的choices值,再对所有其他文档执行按位与运算,步骤如下:
完整聚合管道
db.collection.aggregate([ // 1. 聚合所有文档,提取目标值并保存所有文档数据 { $group: { _id: null, targetChoices: { $first: { $cond: [{ $eq: ["$_id", 1] }, "$choices", "$$REMOVE"] } }, docs: { $push: "$$ROOT" } } }, // 2. 展开文档数组,过滤掉目标文档本身(可选) { $unwind: "$docs" }, { $match: { "docs._id": { $ne: 1 } } }, // 3. 计算按位与结果,生成and_op字段 { $project: { _id: "$docs._id", choices: "$docs.choices", and_op: { $bitAnd: ["$docs.choices", "$targetChoices"] } } }, // 可选:按and_op降序排序,匹配你的示例输出顺序 { $sort: { and_op: -1 } } ])
执行结果
针对你提供的示例数据,运行上述管道后会得到:
{ "_id" : 3, "choices" : 3, "and_op" : 3 } { "_id" : 2, "choices" : 5, "and_op" : 1 } { "_id" : 4, "choices" : 4, "and_op" : 0 }
简化版本(已知目标值时)
如果你已经提前查询到_id:1的choices值为3,可以直接把目标值写死,让管道更简洁高效:
db.collection.aggregate([ { $match: { _id: { $ne: 1 } } }, { $project: { choices: 1, and_op: { $bitAnd: ["$choices", 3] } } }, { $sort: { and_op: -1 } } ])
兼容旧版本(MongoDB 4.2之前)
如果你的MongoDB版本低于4.2,没有原生的$bitAnd,可以通过自定义逻辑模拟按位与:遍历每一位判断是否在两个数中都为1,累加对应权重得到结果。
db.collection.aggregate([ { $group: { _id: null, targetChoices: { $first: { $cond: [{ $eq: ["$_id", 1] }, "$choices", "$$REMOVE"] } }, docs: { $push: "$$ROOT" } } }, { $unwind: "$docs" }, { $match: { "docs._id": { $ne: 1 } } }, { $project: { _id: "$docs._id", choices: "$docs.choices", and_op: { $reduce: { input: { $range: [0, 32] }, // 针对32位整数遍历每一位 initialValue: 0, in: { $add: [ "$$value", { $cond: [ { $and: [ { $ne: [{ $bitwiseAnd: ["$docs.choices", { $pow: [2, "$$this"] }] }, 0] }, { $ne: [{ $bitwiseAnd: ["$targetChoices", { $pow: [2, "$$this"] }] }, 0] } ] }, { $pow: [2, "$$this"] }, 0 ] } ] } } } } }, { $sort: { and_op: -1 } } ])
额外补充:实现最初的“匹配比特位数量”需求
其实你最初想要的「统计相同比特位上1的匹配数量」也可以轻松实现,MongoDB 5.0+新增了$bitCount运算符,用来计算整数的二进制中1的个数。结合$bitAnd就能得到匹配数量:
db.collection.aggregate([ { $match: { _id: { $ne: 1 } } }, { $project: { choices: 1, and_op: { $bitAnd: ["$choices", 3] }, matches: { $bitCount: { $bitAnd: ["$choices", 3] } } } }, { $sort: { matches: -1 } } ])
执行后会得到你理想中的结果:
{ "_id" : 3, "choices" : 3, "and_op" : 3, "matches" : 2 } { "_id" : 2, "choices" : 5, "and_op" : 1, "matches" : 1 } { "_id" : 4, "choices" : 4, "and_op" : 0, "matches" : 0 }
内容的提问来源于stack exchange,提问作者Saran
相关产品推荐
相关产品推荐

