Mongodb : elemMatch - mongodb

My problem cans be described as the following.
Give the following data (copied from mongo manual), how can i find the document which has zipcode 63109 and has 2 studends named "John", and "Jeff". I try
db.schools.find( { zipcode: 63109 },
{ students: { $elemMatch: { name : {"John", "Jeff"} } } } )
But it doesn't work. Could you help me, please ?
Thank you in advanced
{
_id: 1,
zipcode: 63109,
students: [
{ name: "john", school: 102, age: 10 },
{ name: "jess", school: 102, age: 11 },
{ name: "jeff", school: 108, age: 15 }
]
}
{
_id: 2,
zipcode: 63110,
students: [
{ name: "ajax", school: 100, age: 7 },
{ name: "achilles", school: 100, age: 8 },
]
}
{
_id: 3,
zipcode: 63109,
students: [
{ name: "ajax", school: 100, age: 7 },
{ name: "achilles", school: 100, age: 8 },
]
}
{
_id: 4,
zipcode: 63109,
students: [
{ name: "barney", school: 102, age: 7 },
]
}

I don't think it's possible to use $elemMatch here
The only solution I see is:
db.schools.find( { zipcode: 63109, $and: [{"students.name": "john"}, {"students.name": "jeff"}]} )

Related

How to add a particular value for all records in the collection using aggregation

How to add a particular value for all records in the collection using aggregation :
Have a collection with data :
[
{
_id: "bmasndvhjbcw",
name: "lucas",
occupation: "scientist",
present_working:true,
age: 55,
location: "texas",
date:2019-11-25T01:00:00.000+00:00
},
{
_id: "bmasndvhjbcx",
name: "mark",
occupation: "scientist",
age: 45,
location: "texas",
date:2019-11-25T01:00:00.000+00:00
},
{
_id: "bmasndvhjbcq",
name: "cooper",
occupation: "physicist",
age: 69,
location: "texas",
date:2019-11-25T01:00:00.000+00:00
}
]
if date column exists for any of the records present_working:true should be added in the collection for the records
expected output:
[
{
_id: "bmasndvhjbcw",
name: "lucas",
occupation: "scientist",
present_working:true,
age: 55,
location: "texas",
date:2019-11-25T01:00:00.000+00:00
},
{
_id: "bmasndvhjbcx",
name: "mark",
occupation: "scientist",
present_working:true,
age: 45,
location: "texas",
date:2019-11-25T01:00:00.000+00:00
},
{
_id: "bmasndvhjbcq",
name: "cooper",
occupation: "physicist",
present_working:true,
age: 69,
location: "texas",
date:2019-11-25T01:00:00.000+00:00
}
]
There is no need to use aggregation for this.
You can do it simply this way:
collection.updateMany({
date: {
$exists: true
}
}, {
$set: {
present_working: true
}
})
If you want to do it with the aggregation framework:
collection.aggregate(
[
{
"$match" : {
"date" : {
$exists: true
}
}
},
{
"$addFields" : {
"present_working" : true
}
}
]
)
So if you wanted to return conditionally add a field based upon existence of other field in aggregation while reading data from database then try below query :
/** If `date` exists then it will add the field & assign value to true else `$$REMOVE` will help to not add the field at all,
* Additionally if you're ok to add this new field for docs where `date : null` then replace `$gt` with `$gte` */
db.collection.aggregate([
{
$addFields: {
present_working: { $cond: [ { "$gt": [ "$date", null ] }, true, "$$REMOVE" ] }
}
}
])
Test : mongoplayground
Ref : aggregation-pipeline

Remove a particular field for all documents in a collection using aggregation in mongoDB

How to Remove a particular value for all records in the collection using aggregation :
Have a collection with data :
[
{
_id: "bmasndvhjbcw",
name: "lucas",
occupation: "scientist",
present_working:true,
age: 55,
location: "texas"
},
{
_id: "bmasndvhjbcx",
name: "mark",
occupation: "scientist",
age: 45,
present_working:false,
location: "texas"
},
{
_id: "bmasndvhjbcq",
name: "cooper",
occupation: "physicist",
age: 69,
location: "texas",
present_working:false
}
]
Remove the rows in records for which there is present_working:false. Data need not be removed in the database, it should be only modified in the aggregation pipeline
Expected output after removing only present_working:false and present_working:false should be kept in database. :
[
{
_id: "bmasndvhjbcw",
name: "lucas",
occupation: "scientist",
present_working:true,
age: 55,
location: "texas"
},
{
_id: "bmasndvhjbcx",
name: "mark",
occupation: "scientist",
age: 45,
location: "texas"
},
{
_id: "bmasndvhjbcq",
name: "cooper",
occupation: "physicist",
age: 69,
location: "texas"
}
]
MongoDB version: 4.0
You can use $$REMOVE as part of $project:
db.collection.aggregate([
{
$project: {
_id: 1,
name: 1,
occupation: 1,
age: 1,
location: 1,
present_working: { $cond: [ { $eq: [ "$present_working", false ] }, "$$REMOVE", "$present_working" ] }
}
}
])
Mongo Playground

Remove a particular field for all documents in a collection using MongoDB aggregation

