我有一个mongodb集合。当我这样做。
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中实现这一目标。
答案 0 :(得分:16)
您可以使用聚合框架执行此操作,如下所示:
collection.aggregate([
{ "$project": {
"name": 1,
"code": 1,
"abbreviation": 1,
"bill_codes": { "$setUnion": [ "$bill_codes", [] ] }
}}
])
$setUnion
运算符是“set”运算符,因此要设置“set”,那么只会保留“唯一”项。
如果您仍在使用早于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()
检索“光标”,但基本上遵循以下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 } }
)
})
使用添加的操作来比较“重复数据删除”数组是否与原始数组长度相同,并且仅返回那些已删除“重复”的文档以进行更新处理。
也可以在这里添加“for python”注释。如果您不关心“识别”包含重复数组条目的文档并准备用更新“爆炸”整个集合,那么只需在客户端代码中使用python .set()
即可删除重复项:
for doc in collection.find():
collection.update(
{ "_id": doc["_id"] },
{ "$set": { "bill_codes": list(set(doc["bill_codes"])) } }
)
所以这很简单,它取决于哪个是更大的罪恶,找到带有重复文件的文件或更新每个文件的成本是否需要它。
这至少涵盖了技术。
答案 1 :(得分:1)
你可以使用带有一些javascript的foreach循环:
for (int i = 0, count = container.Items.Count; i < count; i++)
{
Application.Current.Dispatcher.BeginInvoke(new Action(delegate()
{
TreeViewItem subContainer = (TreeViewItem)container.ItemContainerGenerator.ContainerFromIndex(i);
GetTotalNTreeViewItems(subContainer, sender);
SetNodesProcessed(sender, GetNodesProcessed(sender) + 1);
}));
}
答案 2 :(得分:0)
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}}
])
答案 3 :(得分:0)
MongoDB 4.2 集合updateMany方法的 update 参数也可以是聚合管道(而不是文档)。管道支持$set
,$unset
和$replaceWith
阶段。通过在$setIntersection
阶段使用$set
聚合管道运算符,您可以从数组字段中删除重复项,并通过一次操作更新集合。
一个例子:
数组集合:
{ "_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()
也具有此功能。