如何从给定MongoDb集合的所有文档中删除null
的所有字段?
我有一个存储在MongoDb数据库中的JSON文档集合,其中每个文档的格式为
{
'property1': 'value1',
'property2': 'value2',
...
}
但每个文档可能有一个null
条目而不是值条目。我想save disk space删除所有null
个条目。 null
条目的存在在我的情况下不包含任何信息,因为我先验地知道JSON文档的格式。
答案 0 :(得分:10)
// run in mongo shell
var coll = db.getCollection("collectionName");
var cursor = coll.find();
while (cursor.hasNext()) {
var doc = cursor.next();
var keys = {};
var hasNull = false;
for ( var x in doc) {
if (x != "_id" && doc[x] == null) {
keys[x] = 1;
hasNull = true;
}
}
if (hasNull) {
coll.update({_id: doc._id}, {$unset:keys});
}
}
答案 1 :(得分:2)
这是一个重要的问题,因为mongodb无法索引空值(即不查询空值或者您将等待很长时间),因此最好完全避免空值并使用setOnInsert
设置默认值
这是一个删除空值的递归解决方案:
/**
* RETRIEVES A LIST OF ALL THE KEYS IN A DOCUMENT, WHERE THE VALUE IS 'NULL' OR 'UNDEFINED'
*
* @param doc
* @param keyName
* @param nullKeys
*/
function getNullKeysRecursively(doc, keyName, nullKeys)
{
for (var item_property in doc)
{
// SKIP BASE-CLASS STUFF
if (!doc.hasOwnProperty(item_property))
continue;
// SKIP ID FIELD
if (item_property === "_id")
continue;
// FULL KEY NAME (FOR SUB-DOCUMENTS)
var fullKeyName;
if (keyName)
fullKeyName = keyName + "." + item_property;
else
fullKeyName = item_property;
// DEBUGGING
// print("fullKeyName: " + fullKeyName);
// NULL FIELDS - MODIFY THIS BLOCK TO ADD CONSTRAINTS
if (doc[item_property] === null || doc[item_property] === undefined)
nullKeys[fullKeyName] = 1;
// RECURSE OBJECTS / ARRAYS
else if (doc[item_property] instanceof Object || doc[item_property] instanceof Array)
getNullKeysRecursively(doc[item_property], fullKeyName, nullKeys);
}
}
/**
* REMOVES ALL PROPERTIES WITH A VALUE OF 'NULL' OR 'UNDEFINED'.
* TUNE THE 'LIMIT' VARIABLE TO YOUR MEMORY AVAILABILITY.
* ONLY CLEANS DOCUMENTS THAT REQUIRE CLEANING, FOR EFFICIENCY.
* USES bulkWrite FOR EFFICIENCY.
*
* @param collectionName
*/
function removeNulls(collectionName)
{
var coll = db.getCollection(collectionName);
var lastId = ObjectId("000000000000000000000000");
var LIMIT = 10000;
while (true)
{
// GET THE NEXT PAGE OF DOCUMENTS
var page = coll.find({ _id: { $gt: lastId } }).limit(LIMIT);
if (! page.hasNext())
break;
// BUILD BULK OPERATION
var arrBulkOps = [];
page.forEach(function(item_doc)
{
lastId = item_doc._id;
var nullKeys = {};
getNullKeysRecursively(item_doc, null, nullKeys);
// ONLY UPDATE MODIFIED DOCUMENTS
if (Object.keys(nullKeys).length > 0)
// UNSET INDIVIDUAL FIELDS, RATHER THAN REWRITE THE ENTIRE DOC
arrBulkOps.push(
{ updateOne: {
"filter": { _id: item_doc._id },
"update": { $unset: nullKeys }
} }
);
});
coll.bulkWrite(arrBulkOps, { ordered: false } );
}
}
// GO GO GO
removeNulls('my_collection');
之前的文件:
{
"_id": ObjectId("5a53ed8f6f7c4d95579cb87c"),
"first_name": null,
"last_name": "smith",
"features": {
"first": {
"a": 1,
"b": 2,
"c": null
},
"second": null,
"third" : {},
"fourth" : []
},
"other": [
null,
123,
{
"a": 1,
"b": "hey",
"c": null
}
]
}
文件后:
{
"_id" : ObjectId("5a53ed8f6f7c4d95579cb87c"),
"last_name" : "smith",
"features" : {
"first" : {
"a" : 1,
"b" : 2
}
},
"other" : [
null,
123,
{
"a" : 1,
"b" : "hey"
}
]
}
如您所见,它删除了null
,undefined
,空对象和空数组。如果你需要它更多/更少攻击性,那么就需要修改块" NULL FIELDS - 修改这个块来添加约束"。
欢迎编辑,特别是@stennie
答案 2 :(得分:1)
像提到的这个问题(mongodb query without field name):
不幸的是,MongoDB不支持使用特定值查询所有字段的任何方法。
因此,您可以迭代文档(如向导的示例)或以非mongodb方式进行。
如果这是一个JSON文件,删除sed中null
的所有行可能有效:
sed '/null/d' ./mydata.json
答案 3 :(得分:1)
您可以使用mongo updateMany 功能,但必须通过指定要更新的参数来执行此操作,例如let mergedOutput = [
{ id: 5, slug: 'category-5',
items: [
{ title: 'item title', description: 'item description' },
{ title: 'item title 2', description: 'item description 2' }
]
},
{ id: 4, slug: 'category-4',
items: [
{ title: 'item title 4', description: 'item description 4' },
]
},
{ id: 3, slug: 'category-3', items: [] },
]
参数:
year
答案 4 :(得分:0)
从Mongo 4.2
开始,db.collection.update()
可以接受聚合管道,最后允许根据其值删除字段:
// { _id: ObjectId("5d0e8...d2"), property1: "value1", property2: "value2" }
// { _id: ObjectId("5d0e8...d3"), property1: "value1", property2: null, property3: "value3" }
db.collection.update(
{},
[{ $replaceWith: {
$arrayToObject: {
$filter: {
input: { $objectToArray: "$$ROOT" },
as: "item",
cond: { $ne: ["$$item.v", null] }
}
}
}}],
{ multi: true }
)
// { _id: ObjectId("5d0e8...d2"), property1: "value1", property2: "value2" }
// { _id: ObjectId("5d0e8...d3"), property1: "value1", property3: "value3" }
详细信息:
第一部分{}
是匹配查询,用于过滤要更新的文档(在本例中为所有文档)。
第二部分[{ $replaceWith: { ... }]
是更新聚合管道(请注意方括号表示使用聚合管道):
$objectToArray
,我们首先将文档转换为键/值数组,例如[{ k: "property1", v: "value1" }, { k: "property2", v: null }, ...]
。$filter
,我们通过删除v
为null
的项目来过滤键/值数组。$arrayToObject
将过滤后的键/值数组转换回对象。$replaceWith
。不要忘记{ multi: true }
,否则只会更新第一个匹配的文档。