Mongodb: How to create an entry in array if not present - mongodb

I have a document structure like the following:
{
"_id" : ObjectId("58be819769f922b8427e7f10"),
"id" : "58be7a4cfc13ae51020002cc",
"source" : "facebook",
"source_id" : "b37f5e43-ea3f-46d2-b2b0-b8f55e923933",
"data" : [
{
"date" : "5/8/2016",
"topics" : [
"Vectorworks",
"Statistics",
"Flight Planning",
"HTK",
"Custom Furniture"
]
},
{
"date" : "7/22/2016",
"topics" : [
"FDR",
"NS2",
"Power Plants"
]
},
{
"date" : "12/23/2016",
"topics" : [
"LTV",
"MMS"
]
}
]
}
Now, If I want to append more topics to the date "12/23/2016", I can do that using:
db.revision.findOneAndUpdate({id: '58be7a4cfc13ae51020002cc', data: {$elemMatch: {date: '03/17/2017'}}}, {$push: {'data.$.topics': 'java lambda'}},{upsert: true})
But, how can I add a new object, to data array when the entry for a date is not present?

First insert an empty data doc for the required date field, if its not already present
db.revision.update( {id: '58be7a4cfc13ae51020002cc', "data.date" : {$ne : '03/17/2017' }} ,
{$addToSet : {"data" : {'date' : '03/17/2017'}} } ,
false , //<-- upsert
true ); //<-- multi
Then update the same using the update query as devised by you
db.revision.findOneAndUpdate({id: '58be7a4cfc13ae51020002cc', data: {$elemMatch: {date: '03/17/2017'}}}, {$push: {'data.$.topics': 'java lambda'}},{upsert: true})

Related

MongoDB: How to get the object names in collection?

