MongoDB Return Inner Document From Array - mongodb

I am trying to fetch an element from an array in a document and only the element I don't want the entire document
I tried a different method but they all return the entire document
db.dept.find({"section.classes.CRN":"1901"}).limit(100)
db.dept.where("section.classes.CRN").eq("1901").limit(100)
json
{
"_id" : ObjectId("5d70ab0c280d6b8ebb850cc1"),
"name" : "Art Studio",
"abbr" : "ARS",
"section" : [
{
"type" : "Undergraduate Courses",
"classes" : [
{
"CRN" : "193",
"Course" : "ARS100",
"Sec" : "01",
"Title" : "Drawing I",
"Cr" : "3",
"Dates" : "8/26-12/19",
"Days" : "MR",
"Time" : "1230P-0320P",
"Loc" : "SAB 226",
"Instructor" : "Schuck",
"Attributes" : "",
"Avail" : "F"
},
{
"CRN" : "293",
"Course" : "ARS100",
"Sec" : "02",
"Title" : "Drawing I",
"Cr" : "3",
"Dates" : "8/26-12/19",
"Days" : "MR",
"Time" : "0330P-0620P",
"Loc" : "SAB 226",
"Instructor" : "Itty",
"Attributes" : "",
"Avail" : "F"
},
{...
I am trying to get this or something similar when searching for a set of CRN values
json
[ {
"CRN" : "193",
"Course" : "ARS100",
"Sec" : "01",
"Title" : "Drawing I",
"Cr" : "3",
"Dates" : "8/26-12/19",
...
"Instructor" : "Schuck",
"Attributes" : "",
"Avail" : "F"
}
]

Try using the aggregate pipeline to project double nested array as:
Input:
[
{
"_id": ObjectId("5d70ab0c280d6b8ebb850cc1"),
"name": "Art Studio",
"abbr": "ARS",
"section": [
{
"type": "Undergraduate Courses",
"classes": [
{
"CRN": "193",
"Course": "ARS100",
"Sec": "01",
"Title": "Drawing I",
"Cr": "3",
"Dates": "8/26-12/19",
"Days": "MR",
"Time": "1230P-0320P",
"Loc": "SAB 226",
"Instructor": "Schuck",
"Attributes": "",
"Avail": "F"
},
{
"CRN": "293",
"Course": "ARS100",
"Sec": "02",
"Title": "Drawing I",
"Cr": "3",
"Dates": "8/26-12/19",
"Days": "MR",
"Time": "0330P-0620P",
"Loc": "SAB 226",
"Instructor": "Itty",
"Attributes": "",
"Avail": "F"
}
]
}
]
}
]
Query:
hereafter unwinding section you can filter classes for CRN
db.collection.aggregate([
{
$unwind: "$section"
},
{
$project: {
name: 1,
abbr: 1,
"section.type": 1,
"section.classes": {
$filter: {
input: "$section.classes",
as: "item",
cond: {
$eq: [
"$$item.CRN",
"193"
]
}
}
}
}
},
{
$group: {
_id: "$_id",
section: {
$push: "$section"
}
}
}
])
Output:
you can manage your keys as you want in project for adding new keys or replacing them.
[
{
"_id": ObjectId("5d70ab0c280d6b8ebb850cc1"),
"section": [
{
"classes": [
{
"Attributes": "",
"Avail": "F",
"CRN": "193",
"Course": "ARS100",
"Cr": "3",
"Dates": "8/26-12/19",
"Days": "MR",
"Instructor": "Schuck",
"Loc": "SAB 226",
"Sec": "01",
"Time": "1230P-0320P",
"Title": "Drawing I"
}
],
"type": "Undergraduate Courses"
}
]
}
]

db.dept.find({"section.classes.CRN":"1901"},{"section.classes":1}).limit(100)
It's called projection in mongodb, you pass a second object in find query to specify which fields you want in result.
so according to your above case if you want name, and section in result you should pass something like this
db.dept.find({"section.classes.CRN":"1901"},{"name":1, "section":1}).limit(100)

Related

elemMatch doesn't retrieve data from [ ] brackets in MongoDB

Structure of collection:
{
"address": {
"building": "1007",
"coord": [ -73.856077, 40.848447 ],
"street": "Morris Park Ave",
"zipcode": "10462"
},
"borough": "Bronx",
"cuisine": "Bakery",
"grades": [
{ "date": { "$date": 1393804800000 }, "grade": "A", "score": 2 },
{ "date": { "$date": 1378857600000 }, "grade": "A", "score": 6 },
{ "date": { "$date": 1358985600000 }, "grade": "A", "score": 10 },
{ "date": { "$date": 1322006400000 }, "grade": "A", "score": 9 },
{ "date": { "$date": 1299715200000 }, "grade": "B", "score": 14 }
],
"name": "Morris Park Bake Shop",
"restaurant_id": "30075445"
}
The first query below doesn't return any results, second does. Why?
"coord": [ -73.856077, 40.848447 ] Here coord is an array of two elements so why isn't elemMatch returning anything?
> db.restaurants.find({address : { $elemMatch: { coord: {$lt : -95.754168}}}});
>
>
> db.restaurants.find({"address.coord" : {$lt : -95.754168}});
{ "_id" : ObjectId("5ed53c72c7494f71176bafb9"), "address" : { "building" : "3707", "coord" : [ -101.8945214, 33.5197474 ], "street" : "82 Street", "zipcode" : "11372" }, "borough" : "Queens", "cuisine" : "American ", "grades" : [ { "date" : ISODate("2014-06-04T00:00:00Z"), "grade" : "A", "score" : 12 }, { "date" : ISODate("2013-11-07T00:00:00Z"), "grade" : "B", "score" : 19 }, { "date" : ISODate("2013-05-17T00:00:00Z"), "grade" : "A", "score" : 11 }, { "date" : ISODate("2012-08-29T00:00:00Z"), "grade" : "A", "score" : 11 }, { "date" : ISODate("2012-04-03T00:00:00Z"), "grade" : "A", "score" : 12 }, { "date" : ISODate("2011-11-16T00:00:00Z"), "grade" : "A", "score" : 7 } ], "name" : "Burger King", "restaurant_id" : "40534067" }
{ "_id" : ObjectId("5ed53c72c7494f71176bb325"), "address" : { "building" : "15259", "coord" : [ -119.6368672, 36.2504996 ], "street" : "10 Avenue", "zipcode" : "11357" }, "borough" : "Queens", "cuisine" : "Italian", "grades" : [ { "date" : ISODate("2014-09-04T00:00:00Z"), "grade" : "A", "score" : 11 }, { "date" : ISODate("2014-03-26T00:00:00Z"), "grade" : "A", "score" : 8 }, { "date" : ISODate("2013-03-04T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2012-09-27T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2012-04-20T00:00:00Z"), "grade" : "A", "score" : 7 }, { "date" : ISODate("2011-11-23T00:00:00Z"), "grade" : "C", "score" : 34 } ], "name" : "Cascarino'S", "restaurant_id" : "40668681" }
{ "_id" : ObjectId("5ed53c72c7494f71176bb7c8"), "address" : { "building" : "60", "coord" : [ -111.9975205, 42.0970258 ], "street" : "West Side Highway", "zipcode" : "10006" }, "borough" : "Manhattan", "cuisine" : "Japanese", "grades" : [ { "date" : ISODate("2014-03-20T00:00:00Z"), "grade" : "A", "score" : 9 }, { "date" : ISODate("2013-06-28T00:00:00Z"), "grade" : "A", "score" : 11 }, { "date" : ISODate("2012-07-05T00:00:00Z"), "grade" : "A", "score" : 13 }, { "date" : ISODate("2011-07-27T00:00:00Z"), "grade" : "A", "score" : 2 } ], "name" : "Sports Center At Chelsea Piers (Sushi Bar)", "restaurant_id" : "40882356" }
The reason:
Your array doesn't contain valid element.
Smallest value in negative is the greatest value. So -73 is > than -95 and 40 is > -95.
Play
And other works. play
Both formats are valid. But data(mentioned coord) is also not matching the query in both the formats.
It is not finding the records because the schema would be different when translated.
For example: if you change your schema to the following, it will return the document:
{
"address": [{
"building": "1007",
"coord": [ -73.856077, 40.848447 ],
"street": "Morris Park Ave",
"zipcode": "10462"
}],
"borough": "Bronx",
"cuisine": "Bakery",
"grades": [
{ "date": { "$date": 1393804800000 }, "grade": "A", "score": 2 },
{ "date": { "$date": 1378857600000 }, "grade": "A", "score": 6 },
{ "date": { "$date": 1358985600000 }, "grade": "A", "score": 10 },
{ "date": { "$date": 1322006400000 }, "grade": "A", "score": 9 },
{ "date": { "$date": 1299715200000 }, "grade": "B", "score": 14 }
],
"name": "Morris Park Bake Shop",
"restaurant_id": "30075445"
}
Notice the square brackets around address sub-document.
Please follow this link for more reference https://docs.mongodb.com/manual/reference/operator/query/elemMatch/#array-of-embedded-documents

Mongodb aggregate with cond and query value

I'm new to mongodb. I need to know how it is possible to query item for set to the value with aggregate
Data
[
{
"_id" : "11111",
"parent_id" : "99",
"name" : "AAAA"
},
{
"_id" : "11112",
"parent_id" : "99",
"name" : "BBBB"
},
{
"_id" : "11113",
"parent_id" : "100",
"name" : "CCCC"
},
{
"_id" : "11114",
"parent_id" : "99",
"name" : "DDDD"
}
]
mongoshell
Assume $check is false
db.getCollection('test').aggregate(
[
{
"$group": {
"_id": "$id",
//...,
"item": {
"$last": {
"$cond": [
{"$eq": ["$check", true]},
"YES",
* * ANSWER **,
}
]
}
},
}
]
)
So i need the result for item is all the name contain with same parent_id as string of array
Expect result
[
{
"_id" : "11111",
"parent_id" : "99",
"name" : "AAAA",
"item" : ["AAAA","BBBB","DDDD"]
},
{
"_id" : "11112",
"parent_id" : "99",
"name" : "BBBB",
"item" : ["AAAA","BBBB","DDDD"]
},
{
"_id" : "11113",
"parent_id" : "100",
"name" : "CCCC",
"item" : ["CCCC"]
},
{
"_id" : "11114",
"parent_id" : "99",
"name" : "DDDD",
"item" : ["AAAA","BBBB","DDDD"]
}
]
Try this..
Sample live demo
db.collection.aggregate([
{
"$group": {
"_id": "$parent_id",
"item": {
"$push": "$name"
},
"data": {
"$push": {
"_id": "$_id",
"name": "$name"
}
}
}
},
{
"$unwind": "$data"
},
{
"$project": {
"_id": "$data._id",
"parent_id": "$_id",
"name": "$data.name",
"item": 1
}
}
])

mongodb aggregation with array

I have data like this:
{
"_id" : ObjectId("..."),
"name" : "Entry 1",
"time" : ISODate("2013-12-28T06:00:00.000Z"),
"value" : 100
},
{
"_id" : ObjectId("..."),
"name" : "Entry 2",
"time" : ISODate("2013-12-28T06:00:00.000Z"),
"value" : 200
},
{
"_id" : ObjectId("..."),
"name" : "Entry 1",
"time" : ISODate("2013-12-28T11:00:00.000Z"),
"value" : 110
},
{
"_id" : ObjectId("..."),
"name" : "Entry 2",
"time" : ISODate("2013-12-28T11:00:00.000Z"),
"value" : 230
},
{
"_id" : ObjectId("..."),
"name" : "Entry 3",
"time" : ISODate("2013-12-28T11:00:00.000Z"),
"value" : 25
},
{
"_id" : ObjectId("..."),
"name" : "Entry 4",
"time" : ISODate("2013-12-28T11:00:00.000Z"),
"value" : 15
}
I need the result grouped by time with percentage for each entry like this (group entries by volume "others" when entries for time period more than two, but it's not necessary):
{
"_id": ISODate("2013-12-28T11:00:00.000Z"),
"entries": [
{
"name": "Entry 1",
"percentage": 33.3
},
{
"name": "Entry 2",
"percentage": 66.6
},
]
},
{
"_id": ISODate("2013-12-28T06:00:00.000Z"),
"entries": [
{
"name": "Entry 1",
"percentage": 28.9
},
{
"name": "Entry 2",
"percentage": 60.5
},
{
"name": "Others",
"percentage": 10.5
}
]
}
So the request I was try:
db.collection.aggregate([
{
"$addFields": {
"full_datetime": {"$substr": ["$time", 0, 19]}
}
},
{
"$group": {
"_id": "$full_datetime",
"value_sum": {"$sum": "$value"},
"entries": {
"$push": {
"name": "$name",
"percentage": {
"$multiply": [{
"$divide": ["$value", {"$literal": "$value_sum" }]
}, 100 ]
}
}
}
}
}
])
This request is not work because $value_sum does not exists inside $push.
Please help me how I can to send this $value_sum into the $push statement
You can use one more stage to calculate percentage using $map as,
db.collection.aggregate([
"$addFields": {
"full_datetime": {
"$substr": ["$time", 0, 19]
}
}
}, {
"$group": {
"_id": "$full_datetime",
"value_sum": {
"$sum": "$value"
},
"entries": {
"$push": {
"name": "$name",
"value": "$value"
}
}
}
}, {
"$project": {
"entriesNew": {
"$map": {
"input": "$entries",
"as": "entry",
"in": {
"name": "$$entry.name",
"percentage": {
"$multiply": [{
"$divide": ["$$entry.value", "$value_sum"]
}, 100]
}
}
}
}
}
}])
Output:
/* 1 */
{
"_id" : "2013-12-28T11:00:00",
"entries" : [
{
"name" : "Entry 1",
"percentage" : 28.9473684210526
},
{
"name" : "Entry 2",
"percentage" : 60.5263157894737
},
{
"name" : "Entry 3",
"percentage" : 6.57894736842105
},
{
"name" : "Entry 4",
"percentage" : 3.94736842105263
}
]
}
/* 2 */
{
"_id" : "2013-12-28T06:00:00",
"entries" : [
{
"name" : "Entry 1",
"percentage" : 33.3333333333333
},
{
"name" : "Entry 2",
"percentage" : 66.6666666666667
}
]
}

$lookup and push new data

Hi i'm having a discussion object collection and a user deatils collection.
Discussion collection stores the participants username in an array of strings.
Discussion collection is as follows:
[{ "_id": "5a4dbdaab46b426863e7ead3",
"topic": "test",
"topicDesc": "test123",
"createdOn": "2018-01-04T05:37:46.088Z",
"participants": ["akhil","ben"] //[usernames]
}]
The user details collection is as follows:
[{
"_id": "59e6d6ba02e11e1814481022",
"username": "ben",
"name": "Ben S",
"email": "qwerty#123.com",
},{
"_id": "5a0431b1d6fab00cdf484677",
"username": "akhil",
"name": "Akhil Clement",
"email": "qwerty#123.com",
}]
and result JSON be like
[{ "_id": "5a4dbdaab46b426863e7ead3",
"topic": "test",
"topicDesc": "test123",
"createdOn": "2018-01-04T05:37:46.088Z",
"participants": ["akhil","ben"] //[usernames]
"participantDetails": [{
"_id": "59e6d6ba02e11e1814481022",
"username": "ben",
"name": "Ben S",
"email": "qwerty#123.com",
},{
"_id": "5a0431b1d6fab00cdf484677",
"username": "akhil",
"name": "Akhil Clement",
"email": "qwerty#123.com",
}]
}]
you need to $lookup with user collection and $group
db.dis.aggregate(
[
{$unwind : "$participants"},
{$lookup : {from : "us", localField : "participants", foreignField : "username", as : "userData"}},
{$group : {_id : {
_id : "$_id", topic : "$topic", topicDesc : "$topicDesc", createdOn : "$createdOn"
},
participants : {$push : "$participants" } ,
participantDetails : {$push : {$arrayElemAt : ["$userData", 0]}}}
},
{$project : {
_id : "$_id._id",
topic : "$_id.topic",
topicDesc : "$_id.topicDesc",
createdOn : "$_id.createdOn",
participants : 1 ,
participantDetails : 1
}}
]
).pretty()
result
{
"participants" : [
"akhil",
"ben"
],
"participantDetails" : [
{
"_id" : "59e6d6ba02e11e1814481020",
"username" : "akhil",
"name" : "Akhil Clement",
"email" : "qwerty#123.com"
},
{
"_id" : "59e6d6ba02e11e1814481021",
"username" : "ben",
"name" : "Ben S",
"email" : "qwerty#123.com"
}
],
"_id" : "5a4dbdaab46b426863e7ead3",
"topic" : "test",
"topicDesc" : "test123",
"createdOn" : "2018-01-04T05:37:46.088Z"
}
EDIT
change $push to $addToSet to avoid duplicates

Aggregate in MongoDB excluding array item

I need to group these docs by taxonomy array but I've to exclude from group the "term3" item from the first Doc. (My situation is more complicated in a real app but this example fits.)
[
{
"_id": "1",
"Taxonomy": [
{
"Key": "1", "Term": "term1"
},
{
"Key": "2", "Term": "term2"
},
{
"Key": "3", "Term": "term3"
}
]
},
{
"_id": "2",
"Taxonomy": [
{
"Key": "1", "Term": "term1"
},
{
"Key": "2", "Term": "term2"
}
]
},
{
"_id": "3",
"Taxonomy": [
{
"Key": "1", "Term": "term1"
},
{
"Key": "2", "Term": "term2"
}
]
}
]
This command generates two nodes due to the "term3" item:
{$group: {"_id" :"$Taxonomy","Posts" : { "$addToSet" : { "_id" : "$_id"}}}}
It is possible to unwind, match and then re-group documents or there is a simple way?
Running this aggregation:
db.terms.aggregate([
{$unwind:"$Taxonomy"},
{$match:{"Taxonomy.Term":{$ne:"term3"}}},
{$group:{_id:"$_id",Taxonomy:{$push:"$Taxonomy"}}}
])
Will produce this result:
{ "_id" : "2", "Taxonomy" : [ { "Key" : "1", "Term" : "term1" }, { "Key" : "2", "Term" : "term2" } ] }
{ "_id" : "3", "Taxonomy" : [ { "Key" : "1", "Term" : "term1" }, { "Key" : "2", "Term" : "term2" } ] }
{ "_id" : "1", "Taxonomy" : [ { "Key" : "1", "Term" : "term1" }, { "Key" : "2", "Term" : "term2" } ] }
If you then need to do further grouping/processing, you can add more stages. If you needed to exclude based on Key rather than Term then you can adjust $match stage. If you want to exclude everything other than term1 and term2 you would change the $ne to be $in:[<list-of-values-to-keep>].