如何删除 mongodb 列表中的重复值
How to remove duplicate values inside a list in mongodb
我有一个 mongodb collection 。当我做的时候。
db.bill.find({})
我明白了,
{
"_id" : ObjectId("55695ea145e8a960bef8b87a"),
"name" : "ABC. Net",
"code" : "1-98tfv",
"abbreviation" : "ABC",
"bill_codes" : [ 190215, 44124, 190215, 147708 ],
"customer_name" : "abc"
}
我需要一个操作来删除 bill_codes 中的重复值。最后应该是
{
"_id" : ObjectId("55695ea145e8a960bef8b87a"),
"name" : "ABC. Net",
"code" : "1-98tfv",
"abbreviation" : "ABC",
"bill_codes" : [ 190215, 44124, 147708 ],
"customer_name" : "abc"
}
如何在 mongodb 中实现这一点。
您可以使用聚合框架执行此操作,如下所示:
collection.aggregate([
{ "$project": {
"name": 1,
"code": 1,
"abbreviation": 1,
"bill_codes": { "$setUnion": [ "$bill_codes", [] ] }
}}
])
$setUnion
运算符是 "set" 运算符,因此要生成 "set",则只保留 "unique" 项。
如果您仍在使用早于 2.6 的 MongoDB 版本,那么您必须使用 $unwind
和 执行此操作$addToSet
改为:
collection.aggregate([
{ "$unwind": "$bill_codes" },
{ "$group": {
"_id": "$_id",
"name": { "$first": "$name" },
"code": { "$first": "$code" },
"abbreviation": { "$first": "$abbreviation" },
"bill_codes": { "$addToSet": "$bill_codes" }
}}
])
效率不高,但从 2.2 版开始支持运算符。
当然,如果您真的想永久修改您的集合文档,那么您可以对此进行扩展并相应地处理每个文档的更新。您可以从 .aggregate()
检索 "cursor",但基本上遵循此 shell 示例:
db.collection.aggregate([
{ "$project": {
"bill_codes": { "$setUnion": [ "$bill_codes", [] ] },
"same": { "$eq": [
{ "$size": "$bill_codes" },
{ "$size": { "$setUnion": [ "$bill_codes", [] ] } }
]}
}},
{ "$match": { "same": false } }
]).forEach(function(doc) {
db.collection.update(
{ "_id": doc._id },
{ "$set": { "bill_codes": doc.bill_codes } }
)
})
早期版本涉及更多:
db.collection.aggregate([
{ "$unwind": "$bill_codes" },
{ "$group": {
"_id": {
"_id": "$_id",
"bill_code": "$bill_codes"
},
"origSize": { "$sum": 1 }
}},
{ "$group": {
"_id": "$_id._id",
"bill_codes": { "$push": "$_id.bill_code" },
"origSize": { "$sum": "$origSize" },
"newSize": { "$sum": 1 }
}},
{ "$project": {
"bill_codes": 1,
"same": { "$eq": [ "$origSize", "$newSize" ] }
}},
{ "$match": { "same": false } }
]).forEach(function(doc) {
db.collection.update(
{ "_id": doc._id },
{ "$set": { "bill_codes": doc.bill_codes } }
)
})
在其中添加操作以比较 "de-duplicated" 数组是否与原始数组长度相同,并且仅 return 删除了 "duplicates" 的文档以进行处理更新。
可能还应在此处添加 "for python" 注释。如果您不关心 "identifying" 包含重复数组条目的文档并准备 "blast" 更新整个集合,那么只需在客户端中使用 python .set()
删除重复项的代码:
for doc in collection.find():
collection.update(
{ "_id": doc["_id"] },
{ "$set": { "bill_codes": list(set(doc["bill_codes"])) } }
)
所以这很简单,这取决于哪个是更大的罪恶,查找具有重复的文档或更新每个文档的成本是否需要它。
这至少涵盖了技术。
您可以使用带有一些 javascript:
的 foreach 循环
db.bill.find().forEach(function(entry){
var arr = entry.bill_codes;
var uniqueArray = arr.filter(function(elem, pos) {
return arr.indexOf(elem) == pos;
});
entry.bill_codes = uniqueArray;
db.bill.save(entry);
})
Mongo 3.4+ 具有 $addFields 聚合阶段,这使您可以避免显式列出 $project
:
中的所有其他字段
db.bill.aggregate([
{"$addFields": {
"bill_codes": {"$setUnion": ["$bill_codes", []]}
}}
])
仅供参考,这是另一种(更冗长的)使用 replaceRoot
的方式,也不需要列出所有可能的字段:
db.bill.aggregate([
{'$unwind': {
'path': '$bill_codes',
// output the document even if its list of books is empty
'preserveNullAndEmptyArrays': true
}},
{'$group': {
'_id': '$_id',
'bill_codes': {'$addToSet': '$bill_codes'},
// arbitrary name that doesn't exist on any document
'_other_fields': {'$first': '$$ROOT'},
}},
{
// the field, in the resulting document, has the value from the last document merged for the field. (c) docs
// so the new deduped array value will be used
'$replaceRoot': {'newRoot': {'$mergeObjects': ['$_other_fields', "$$ROOT"]}}
},
{'$project': {'_other_fields': 0}}
])
MongoDB 4.2集合updateMany方法的update参数也可以是聚合pipeline(而不是文档)。管道支持 $set
、$unset
和 $replaceWith
阶段。在 $set
阶段使用 $setIntersection
聚合管道运算符,您可以从数组字段中删除重复项并在单个操作中更新集合。
一个例子:
数组集合:
{ "_id" : 0, "a" : [ 3, 5, 5, 3 ] }
{ "_id" : 1, "a" : [ 1, 2, 3, 2, 4 ] }
来自mongo shell:
db.arrays.updateMany(
{ },
[
{ $set: { a: { $setIntersection: [ "$a", "$a" ] } } }
]
)
更新数组集合:
{ "_id" : 0, "a" : [ 3, 5 ] }
{ "_id" : 1, "a" : [ 1, 2, 3, 4 ] }
其他更新方法update()
、updateOne()
和findAndModify()
也有此功能。
我有一个 mongodb collection 。当我做的时候。
db.bill.find({})
我明白了,
{
"_id" : ObjectId("55695ea145e8a960bef8b87a"),
"name" : "ABC. Net",
"code" : "1-98tfv",
"abbreviation" : "ABC",
"bill_codes" : [ 190215, 44124, 190215, 147708 ],
"customer_name" : "abc"
}
我需要一个操作来删除 bill_codes 中的重复值。最后应该是
{
"_id" : ObjectId("55695ea145e8a960bef8b87a"),
"name" : "ABC. Net",
"code" : "1-98tfv",
"abbreviation" : "ABC",
"bill_codes" : [ 190215, 44124, 147708 ],
"customer_name" : "abc"
}
如何在 mongodb 中实现这一点。
您可以使用聚合框架执行此操作,如下所示:
collection.aggregate([
{ "$project": {
"name": 1,
"code": 1,
"abbreviation": 1,
"bill_codes": { "$setUnion": [ "$bill_codes", [] ] }
}}
])
$setUnion
运算符是 "set" 运算符,因此要生成 "set",则只保留 "unique" 项。
如果您仍在使用早于 2.6 的 MongoDB 版本,那么您必须使用 $unwind
和 执行此操作$addToSet
改为:
collection.aggregate([
{ "$unwind": "$bill_codes" },
{ "$group": {
"_id": "$_id",
"name": { "$first": "$name" },
"code": { "$first": "$code" },
"abbreviation": { "$first": "$abbreviation" },
"bill_codes": { "$addToSet": "$bill_codes" }
}}
])
效率不高,但从 2.2 版开始支持运算符。
当然,如果您真的想永久修改您的集合文档,那么您可以对此进行扩展并相应地处理每个文档的更新。您可以从 .aggregate()
检索 "cursor",但基本上遵循此 shell 示例:
db.collection.aggregate([
{ "$project": {
"bill_codes": { "$setUnion": [ "$bill_codes", [] ] },
"same": { "$eq": [
{ "$size": "$bill_codes" },
{ "$size": { "$setUnion": [ "$bill_codes", [] ] } }
]}
}},
{ "$match": { "same": false } }
]).forEach(function(doc) {
db.collection.update(
{ "_id": doc._id },
{ "$set": { "bill_codes": doc.bill_codes } }
)
})
早期版本涉及更多:
db.collection.aggregate([
{ "$unwind": "$bill_codes" },
{ "$group": {
"_id": {
"_id": "$_id",
"bill_code": "$bill_codes"
},
"origSize": { "$sum": 1 }
}},
{ "$group": {
"_id": "$_id._id",
"bill_codes": { "$push": "$_id.bill_code" },
"origSize": { "$sum": "$origSize" },
"newSize": { "$sum": 1 }
}},
{ "$project": {
"bill_codes": 1,
"same": { "$eq": [ "$origSize", "$newSize" ] }
}},
{ "$match": { "same": false } }
]).forEach(function(doc) {
db.collection.update(
{ "_id": doc._id },
{ "$set": { "bill_codes": doc.bill_codes } }
)
})
在其中添加操作以比较 "de-duplicated" 数组是否与原始数组长度相同,并且仅 return 删除了 "duplicates" 的文档以进行处理更新。
可能还应在此处添加 "for python" 注释。如果您不关心 "identifying" 包含重复数组条目的文档并准备 "blast" 更新整个集合,那么只需在客户端中使用 python .set()
删除重复项的代码:
for doc in collection.find():
collection.update(
{ "_id": doc["_id"] },
{ "$set": { "bill_codes": list(set(doc["bill_codes"])) } }
)
所以这很简单,这取决于哪个是更大的罪恶,查找具有重复的文档或更新每个文档的成本是否需要它。
这至少涵盖了技术。
您可以使用带有一些 javascript:
的 foreach 循环db.bill.find().forEach(function(entry){
var arr = entry.bill_codes;
var uniqueArray = arr.filter(function(elem, pos) {
return arr.indexOf(elem) == pos;
});
entry.bill_codes = uniqueArray;
db.bill.save(entry);
})
Mongo 3.4+ 具有 $addFields 聚合阶段,这使您可以避免显式列出 $project
:
db.bill.aggregate([
{"$addFields": {
"bill_codes": {"$setUnion": ["$bill_codes", []]}
}}
])
仅供参考,这是另一种(更冗长的)使用 replaceRoot
的方式,也不需要列出所有可能的字段:
db.bill.aggregate([
{'$unwind': {
'path': '$bill_codes',
// output the document even if its list of books is empty
'preserveNullAndEmptyArrays': true
}},
{'$group': {
'_id': '$_id',
'bill_codes': {'$addToSet': '$bill_codes'},
// arbitrary name that doesn't exist on any document
'_other_fields': {'$first': '$$ROOT'},
}},
{
// the field, in the resulting document, has the value from the last document merged for the field. (c) docs
// so the new deduped array value will be used
'$replaceRoot': {'newRoot': {'$mergeObjects': ['$_other_fields', "$$ROOT"]}}
},
{'$project': {'_other_fields': 0}}
])
MongoDB 4.2集合updateMany方法的update参数也可以是聚合pipeline(而不是文档)。管道支持 $set
、$unset
和 $replaceWith
阶段。在 $set
阶段使用 $setIntersection
聚合管道运算符,您可以从数组字段中删除重复项并在单个操作中更新集合。
一个例子:
数组集合:
{ "_id" : 0, "a" : [ 3, 5, 5, 3 ] }
{ "_id" : 1, "a" : [ 1, 2, 3, 2, 4 ] }
来自mongo shell:
db.arrays.updateMany(
{ },
[
{ $set: { a: { $setIntersection: [ "$a", "$a" ] } } }
]
)
更新数组集合:
{ "_id" : 0, "a" : [ 3, 5 ] }
{ "_id" : 1, "a" : [ 1, 2, 3, 4 ] }
其他更新方法update()
、updateOne()
和findAndModify()
也有此功能。