How to Remove a particular value for all records in the collection using aggregation :
Have a collection with data :
[
{
_id: "bmasndvhjbcw",
name: "lucas",
occupation: "scientist",
present_working:true,
age: 55,
location: "texas",
date:2019-11-25T10:49:36.534+00:00
},
{
_id: "bmasndvhjbcx",
name: "mark",
occupation: "scientist",
age: 45,
present_working:true,
location: "texas",
date:null
},
{
_id: "bmasndvhjbcq",
name: "cooper",
occupation: "physicist",
age: 69,
location: "texas",
date:null
}
]
Remove the rows in records for which there is date:null. Data need not be removed in the database, it should be only modified in the aggregation pipeline
Expected output after removing only date:null :
[
{
_id: "bmasndvhjbcw",
name: "lucas",
occupation: "scientist",
present_working:true,
age: 55,
location: "texas",
date:2019-11-25T10:49:36.534+00:00
},
{
_id: "bmasndvhjbcx",
name: "mark",
occupation: "scientist",
age: 45,
present_working:true,
location: "texas"
},
{
_id: "bmasndvhjbcq",
name: "cooper",
occupation: "physicist",
age: 69,
location: "texas"
}
]
MongoDB version: 4.0
You can use $ifNull operator & $$REMOVE to do that :
db.collection.aggregate([
{
$addFields: {
date: {
$ifNull: [
"$date",
"$$REMOVE"
]
}
}
}
])
Test : MongoDB-Playground

MongoDB .Need to count fileds of an array

I have a collection like below
{ _id: 1, zipcode: "63109", students: [
{ name: "john", school: 102, age: 10 },
{ name: "jess", school: 102, age: 11 },
{ name: "jeff", school: 108, age: 15 }
] }
{ _id: 2, zipcode: "63110", students: [
{ name: "ajax", school: 100, age: 7 },
{ name: "achilles", school: 100, age: 8 },
] }
{ _id: 3, zipcode: "63109", students: [
{ school: 100, age: 7 },
{ school: 100, age: 8 },
] }
{ _id: 4, zipcode: "63109", students: [
{ name: "barney", school: 102, age: 7 },
{ name: "ruth", school: 102, age: 16 },
] }
Note:Some document doesnot have name field.
I want to findout the count of name field.
Kindly suggest query to find count in mongo
You can do this with aggregation framework with $unwind. Let say your collection name is test:
db.test.aggregate(
{$unwind: '$students'},
{$match:{'students.name':{$exists:1}}},
{$group:{_id: '$students.name', count:{$sum:1}}},
{$project:{tmp:{name:'$_id', count:'$count'}}},
{$group:{_id:'Total Names', total:{$sum:1}, data:{$addToSet:'$tmp'}}}
)
Hope this is what you want!
You need to use the aggregation framework in this case.
The first aggregation step would be to use $unwind to turn the arrays into streams of documents.
Then you can use $group as a second aggregation step with $sum:1 to get the count of every name.
db.collection.aggregate([
{
$unwind: "$students"
},
{
$group: {
_id:"$students.name",
count: { $sum:1 }
}
}
]
This is doable through the aggregation framework
I'll assume your collection is called mycoll:
db.mycoll.aggregate([
{$unwind:'$students'},
{$group:{_id:'$name', 'count':{$sum:1}},
{$match:{'students.name':{$exists:1}}}
]);
References:
Aggregations http://docs.mongodb.org/manual/tutorial/aggregation-zip-code-data-set/
Exists: http://docs.mongodb.org/manual/reference/operator/query/exists/
Summing '1' essentially turns it into a count.

Nested grouping with MongoDB

Given a database the form of
[
{ gender: "m", age: 1, name: "A" },
{ gender: "f", age: 2, name: "B" },
{ gender: "m", age: 3, name: "C" },
{ gender: "f", age: 1, name: "D" },
{ gender: "m", age: 2, name: "E" },
{ gender: "f", age: 3, name: "F" },
{ gender: "m", age: 1, name: "G" },
{ gender: "f", age: 2, name: "H" },
{ gender: "m", age: 3, name: "I" },
{ gender: "f", age: 1, name: "J" }
]
I want to first group by age and secondly group by gender so that I get a nested result looking something like
[{
_id: "1",
children: [
{ _id: "f" },
{ _id: "m" }
]
}, {
_id: "2",
children: [
{ _id: "f" },
{ _id: "m" }
]
}, {
_id: "3",
children: [
{ _id: "f" },
{ _id: "m" }
]
}]
Here is what I tried so far:
db.example.aggregate(
{ $group: { _id: "$age", children: { $addToSet: {
age: "$age", gender: "$gender", name: "$name"
}}}},
{ $group: { _id: "$children.gender"}}
)
But this returns an {_id: null} as its result. Is this possible and in case yes, how?
Something like this should do it;
db.example.aggregate(
{
$group: {
_id: { age: "$age", gender: "$gender" },
names: { $addToSet: "$name" }
}
},
{
$group: {
_id: { age: "$_id.age" },
children: { $addToSet: { gender: "$_id.gender", names:"$names" } }
}
}
)
...which gives the result;
{
"_id" : {
"age" : 1
},
"children" : [
{ "gender" : "m", "names" : [ "G", "A" ] },
{ "gender" : "f", "names" : [ "J", "D" ] }
]
},
...
If you want the age as _id as in your example, just replace the second grouping's _id by;
_id: "$_id.age",