I'm working on Django which uses MongoDB.
One of collections has the following structure:
{
"inspectionType" : {
"id" : "59a79e44d12b52042104c1e8",
"name" : "Example Name",
"inspMngrsRole" : [
{
"id" : "55937af6f3de6c004bc42862",
"type" : "inspectorManager",
"is_secret_shoper" : false
}
],
"scopes" : {
"56fcf6736389b9007a114b10" : {
"_cls" : "SomeClass",
"id" : "56fcf6736389b9007a114b10",
"name" : "Example Name",
"category" : "Example Category"
},
}
}
}
I need to update field "_cls" ("inspectionType.scopes.._cls") for all documents in the collection.
The problem is the scope_id is dynamic and unique for each scope.
Is it possible to use db.collection.update for that?
And how should the path to the field look like?
Update:
MongoDB version: 3.6.7
You can update using an aggregation (if you are using MongoDB version lesser than 4.2) plus an update operation or the updateMany method (if using version 4.2 or later) as follows:
# 1
var NEW_VALUE = "some new value" // the value to be updated
db.collection.aggregate( [
{
$addFields: {
"inspectionType.scopes": { $objectToArray: "$inspectionType.scopes" }
}
},
{
$addFields: {
"inspectionType.scopes.v._cls": NEW_VALUE
}
},
{
$addFields: {
"inspectionType.scopes": { $arrayToObject: "$inspectionType.scopes" }
}
}
] ).forEach( doc => db.scopes.updateOne( { _id: doc._id }, { $set: { "inspectionType.scopes": doc.inspectionType.scopes } } ) )
Starting MongoDB version 4.2 the updateMany can use an aggregation pipeline for the update operation; see Update with Aggregation Pipeline.
# 2
db.collection.updateMany(
{ },
[
{
$set: {
"inspectionType.scopes": { $objectToArray: "$inspectionType.scopes" }
}
},
{
$set: {
"inspectionType.scopes.v._cls": NEW_VALUE
}
},
{
$set: {
"inspectionType.scopes": { $arrayToObject: "$inspectionType.scopes" }
}
}
]
)
Related
I'm currently working with a MongoDB database and I have fields that have a value of NULL is there a way to run a query that will replace these NULL fields with a value of "Missing" instead?
An example of the document is:
{
"_id" : 1,
"Users" : [
{
"name" : "John Davies",
"age" : null,
"place_of_birth" : "Cardigan"
},
{
"name" : "Edward Jones",
"age" : null,
"place_of_birth" : null
},
{
"name" : "Daniel Rhys",
"age" : NumberLong(63),
"place_of_birth" : "Cardigan"
},
{
"name" : null,
"age" : NumberLong(61),
"place_of_birth" : "Cardigan"
},
{
"name" : "John Davies ",
"age" : null,
"place_of_birth" : "Cardigan"
}
]
}
Demo - https://mongoplayground.net/p/dsI5G6zfbLr
Use $[]
db.collection.update(
{},
{ $set: { "Users.$[u].age": "missing" } },
{ arrayFilters: [ { "u.age": null } ], multi: true}
)
Combine multiple queries into 1 using db.collection.bulkWrite
db.collection.bulkWrite( [
{ updateMany :
{
"filter": {},
"update": { $set: { "Users.$[u].age": "missing" } },
"arrayFilters": [ { "u.age": null } ],
}
},
{ updateMany :
{
"filter": {},
"update": { $set: { "Users.$[u].name": "missing" } },
"arrayFilters": [ { "u.name": null } ],
}
},
{ updateMany :
{
"filter": {},
"update": { $set: { "Users.$[u].place_of_birth": "missing" } },
"arrayFilters": [ { "u.place_of_birth": null } ],
}
}
] )
Update for MongoDB Version 3.2+
while (db.collection.find({$or:[{"Users.age":null},{"Users.name":null},{"Users.place_of_birth":null}]}).count()) {
db.collection.bulkWrite( [
{ updateMany :
{
"filter": { "Users.age": null },
"update": { $set: { "Users.$.age": "missing" } }
}
},
{ updateMany :
{
"filter": { "Users.name": null },
"update": { $set: { "Users.$.name": "missing" } },
}
},
{ updateMany :
{
"filter": { "Users.place_of_birth": null },
"update": { $set: { "Users.$.place_of_birth": "missing" } },
}
}
] )
}
Try update with aggregation pipeline starting from MongoDB 4.2,
$map to iterate loop of Users array
$objectToArray to convert current object in $map to array key-value pair
$map to iterate loop of above converted array
$ifNull to check if value is null then replace Missing otherwise remain same
$arrayToObject convert above key-value array to object format
db.collection.update({},
[{
$set: {
Users: {
$map: {
input: "$Users",
in: {
$arrayToObject: {
$map: {
input: { $objectToArray: "$$this" },
in: {
k: "$$this.k",
v: { $ifNull: ["$$this.v", "Missing"] }
}
}
}
}
}
}
}
}],
{ multi: true }
)
Playground
MongoDB version 3.2 or above:
set default value for replacement in variable nullReplace
find() query to get all documents from your collection and loop through forEach
for loop of user object and check condition if value is null then replace nullReplace variable
return user oibject
updateOne() to update updated Users array in your collection
var nullReplace = "Missing";
db.collection.find({}).forEach(function(doc) {
var Users = doc.Users.map(function(u) {
for (var u in userObj) {
if (userObj[u] === null) userObj[u] = nullReplace;
}
return userObj;
})
db.collection.updateOne({ _id: doc._id }, { $set: { Users: Users } });
});
I am trying to delete the duplicate object inside the array in multiple documents in Mongodb.
I try many ways but not able to fix
Document Structure:-
{
"_id" : ObjectId("5a544fe234602415114601d3"),
"GstDetails" : [
{
"_id" : ObjectId("5e4837374d62f4c95163908e"),
"StateId" : "1",
"GstIn" : "33ABFFM1655H1ZF",
"StateDesc" : "TAMIL NADU",
"CityDesc" : "CHENNAI"
},
{
"_id" : ObjectId("5e4837484d62f4c9516395e8"),
"StateId" : "1",
"GstIn" : "33ABFFM1655H1ZF",
"StateDesc" : "TAMIL NADU",
"CityDesc" : "CHENNAI"
}
]
}
Like that many more documents
I tried:-
db.Supplier.find({ "GstDetails": { $size: 2 } }).limit(1).forEach(function (doc) {
var stateId;
doc.GstDetails.forEach(function (data) {
if (data.StateId == stateId) {
pull doc.GstDetails[0];
} else {
stateId = data.StateId
}
print(JSON.stringify(doc));
});
db.Supplier.save(doc)
});
Check if aggregation below meets your requirements:
db.Supplier.aggregate([
{
$unwind: "$GstDetails"
},
{
$group: {
_id: {
_id: "$_id",
StateId: "$GstDetails.StateId"
},
GstDetails: {
$push: "$GstDetails"
}
}
},
{
$addFields: {
GstDetails: {
$slice: [
"$GstDetails",
1
]
}
}
},
{
$unwind: "$GstDetails"
},
{
$group: {
_id: "$_id._id",
GstDetails: {
$push: "$GstDetails"
}
}
}
])
MongoPlayground
Note: This read-only query. If it is OK, you need to add as last stage below operator (once you execute it, it will update your documents, no rollback available):
{$out: "Supplier"}
How do we find keys which do not exist in collection.
Given an input list of keys ['3321', '2121', '5647'] , i want to return those that do not exist in the collection :
{ "_id" : { "$oid" : "5e2993b61886a22f400ea319" }, "scrip" : "5647" }
{ "_id" : { "$oid" : "5e2993b61886a22f400ea31a" }, "scrip" : "3553" }
So the expected output is ['3321', '2121']
This aggregation gets the desired output (works with MongoDB version 3.4 or later):
INPUT_ARRAY = ['3321', '2121', '5647']
db.test.aggregate( [
{
$match: {
scrip: {
$in: INPUT_ARRAY
}
}
},
{
$group: {
_id: null,
matches: { $push: "$scrip" }
}
},
{
$project: {
scrips_not_exist: { $setDifference: [ INPUT_ARRAY, "$matches" ] },
_id: 0
}
}
] )
The output:
{ "scrips_not_exist" : [ "3321", "2121" ] }
This is my my data in Mongodb
{
"d" : {
"results" : [
{
"slack_id" : "RAGHU#TN.COM",
"connector_id" : "GRECLNT900",
"sys_role" : "DEV",
"user_id" : "RAGHU"
},
{
"slack_id" : "RAGHU#TN.COM",
"connector_id" : "GRECLNT900",
"sys_role" : "PRD",
"user_id" : "RAGHU",
"question" : "What is your favorite color?",
"Answer" : "Orange"
},
]
}
}
If i am giving RAGHU#TN.COM. then i want display sys_role. Output like this[DEV, PRD]
I am trying this way
x = mydb.mycollection.distinct("sys-role")
But I get an empty array like [ ]
You have to treat the cursor as a reference(personally I see it as a reference in C), and then de-reference it to see the result.(What is inside the address)
For the specific column, here is the result from command prompt:
my_cursor = mydb.mycollection.distinct("sys-role")
for x in my_cursor:
print('{0}'.format(x['sys_role']))
The distinct operator is not inter-operatable thus it's hard to filter by slack_id first. I would recommande using aggregation pipelines.
Here is an example.
[
{
'$match': {
'slack_id': 'RAGHU#TN.COM'
}
}, {
'$group': {
'_id': 'slack_id',
'result': {
'$addToSet': 'sys_role'
}
}
}
]
With this pipeline, your sys_role set will be in the .result field.
Using Mongo aggregation query you will get required result set. Try this:
db.collection.aggregate([
{
"$match": {
"d.results.slack_id": "RAGHU#TN.COM"
}
},
{
$group: {
_id: "$d.results.slack_id",
sys_role: {
$push: "$d.results.sys_role"
}
}
}
])
db.getCollection("collection").aggregate(
// Pipeline
[
// Stage 1
{
$project: {
results: {
$filter: {
input: "$d.results",
as: "item",
cond: { $eq: [ "$$item.slack_id", 'RAGHU#TN.COM' ] }
}
}
}
},
// Stage 2
{
$unwind: {
path : "$results",
preserveNullAndEmptyArrays : false // optional
}
},
// Stage 3
{
$group: {
_id:'$results.slack_id',
sys_roles:{$addToSet:'$results.sys_role'}
}
},
]
);
I have the following dataset. I need to group them by Account, and then turn the Element_Fieldname into a column.
var collection = [
{
Account:12345,
Element_Fieldname:"cars",
Element_Value:true
},
{
Account:12345,
Element_Fieldname:"boats",
Element_Value:false
}
]
This was my attempt to convert rows to columns, but its not working.
db.getCollection('my_collection').aggregate([{
$match : {
Element_Fieldname : {
$in : ["cars", "boats"]
}
}
}, {
$group : {
_id : "$Account",
values : {
$addToSet : {
field : "$Element_Fieldname",
value : "$Element_Value"
}
}
}
}, {
$project : {
Account : "$_id",
cars : {
"$cond" : [{
$eq : ["$Element_Fieldname", "cars"]
}, "$Element_Value", null]
},
boats : {
"$cond" : [{
$eq : ["$Element_Fieldname", "day_before_water_bottles"]
}, "$Element_Value", null]
},
}
}
])
This just gives me null in my cars and boats fields. Any help would be great.
And this is my desired results:
var desiredResult = [
{
Account:12345,
cars:true,
boats:false
}
]
this is a big tricky but you will get what you need :-)
please add $match on the top of aggregation pipeline
db.collection.aggregate([{
$project : {
_id : 0,
"Account" : 1,
car : {
$cond : [{
$eq : ["$Element_Fieldname", "cars"]
}, "$Element_Value", null]
},
boats : {
$cond : [{
$eq : ["$Element_Fieldname", "boats"]
}, "$Element_Value", null]
},
}
},
{
$group : {
_id : "$Account",
carData : {
$addToSet : "$car"
},
boatsData : {
$addToSet : "$boats"
}
}
}, {
$unwind : "$carData"
}, {
$match : {
carData : {
$ne : null
}
}
}, {
$unwind : "$boatsData"
}, {
$match : {
boatsData : {
$ne : null
}
}
},
])
and result
{
"_id" : 12345,
"carData" : true,
"boatsData" : false
}
It is not possible to do the type of computation you are describing with the aggregation framework, however there is a proposed $arrayToObject expression which will give you the functionality to peek into the key names, and create new key/values dynamically.
For example, you could do
db.collection.aggregate([
{
"$match": { "Element_Fieldname":{ "$in": ["cars", "boats"] } }
},
{
"$group": {
"_id": "$Account",
"attrs": {
"$push": {
"key": "$Element_Fieldname",
"val": "$Element_Value"
}
}
}
},
{
"$project": {
"Account": "$_id",
"_id": 0,
"newAttrs": {
"$arrayToObject": {
"$map": {
"input": "$attrs",
"as": "el",
in: ["$$el.key", "$$el.val"]
}
}
}
}
},
{
"$project": {
"Account": 1,
"cars": "$newAttrs.cars",
"boats": "$newAttrs.boats"
}
}
])
Vote for this jira ticket https://jira.mongodb.org/browse/SERVER-23310 to get this feature.
As a workaround, mapreduce seems like the available option. Consider running the following map-reduce operation:
db.collection.mapReduce(
function() {
var obj = {};
obj[this.Element_Fieldname] = this.Element_Value;
emit(this.Account, obj);
},
function(key, values) {
var obj = {};
values.forEach(function(value) {
Object.keys(value).forEach(function(key) {
obj[key] = value[key];
});
});
return obj;
},
{ "out": { "inline": 1 } }
)
Result:
{
"_id" : 12345,
"value" : {
"cars" : true,
"boats" : false
}
}