and think you in advance for the help. I have recently started using mongoDB for some personal project and I'm interested in finding a better way to query my data.
My question is: I have the following collection:
{
"_id" : ObjectId("5dbd77f7a204d21119cfc758"),
"Toyota" : {
"Founder" : "Kiichiro Toyoda",
"Founded" : "28 August 1937",
"Subsidiaries" : [
"Lexus",
"Daihatsu",
"Subaru",
"Hino"
]
}
}
{
"_id" : ObjectId("5dbd78d3a204d21119cfc759"),
"Volkswagen" : {
"Founder" : "German Labour Front",
"Founded" : "28 May 1937",
"Subsidiaries" : [
"Audi",
"Volkswagen",
"Skoda",
"SEAT"
]
}
}
I want to get the object name for example here I want to return
[Toyota, Volkswagen]
I have use this method
var names = {}
db.cars.find().forEach(function(doc){Object.keys(doc).forEach(function(key){names[key]=1})});
names;
which gave me the following result:
{ "_id" : 1, "Toyota" : 1, "Volkswagen" : 1 }
however, is there a better way to get the same result and also to just return the names of the objects. Thank you.
I would suggest you to change the schema design to be something like:
{
_id: ...,
company: {
name: 'Volkswagen',
founder: ...,
subsidiaries: ...,
...<other fields>...
}
You can then use the aggregation framework to achieve a similar result:
> db.test.find()
{ "_id" : 0, "company" : { "name" : "Volkswagen", "founder" : "German Labour Front" } }
{ "_id" : 1, "company" : { "name" : "Toyota", "founder" : "Kiichiro Toyoda" } }
> db.test.aggregate([ {$group: {_id: null, companies: {$push: '$company.name'}}} ])
{ "_id" : null, "companies" : [ "Volkswagen", "Toyota" ] }
For more details, see:
Aggregation framework
$group
Accumulator operators
As a bonus, you can create an index on the company.name field, whereas you cannot create an index on varying field names like in your example.

What are the efficient query for mongodb if value exist on array then don't update and return the error that id already exist

I have an entry stored on my collection like this:
{
"_id" : ObjectId("5d416c595f19962ff0680dbc"),
"data" : {
"a" : 6,
"b" : [
"5c35f04c4e92b8337885d9a6"
]
},
"image" : "123.jpg",
"hyperlinks" : "google.com",
"expirydate" : ISODate("2019-08-27T06:10:35.074Z"),
"createdate" : ISODate("2019-07-31T10:24:25.311Z"),
"lastmodified" : ISODate("2019-07-31T10:24:25.311Z"),
"__v" : 0
},
{
"_id" : ObjectId("5d416c595f19962ff0680dbd"),
"data" : {
"a" : 90,
"b" : [
"5c35f04c4e92b8337885d9a7"
]
},
"image" : "456.jpg",
"hyperlinks" : "google.com",
"expirydate" : ISODate("2019-08-27T06:10:35.074Z"),
"createdate" : ISODate("2019-07-31T10:24:25.311Z"),
"lastmodified" : ISODate("2019-07-31T10:24:25.311Z"),
"__v" : 0
}
I have to write the query for push userid on b array which is under data object and increment the a counter which is also under data object.
For that, I wrote the Code i.e
db.collection.updateOne({_id: ObjectId("5d416c595f19962ff0680dbd")},
{$inc: {'data.a': 1}, $push: {'data.b': '124sdff54f5s4fg5'}}
)
I also want to check that if that id exist on array then return the response that following id exist, so for that I wrote extra query which will check and if id exist then return the error response that following id exist,
My question is that any single query will do this? Like I don't want to write Two Queries for single task.
Any help is really appreciated for that
You can add one more check in the update query on "data.b". Following would be the query:
db.collection.updateOne(
{
_id: ObjectId("5d416c595f19962ff0680dbd"),
"data.b":{
$ne: "124sdff54f5s4fg5"
}
},
{
$inc: {'data.a': 1},
$push: {'data.b': '124sdff54f5s4fg5'}
}
)
For duplicate entry, you would get the following response:
{ "acknowledged" : true, "matchedCount" : 0, "modifiedCount" : 0 }
If matched count is 0, you can show the error that the id already exists.
You can use the operator $addToSet to check if the element already exits in the array.
db.collection.updateOne({_id: ObjectId("5d416c595f19962ff0680dbd")},
{$inc: {'data.a': 1}, $addToSet: {'data.b': '124sdff54f5s4fg5'}}
)

MongoDB findOne() and updateOne() with embedded document

Assume I got the following document with embedded documents in arrays:
{
"name" : "test",
"children" : [
{
"name" : "child1",
"children" : [
{
"name" : "sub1",
"children" : [
{"name" : "c1"},
{"name" : "c2"},
{"name" : "c3"},
{"name" : "c4"},
{"name" : "c5"}
]
}
]
},
{
"name" : "child2",
"children" : [
{
"name" : "sub2",
"children" : [
{"name" : "c1"},
{"name" : "c2"},
{"name" : "c3"},
{"name" : "c4"}
]
},
{
"name" : "sub3",
"children" : [
{"name" : "c1"},
{"name" : "c2"},
{"name" : "c3"},
{"name" : "c4"}
]
},
{
"name" : "sub4",
"children" : [
{"name" : "c1"},
{"name" : "c2"},
{"name" : "c3"},
{"name" : "c4"}
]
}
]
}
]
}
As we can see, test has two children: child1 and child2, while a child has their own children like sub1, sub2, sub3, sub4, etc, and so on.
Currently, I have to update some of the children content, but I ran into the following two issues:
The first issue is about findOne(), when I accidentally made a typo (name child1 should be child2):
db.collection_name.findOne({
'name': 'test',
'children.name': 'child1', // -> should be `child2`
'children.children.name': 'sub2'
})
I can still get document 'test' correctly, but since I made a typo and sub2 is actually under child2, not child1, then how can I still get this correct findOne() result?
The second issue is about updateOne(), when I try to update child1's array children, it works good (the code is also like the sample from the official document):
db.collection_name.updateOne({
'name': 'test',
'children.name': 'child1' // locate child1
}, {
'$set':{'children.$.children':[some other content]}
})
But when I also try to update sub3's array (under child2), the same way failed with error cannot use the part (children of children.children.1.children) to traverse the element, and here is the code with issue:
db.collection_name.updateOne({
'name': 'test',
'children.name': 'child2',
'children.children.name': 'sub3' // locate sub3
}, {
'$set':{'children.children.$.children':[some other content]}
})
I will be really appreaciate if someone can help with these two issues, thanks in advance!
For your first issue, you can use $elemMatch to find the exact element.
db.collection_name.findOne({
'name':'test',
children:{
$elemMatch:{
'name':'child2',
'children.name':'sub2'
}
}
})
I guess that mongo does not support multiple nested array update, but if you know the subarray position you can use this approach.
db.collection_name.updateOne({
'name':'test',
children:{
$elemMatch:{
'name':'child2',
'children.name':'sub3'
}
}
}
, {
$set: {"children.$.children.1.children" :
[
{"name" : "c1"},
{"name" : "c2"},
{"name" : "c3"},
{"name" : "c4"}
]
}
}
)
First Issue: The query return results when querying array as the match is evaluated against all elements of array i.e first match 'children.name': 'child1' matches first element and second 'children.children.name': 'sub2' matches the second element.
Use $elemMatch(query) to match both the conditions to the same array element.
db.collection_name.findOne({
'name': 'test',
'children':{$elemMatch:{'name': 'child2', 'children.name': 'sub2'}}
})
Compare both the above approaches 1 & 2
Second issue: Mongodb 3.6 now supports multiple positional updates through arrayFilters expression.
The below query to replace all children of sub3 children.
db.collection_name.updateOne(
{'name': 'test'},
{'$set':{'children.$[first].children.$[second].children':[some data]}},
{'arrayFilters': [{ 'first.name': 'child2', 'second.name': 'sub3'}]}
)
The below query to push new child in the childrens of sub3 children.
db.collection_name.updateOne(
{'name': 'test'},
{'$push':{'children.$[first].children.$[second].children':new child data}},
{'arrayFilters': [{ 'first.name': 'child2', 'second.name': 'sub3'}]}
)

insert and update a field in MongoDB

I want to update all the collection field profile.email with emails.[0].address just something like this in SQL:
update dbo.users
set profile.email=emials.[0].address
I know this stupid code won't work on SQL either. Just put it to make it sensible for what I need.
it is something like this,but is not working:
db.getCollection('users').update(
{_id: "QYoHQkTuTXnEC6Pws"},
{$set:
{'profile.email': db.getCollection('users').aggregate({$match: {_id:'QYoHQkTuTXnEC6Pws'}},
{$project: {_id:0,email: {$arrayElemAt:
['$emails.address',0]}}})
}
}
)
the result is not true :
"email" : {
"_batch" : [
{
"email" : "deleted_sadaf#ham.com"
}
],
"_useReadCommands" : true,
"_cursorid" : NumberLong(0),
"_batchSize" : undefined,
"_ns" : "meteor.users",
"_db" : {
"_mongo" : {
"slaveOk" : true,
"host" : "localhost:3001",
"defaultDB" : "",
"_readMode" : "commands",
"_writeMode" : "commands"
},
"_name" : "meteor"
},
"_collName" : "users",
"_cursorHandle" : {}
}
You have to iterate over each document and update to achieve expected result.
db.users.find({}).forEach(function (user) {
db.users.update({_id: user._id}, {$set: {'profile.email': user.emails[0].address}});
});
db.users.update({_id: "doc id"},
{$set: {'profile.email': db.users.find({_id: "doc id"}).emails[0].address}
})
Your task has 2 steps:
1. get the email address, which is performed by "db.users.find"
2. update the document, which is performed by "db.users.update"

Updating an object in a nested array in Mongoose

Given the following schema
{
"_id" : ObjectId("55e0ecfa422d86f0a3b37b90"),
"username" : "Michael",
"hostedEvents" : [
{ "_id" : "1", "usersApplied" : [ ] }
],
"joinedEvents" : [
{ "confirmed" : false }
]
}
And I am trying to push into the usersApplied array the string "Lisa"
Right now I have a mongodb command that works but I want to do this in mongoose:
db.users.update({username:'Michael', "hostedEvents._id": "1"},
{$addToSet: {"hostedEvents.$.usersApplied": {"username":"Lisa"}}});
I tried to do it in mongoose this way:
Users.update({username:'Michael', "hostedEvents._id": "1"},
{$addToSet: {"hostedEvents.$.usersApplied": {"username":"Lisa"}}});
I am trying to follow the recommendation made here
I think you should use $push instead
Users.update({username:'Michael', "hostedEvents._id": "1"},
{$push: {"hostedEvents.$.usersApplied": {"username":"Lisa"}}});
That should work.