adding data in fields of a single document in mongodb - mongodb

I have this data:
_id : 1
status:1,
name:"name",
details:
{
crm:115,
webs:
{ tag:"blog" , url:"http://..."},
contacts:
{
_id:1,
name:"me",
phones:
{ tag:"home", number:"123..." },
{tag:"mobile", number:"123456789"}
}
}
I want one more entry in "phones" with {tag:office", number:"9823..."}
What would be the command/query for that?

You can easily push this into the array with the following query (I had to modify the JSON you pasted, as it was not valid a little):
db.collection.drop();
db.collection.insert( {
_id : 1,
status: 1,
name: "name",
details: {
crm:115,
webs: {
tag:"blog",
url:"http://..."
},
contacts: {
_id: 1,
name: "me",
phones: [
{ tag: "home", number: "123..." },
{ tag:"mobile", number:"123456789" }
]
}
}
} );
db.collection.update(
{ _id: 1 },
{ $push : { 'details.contacts.phones' : { tag:"office", rname:"9823" } } }
);
db.collection.find().pretty();
{
"_id" : 1,
"details" : {
"contacts" : {
"_id" : 1,
"name" : "me",
"phones" : [
{
"tag" : "home",
"number" : "123..."
},
{
"tag" : "mobile",
"number" : "123456789"
},
{
"tag" : "office",
"rname" : "9823"
}
]
},
"crm" : 115,
"webs" : {
"tag" : "blog",
"url" : "http://..."
}
},
"name" : "name",
"status" : 1
}

The value of the $push operator must refer to the array to be updated. So when the array field is embedded in other documents you need to use dot notation like this:
db.abcd.update({_id: 1},
{$push: {"details.contacts.phones": {tag:"office", rname:"9823"}}});

Related

how to push object except duplicate object in mongoDB

I want to add an object to an array.
But I want to remove duplicate objects.
here is mongoDB JSON
{
"_id" : ObjectId("5e61ef7e003307047c13329d"),
"subject" : "Grammar",
"deletedYn" : false,
"answerList" : [
{
"_id" : ObjectId("5e61ef8c003307047c1332a4"),
"sentence" : "hi gui, nice to meet you",
"answer" : [
{
"_id" : ObjectId("5e61f15cd863e104a8f66770"),
"word" : "nice",
"index" : 3
}
]
},
{
"_id" : ObjectId("5e61ef8c003307047c1332a5"),
"sentence" : "how to make a project.",
"answer" : []
}
],
"passageId" : "123",
}
So I want to push answer in answerList.
Below is the code I created, but it contains duplicate objects.
const update = await PassageGrammar.updateOne(
{
_id: passageGrammarId,
answerList: { $elemMatch: { _id: sentenceId } },
},
{
$addToSet: {
'answerList.$.answer': {
word: answerList.word,
index: answerList.index,
},
},
},
);
but It is not working (it has duplicate set) like that:
"answer" : [
{
"_id" : ObjectId("5e61f15cd863e104a8f66770"),
"word" : "nice",
"index" : 3
},
{
"_id" : ObjectId("5e61f15cd863e104a8f66770"),
"word" : "nice",
"index" : 3
}
]
How can I change it?
Try Creating a unique index each time and droping the duplicates if they exist
const update = await PassageGrammar.updateOne(
{
_id: passageGrammarId,
answerList: { $elemMatch: { _id: sentenceId } },
},
{
$addToSet: {
'answerList.$.answer': {
word: answerList.word,
index: answerList.index,
},
},
},
{ unique:true, dropDups:true }
);

How to get the last element in a sub document in mongodb

This is a sample mongo document i have.
{
"_id" : ObjectId("someid"),
"doc": {
subDoc: [{
content: [{name: 'aaa'}, {name: 'bbb'}, {name: 'ccc'}
},
{
content: [{name: 'aaa'}, {name: 'bbb'}, {name: 'ccc'}
},
{
content: [{name: 'aaa'}, {name: 'bbb'}, {name: 'ccc'}
}]
}
}
I need to get the last element of the content which is nested array as mentioned in the above sample. I tried using aggregate with $project, still i was not able to get the result.
Expected result:
{
"_id" : ObjectId("someid"),
"doc": {
subDoc: [{
content: {name: 'ccc'}
},
{
content: {name: 'ccc'}
},
{
content: {name: 'ccc'}
}]
}
}
Please try this :
db.yourCollectionName.aggregate([{
"$project": {
"doc.subDoc": {
$map:
{
input: "$doc.subDoc",
as: "each",
in: { content: { "$arrayElemAt": ["$$each.content", -1] } }
}
}
}
}])
Collection Data :
/* 1 */
{
"_id" : ObjectId("5e0e6d1f400289966ece90d8"),
"doc" : {
"subDoc" : [
{
"content" : [
{
"name" : "aaa"
},
{
"name" : "bbb"
},
{
"name" : "ccc"
}
]
},
{
"content" : [
{
"name" : "aaa"
},
{
"name" : "bbb"
},
{
"name" : "ccc"
}
]
},
{
"content" : [
{
"name" : "aaa"
},
{
"name" : "bbb"
},
{
"name" : "ccc"
}
]
}
]
}
}
Result :
/* 1 */
{
"_id" : ObjectId("5e0e6d1f400289966ece90d8"),
"doc" : {
"subDoc" : [
{
"content" : {
"name" : "ccc"
}
},
{
"content" : {
"name" : "ccc"
}
},
{
"content" : {
"name" : "ccc"
}
}
]
}
}
Use $slice operator to get the last item from an array in the collection.
{
"$project" : {
"_id" : 1,
"subDoc": { "$slice": [ "$doc.subDoc", -1 ] }
}
}

Update nested array element in mongodb

In my case I have mongodb document with nested arrays and I need to update attributes array elements. My mongodb document as follows.
{
"_id" : ObjectId("5dca9c5ece5b91119746eece"),
"updatedAt" : ISODate("2019-11-20T11:48:55.339Z"),
"attributeSet" : [
{
"attributeSetName" : "test-0",
"type" : "Product",
"id" : "1574158032603",
"updatedAt" : ISODate("2019-11-19T10:10:53.783Z"),
"createdAt" : ISODate("2019-11-19T10:07:20.084Z"),
"attributes" : [
{
"attributeName" : "test-attribute",
"defaultValue" : 123,
"isRequired" : false,
"id" : "1574221129398"
},
{
"attributeName" : "test-attribute-02",
"defaultValue" : 456,
"isRequired" : false,
"id" : "1574250533840"
}
]
},
{
"attributeSetName" : "test-1",
"type" : "Product",
"id" : "1574158116355",
"updatedAt" : ISODate("2019-11-19T10:08:37.251Z"),
"createdAt" : ISODate("2019-11-19T10:08:37.251Z"),
"attributes" : []
}
]
}
I need to update element in attributes array and get the updates document into result object. This is what I tried so far.
const result = await this.model.findOneAndUpdate(
{
_id: settingsToBeUpdated._id,
"attributeSet": {
$elemMatch: {
"attributeSet.id": attributeSetId,
"attributes": {
$elemMatch: {
'attributes.id': id
}
}
}
}
},
{
$set: {
'attributeSet.$[outer].attributes.$[inner].attributeName': attributeDto.attributeName,
'attributeSet.$[outer].attributes.$[inner].defaultValue': attributeDto.defaultValue,
'attributeSet.$[outer].attributes.$[inner].isRequired': attributeDto.isRequired,
}
},
{
"arrayFilters": [
{ "outer.id": attributeSetId },
{ "inner.id": id }
]
}
);
It does not update the model. I refered to this link, but it does not help.
Any suggestion would be appreciated.
Couple of rectification required on the query, otherwise its almost there. The update is not working because $elemMatch for attributeSet (array of docs) field is to happen on id property of those docs to filter and not on attributeSet.id, it woudn't figure what it is. And nested elemMatch is not required, simply use dot notation.
To debug you can try it out with a find query.
Query (Shell):
db.collection.findOneAndUpdate(
{
_id: settingsToBeUpdated._id,
attributeSet: {
$elemMatch: {
id: attributeSetId,
"attributes.id": id
}
}
},
{
$set: {
"attributeSet.$[as].attributes.$[a].attributeName":
attributeDto.attributeName,
"attributeSet.$[as].attributes.$[a].defaultValue":
attributeDto.defaultValue,
"attributeSet.$[as].attributes.$[a].isRequired": attributeDto.isRequired
}
},
{
arrayFilters: [{ "as.id": attributeSetId }, { "a.id": id }],
returnNewDocument: true
}
);

MongoDB query with aggregation

I'm using MongoDB and I have this data model for my collection 'book':
{
"_id" : ObjectId("5b32566e2796789e15f18cbe"),
"service" : ObjectId("5b32550fe66a2c2d781a021e"),
"state" : "paid",
"observations" : "",
"datetime_paid" : "",
"price" : 50,
"responsible" : {
"name" : "Mark",
"surname" : "Beckam",
"birthdate" : "",
"email" : "mark#example.com",
"phone" : ""
},
"updates" : [],
"tickets" : [
{
"passenger" : {
"name" : "Mark",
"surname" : "Beckam",
"birthdate" : "",
"email" : "mark#beckam.com",
"phone" : "666 666 666"
},
"qr_id" : "pwenci3y32m",
"roundtrip" : "ida_vuelta",
"price" : 50,
}
]
}
Now I'm trying to query all the tickets and the responsible from every book that matches with a service id. My approach is use aggregate this:
db.getCollection('books').aggregate([
{ $match: { service: ObjectId("5b32550fe66a2c2d781a021e") } },
{
$group: {
_id: 0,
tickets: { $push: "$tickets" }
}
},
{
$project: {
tickets: {
$reduce: {
input: "$tickets",
initialValue: [],
in: { $setUnion: ["$$value", "$$this"] }
}
}
}
}
]);
With that query I'm returning the full list of the tickets throught differents books, but I also need the responsible data (that is in every book) in each ticket:
{
"_id" : 0.0,
"tickets" : [
{
"passenger" : {
"name" : "Mark",
"surname" : "Beckam",
"birthdate" : "",
"email" : "mark#beckam.com",
"phone" : "666 666 666"
},
"qr_id" : "",
"roundtrip" : "ida_vuelta",
"price" : 50,
// I need here the responsible field associated to the book that
contains that ticket
}
]
}
Thank you in advance
How about this:
db.getCollection('books').aggregate([
{ $match: { service: ObjectId("5b32550fe66a2c2d781a021e") } },
{ $addFields: { "tickets.responsible": "$responsible" } }, // push the responsible information into the "tickets" array
{ $unwind: "$tickets" }, // flatten out all "tickets" to individual documents
{
$group: {
_id: 0,
tickets: { $addToSet: "$tickets" } // addToSet will eliminate duplicate entries
}
}
]);

Meteor MongoDB return single object

{
"_id" : "R9zbu8oTWwgxT5eCR",
"practiceSetup" : {
"operatoriesSettings" : [
{
"name" : "Operatory 1",
"description" : "Room 1",
"status" : true
},
{
"name" : "Operatory 2",
"description" : "Room B",
"status" : true
}
],
}
I'm trying to lookup object that has name "Operatory 1" and only return that object.
Here is what I have tried,
Practice.findOne({ _id: 'R9zbu8oTWwgxT5eCR' }, { "$elemMatch": { "practiceSetup.operatoriesSettings": { "name": "Operatory 1" } } } );
However this will return the whole document, not that particular object, not sure what I'm doing wrong here.
You can try out this,
Practice.findOne({ _id: 'R9zbu8oTWwgxT5eCR' }).map(function(u){
return u.practiceSetup
.operatoriesSettings
.filter( m => m.name == "Operatory 1");
})
This will return:
[
[
{
"name" : "Operatory 1",
"description" : "Room 1",
"status" : true
}
]
]
That's how $elemMatch projection works:
The $elemMatch operator limits the contents of an field from the query results to contain only the first element matching the $elemMatch condition.
If you only need that specific subdocument, you get it using JS:
const doc = Practice.findOne(
{ _id: 'R9zbu8oTWwgxT5eCR' },
{
$elemMatch: {
'practiceSetup.operatoriesSettings': {
name: 'Operatory 1'
}
}
}
);
const operator1 = doc.practiceSetup.operatoriesSettings[0];
I found that using "Projections" will return matched object off an array.
Query:
{ "_id": "R9zbu8oTWwgxT5eCR", "practiceSetup.operatoriesSettings": { $elemMatch: {'name': 'Operatory 1'} } }
Projection
{"practiceSetup.operatoriesSettings.$.name": 1}
Full Query:
db.Practice.findOne({
"_id" : "R9zbu8oTWwgxT5eCR",
"practiceSetup.operatoriesSettings" : {
"$elemMatch" : {
"name" : "Operatory 1"
}
}
}, {
"practiceSetup.operatoriesSettings.$.name" : 1.0
});
Returns
{
"_id" : "R9zbu8oTWwgxT5eCR",
"practiceSetup" : {
"operatoriesSettings" : [
{
"name" : "Operatory 1",
"description" : "Room 1",
"status" : true
}
]
}
}
Meteor
When using Meteor methods, you will need to use "fields" when using Projections.
return Practice.findOne({
"_id": "R9zbu8oTWwgxT5eCR",
"practiceSetup.operatoriesSettings": {
"$elemMatch": {
"name": data
}
}
},
{ fields: { 'practiceSetup.operatoriesSettings.$.name': 1, _id: 0 } }
);