Meteor MongoDB return single object - mongodb

{
"_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 } }
);

Related

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
}
);

How to use Group By and concat fields as string

I am novice at MongoDB 3.2,
Consider below example,
{ "_id" : "2tLX8ALYfRvbgiurZ", "service" : "GSTR 1" }
{ "_id" : "2tLX8ALYfRvbgiurZ", "service" : "GSTR 2" }
{ "_id" : "2tLX8ALYfRvbgiurZ", "service" : "GSTR 3" }
How can I use Group By _id and create single field with all document fields being concatenated, below is expected output,
{ "_id" : "2tLX8ALYfRvbgiurZ", "service" : "GSTR 1, GSTR 2, GSTR 3" }
I used below but it gives array,
db.getCollection('Clients').aggregate(
[
{
$group : {
_id : "$_id",
services : "$services"
}
}
]
).map( doc =>
Object.assign(
doc,
{ "services": doc.services.join(",") }
)
);
it gives output as
[{ "_id" : "2tLX8ALYfRvbgiurZ", "service" : "GSTR 1, GSTR 2, GSTR 3" }]
It's working fine i checked
db.client.insert([
{ "id" : "2tLX8ALYfRvbgiurZ", "service" : "GSTR 1" },
{ "id" : "2tLX8ALYfRvbgiurZ", "service" : "GSTR 2" },
{ "id" : "2tLX8ALYfRvbgiurZ", "service" : "GSTR 3" }
]);
db.getCollection('client').aggregate(
[
{
$group: {
_id:"$id",
myfield: {$push: {$concat: ["$service"]}}
}
},
{
$project:{
"results": {
$reduce: {
input: "$myfield",
initialValue: '',
in: {$concat: ["$$value", ", ", "$$this"]}
}
}
}
}
]
);
docIwant = db.getCollection('Clients').aggregate(
[
{
$group : {
_id : "$_id",
services : "$services"
}
}
]
).map( doc =>
Object.assign(
doc,
{ "services": doc.services.join(",") }
)
);
return docIwant[0].

Update a nested array objects in a different collection and position in MongoDB

I have a douments like as follows.
How do I update a skillcluster name. Suppose the other document has name :"c" in 4th position.
{
Job: {
post: { name:"x" }
skill: {
skillcluster: [
{name:"c++",id:"23"},
{name:"c",id:"898"}
]
}
}
}
{
Job: {
post: { name:"x" }
skill: {
skillcluster: [
{name:"c++",id:"23"},
{name:"java"},
{name:"python"},
{name:"c",id:"898"}
]
}
}
}
You need to query to match the "name" field at the embedded level of the document using "dot notation", and then pass that match with the positional $ operator within the update:
db.collection.update(
{ "Job.skill.skillcluster.name": "c" },
{ "$set": { "Job.skill.skillcluster.$.name": "Simple C"}},
{ "multi": true }
)
Also use the "multi" flag to match and update more than one document.
The result will be:
{
"_id" : ObjectId("55dbfd0ed96d655eb0ed2b4f"),
"Job" : {
"post" : {
"name" : "x"
},
"skill" : {
"skillcluster" : [
{
"name" : "c++",
"id" : "23"
},
{
"name" : "Simple C",
"id" : "898"
}
]
}
}
}
{
"_id" : ObjectId("55dbfd0ed96d655eb0ed2b50"),
"Job" : {
"post" : {
"name" : "x"
},
"skill" : {
"skillcluster" : [
{
"name" : "c++",
"id" : "23"
},
{
"name" : "java"
},
{
"name" : "python"
},
{
"name" : "Simple C",
"id" : "898"
}
]
}
}
}

MongoDB nested group?

I'm trying to implement a nested group query in mongodb and I'm getting stuck trying to add the outer group by. Given the below (simplified) data document:
{
"timestamp" : ISODate(),
"category" : "movies",
"term" : "my movie"
}
I'm trying to achieve a list of all categories and within the categories there should be the top number of terms. I would like my output something like this:
[
{ category: "movies",
terms: [ { term: "movie 1", total: 5000 }, { term: "movie 2", total: 200 } ... ]
},
{ category: "sports",
terms: [ { term: "football 1", total: 4000 }, { term: "tennis 2", total: 250 } ... ]
},
]
My 'inner group' is as shown below, and will get the top 5 for all categories:
db.collection.aggregate([
{ $match : { "timestamp": { $gt: ISODate("2014-08-27") } } },
{ $group : { _id : "$term", total : { $sum : 1 } } },
{ $sort : { total : -1 } },
{ $limit: 5 }
]);
// Outputs:
{ "_id" : "movie 1", "total" : 943 }
{ "_id" : "movie 2", "total" : 752 }
How would I go about implementing the 'outer group'?
Additionally sometimes the above aggregate]ion returns a null value (not all documents have a term value). How do I go about ignoring the null values?
thanks in advance
You will need two groups in this case. The first group generates a stream of documents with one document per term and category:
{ $group : {
_id : {
category: "$category",
term: "$term",
},
total: { $sum : 1 }
}
}
A second group will then merge all documents with the same term into one, using the $push operator to merge the categories into an array:
{ $group : {
_id : "$_id.category",
terms: {
$push: {
term:"$_id.term",
total:"$total"
}
}
}
}
Query:
db.getCollection('orders').aggregate([
{$match:{
tipo: {$regex:"[A-Z]+"}
}
},
{$group:
{
_id:{
codigo:"1",
tipo:"$tipo",
},
total:{$sum:1}
}
},
{$group:
{
_id:"$_id.codigo",
tipos:
{
$push:
{
tipo:"$_id.tipo",
total:"$total"
}
},
totalGeneral:{$sum:"$total"}
}
}
]);
Response:
{
"_id" : "1",
"tipos" : [
{
"tipo" : "TIPO_01",
"total" : 13.0
},
{
"tipo" : "TIPO_02",
"total" : 2479.0
},
{
"tipo" : "TIPO_03",
"total" : 12445.0
},
{
"tipo" : "TIPO_04",
"total" : 12445.0
},
{
"tipo" : "TIPO_05",
"total" : 21.0
},
{
"tipo" : "TIPO_06",
"total" : 21590.0
},
{
"tipo" : "TIPO_07",
"total" : 1065.0
},
{
"tipo" : "TIPO_08",
"total" : 562.0
}
],
"totalGeneral" : 50620.0
}

adding data in fields of a single document in 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"}}});