MongoDB Nested Document query - mongodb

im having trouble with mongodb querying
my object document:
[
{
"_id": "5a2ca2227c42ad67682731d4",
"name": "name",
"photos": [
{
"_id": "5a2ca22b7c42ad67682731d5",
"approved": false,
"text":"good"
},
{
"_id": "5a2ca72b0a1aa173aaae07da",
"approved": true,
"text":"bad"
}
]
},
{
"_id": "4v2ca2227cad676821731d4",
"name": "name"
}
]
im trying to query all documents.. in this documents, the documents somethings will have photos, if exists , would like to bring the photos that have attribute approved equals to true.. but i have to bring all documents, thoses who have photos, and those who not.. im trying to use aggregation, project, unwind, but im not got any good result yet :(
i need the results be like :
[
{
"_id": "5a2ca2227c42ad67682731d4",
"name": "name",
"photos": [
{
"_id": "5a2ca22b7c42ad67682731d5",
"approved": false,
"text":"good"
}
]
},
{
"_id": "4v2ca2227cad676821731d4",
"name": "name"
}
]
thank you!

You might need to $unwind all including the non existing photos array for getting the results
db.ph.aggregate(
[
{
$unwind:
{
path: "$photos",
preserveNullAndEmptyArrays: true
}
},
{
$match : {"photos.approved" : {$ne : true} }
}
]
)
result
{
"_id" : "5a2ca2227c42ad67682731d4",
"name" : "name",
"photos" : {
"_id" : "5a2ca22b7c42ad67682731d5",
"approved" : false,
"text" : "good"
}
}
{ "_id" : "4v2ca2227cad676821731d4", "name" : "name" }
>

Related

Get paginated records and total records in one mongodb aggregate query

I have my monogdb departments data structure like as shown below
[{
category: "ABC",
sections: [
{
section_hod: "x111",
section_name: "SECTION A",
section_staff_count: "v11111",
section_id: "a1111",
:
},
{
section_hod: "x2222",
section_name: "SECTION B",
section_staff_count: "v2222",
section_id: "a2222",
:
}
]
}
:
:
]
I wrote a mongodb query like as shown below
db.getSiblingDB("departments").getCollection("DepartmentDetails").aggregate([
{ $unwind : "$sections"},
{ $match : { $and : [{ "sections.section_name" : "SECTION A"},
{ $or : [{ "category" : "ABC"}]}]}},
{$project : { "name" : "$sections.section_name", "hod" : "$sections.section_hod", "staff_count" : "$sections.section_staff_count", "id" : "$sections.section_id"}},
{$skip: 0}, {$limit: 10}
]);
which gives me a list of section details as shown below which contains name, hod, staff_count, id etc
[
{
"name": "xxxxx",
"hod": "xxxxx",
"staff_count": "xxxxx",
"id": "xxxxx"
},
{
"name": "yyyyy",
"hod": "yyyyy",
"staff_count": "yyyyy",
"id": "yyyyy"
}
:
:
:
]
Everything looks good, but the problem is I have so many records in the list with which I am trying to build a pagination. For implementing pagination I know I can use the skip and limit function for iterating the pages, but for doing that I need to know the total counts of all the records.
I can do this in two ways, First way is I can execute two queries one which will be a count and then the aggregate query passing the skip and limit, second way is execute one query which return me the total counts and the documents in the order of first paginated page.
I am trying to implement the second way and bring the expected result is as shown below
{
"documents": [
{
"name": "xxxxx",
"hod": "xxxxx",
"staff_count": "xxxxx",
"id": "xxxxx"
},
{
"name": "yyyyy",
"hod": "yyyyy",
"staff_count": "yyyyy",
"id": "yyyyy"
}
:
:
:
],
"totalCount": 5444
}
Not sure if this is achievable. Can someone please help me on this. My default limit is 10
You can do it like this, it will give you total records and paginated results in one go,
db.getSiblingDB("departments").getCollection("DepartmentDetails")
.aggregate([
{ $unwind : "$sections"},
{ $match : { $and : [{ "sections.section_name" : "SECTION A"},
{ $or : [{ "category" : "ABC"}]}]}},
{
$project : {
"name" : "$sections.section_name",
"hod" : "$sections.section_hod",
"staff_count" : "$sections.section_staff_count",
"id" : "$sections.section_id"
}
},
{
$facet: {
metaData: [{
$count: 'total'
}],
records: [
{$skip: 0},
{$limit: 10}
]
}
},
{
$project: {
records: 1,
total: {
$let: {
vars: {
totalObj: {
$arrayElemAt: ['$metaData', 0]
}
},
in: '$$totalObj.total'
}
},
}
}
]);

group by field and count mongodb [duplicate]

I have following collection
{
"_id" : ObjectId("5b18d14cbc83fd271b6a157c"),
"status" : "pending",
"description" : "You have to complete the challenge...",
}
{
"_id" : ObjectId("5b18d31a27a37696ec8b5773"),
"status" : "completed",
"description" : "completed...",
}
{
"_id" : ObjectId("5b18d31a27a37696ec8b5775"),
"status" : "pending",
"description" : "pending...",
}
{
"_id" : ObjectId("5b18d31a27a37696ec8b5776"),
"status" : "inProgress",
"description" : "inProgress...",
}
I need to group by status and get all the keys dynamically which are in status
[
{
"completed": [
{
"_id": "5b18d31a27a37696ec8b5773",
"status": "completed",
"description": "completed..."
}
]
},
{
"pending": [
{
"_id": "5b18d14cbc83fd271b6a157c",
"status": "pending",
"description": "You have to complete the challenge..."
},
{
"_id": "5b18d31a27a37696ec8b5775",
"status": "pending",
"description": "pending..."
}
]
},
{
"inProgress": [
{
"_id": "5b18d31a27a37696ec8b5776",
"status": "inProgress",
"description": "inProgress..."
}
]
}
]
Not that I think it's a good idea and mostly because I don't see any "aggregation" here at all is that after "grouping" to add to an array you similarly $push all that content into array by the "status" grouping key and then convert into keys of a document in a $replaceRoot with $arrayToObject:
db.collection.aggregate([
{ "$group": {
"_id": "$status",
"data": { "$push": "$$ROOT" }
}},
{ "$group": {
"_id": null,
"data": {
"$push": {
"k": "$_id",
"v": "$data"
}
}
}},
{ "$replaceRoot": {
"newRoot": { "$arrayToObject": "$data" }
}}
])
Returns:
{
"inProgress" : [
{
"_id" : ObjectId("5b18d31a27a37696ec8b5776"),
"status" : "inProgress",
"description" : "inProgress..."
}
],
"completed" : [
{
"_id" : ObjectId("5b18d31a27a37696ec8b5773"),
"status" : "completed",
"description" : "completed..."
}
],
"pending" : [
{
"_id" : ObjectId("5b18d14cbc83fd271b6a157c"),
"status" : "pending",
"description" : "You have to complete the challenge..."
},
{
"_id" : ObjectId("5b18d31a27a37696ec8b5775"),
"status" : "pending",
"description" : "pending..."
}
]
}
That might be okay IF you actually "aggregated" beforehand, but on any practically sized collection all that is doing is trying force the whole collection into a single document, and that's likely to break the BSON Limit of 16MB, so I just would not recommend even attempting this without "grouping" something else before this step.
Frankly, the same following code does the same thing, and without aggregation tricks and no BSON limit problem:
var obj = {};
// Using forEach as a premise for representing "any" cursor iteration form
db.collection.find().forEach(d => {
if (!obj.hasOwnProperty(d.status))
obj[d.status] = [];
obj[d.status].push(d);
})
printjson(obj);
Or a bit shorter:
var obj = {};
// Using forEach as a premise for representing "any" cursor iteration form
db.collection.find().forEach(d =>
obj[d.status] = [
...(obj.hasOwnProperty(d.status)) ? obj[d.status] : [],
d
]
)
printjson(obj);
Aggregations are used for "data reduction" and anything that is simply "reshaping results" without actually reducing the data returned from the server is usually better handled in client code anyway. You're still returning all data no matter what you do, and the client processing of the cursor has considerably less overhead. And NO restrictions.

Join Same Collection in Mongo

Below is the sample collection document record that i want to join the same collection with different child array elements.
Sample Collection Record :
{
"_id": "052dc2aa-043b-4cd7-a3f2-f3fe6540ae52",
"Details": [
{
"Id": "104b0bb1-d4a5-469b-b1fd-b4822e96dcb0",
"Number": "12345",
"Percentages": [
{
"Code": "55555",
"Percentage": "45"
},
{
"Code": "55333",
"Percentage": "50"
}
]
},
{
"Id": "104b0bb1-d4a5-469b-b1fd-b4822e96dcb0",
"Number": "55555",
"Percentages": [
{
"Code": "55555",
"Percentage": "45"
}
]
}
],
"Payments": [
{
"Id": "61ee1a6f-3334-4f33-ab6c-51c646b75c41",
"Number": "12345"
}
]
}
The mongo Pipeline query which i would like to fetch the Percentages Array with matched conditions whose Details.Number and Payment.Number should be same
Result:
"Percentages": [
{
"Code": "55555",
"Percentage": "45"
},
{
"Code": "55333",
"Percentage": "50"
}]
How to bring the result by joining the same collections child elements using aggregate ?
Following query does what you want:
db.collection.aggregate([
{$unwind : "$Details"},
{$unwind : "$Details.Percentages"},
{$unwind : "$Payments"}, // $unwind all your arrays
{
$addFields : { //This include new `isMatch` field, which is gonna be true, only if Details.Number = Payment.Number
"isMatch" : {$cond: { if: { $eq: [ "$Details.Number", "$Payments.Number" ] }, then: true, else: false }}
}
},
{
$match : { // This ignores all others, for which Details.Number != Payment.Number
"isMatch" : true
}
},
{
$group : { // This will return only the Percentage objects
_id : null,
"Percentages" : {$push : "$Details.Percentages"}
}
},
{
$project : { // To ignore "_id" field
_id : 0,
"Percentages" : 1
}
}
])
Result:
{
"Percentages" : [
{
"Code" : "55555",
"Percentage" : "45"
},
{
"Code" : "55333",
"Percentage" : "50"
}
]
}
Hope this helps!

How to aggregate multiple document with one array?

Here is my data structure:
{
"_id" : ObjectId("5becc8e2e9427e48d0edab83"),
"theater" : "TodayTainan",
"geometry" : {
"type" : "Point",
"coordinates" : [
120.196866,
22.99322
]
},
"movie" : [
{
"movieDate" : "上映日期:2018-07-25",
"videoId" : [
"17Y_lXjB3VI",
"ovbEe1-qUZ0"
],
"imdbScore" : "",
"cnName" : "不可能的任務:全面瓦解",
"photoHref" : "https://movies.yahoo.com.tw/x/r/w420/i/o/production/movies/June2018/8LBd1GYiRhtjZMCcdOSl-1019x1500.JPG",
"rottenScore" : "",
"releasedTime" : [
ISODate("2018-11-15T12:40:00.000Z"),
ISODate("2018-11-15T17:20:00.000Z"),
ISODate("2018-11-15T22:00:00.000Z")
],
"enName" : "Mission: Impossible Fall Out",
"goodMinePoint" : 0.75
},
{
"movieDate" : "上映日期:2018-08-10",
"videoId" : [
"j-FAn2jGI08",
"6aWIWGBWlPk"
],
"imdbScore" : "5.9",
"cnName" : "巨齒鯊",
"photoHref" : "https://movies.yahoo.com.tw/x/r/w420/i/o/production/movies/July2018/xO7qzzEwPCaipjCWkkxg-2714x3878.jpg",
"rottenScore" : "45%",
"releasedTime" : [
ISODate("2018-11-15T15:20:00.000Z"),
ISODate("2018-11-15T20:00:00.000Z")
],
"enName" : "The Meg",
"goodMinePoint" : 0.3
}
],
"phone" : "06-2205151"
}
I use addToSet to remove duplicate data with one document like this code:
db.getCollection('TaipeiEast').aggregate([
{ "$match": {
"theater": "TodayTainan"
}
},
{ "$unwind": '$movie' },
{ "$group": {
"_id": "$_id",
"movie": {
"$addToSet": {
"cnName": "$movie.cnName",
"photoHref": "$movie.photoHref"
}
}
}
}
])
Now I have a lots of documents in one collection, I want to query them become one movie array without duplicate movie.
I try to remove match like this:
db.getCollection('TaipeiEast').aggregate([
{ "$unwind": '$movie' },
{ "$group": {
"_id": "$_id",
"movie": {
"$addToSet": {
"cnName": "$movie.cnName",
"photoHref": "$movie.photoHref"
}
}
}
}
])
I can get all of documents data, but the movie array is independent with each of document.
Any one knows how to generate one movie array from multiple documents ? Thanks in advance.
Try $group with null. So you can get all movies in a array without depending on each document
{ "$group": {
"_id": null,
"movie": {
"$addToSet": {
"cnName": "$movie.cnName",
"photoHref": "$movie.photoHref"
}
}
}
}

Project an array with MongoDB

I'm using MongoDB's aggregation pipeline, to get my documents in the form that I want. As the last step of aggregation, I use $project to put the documents into their final form.
But I'm having trouble projecting and array of sub-documents. Here is what I currently get from aggrgation:
{
"_id": "581c8c3df1325f68ffd23386",
"count": 14,
"authors": [
{
"author": {
"author": "57f246b9e01e6c6f08e1d99a",
"post": "581c8c3df1325f68ffd23386"
},
"count": 13
},
{
"author": {
"author": "5824382511f16d0f3fd5aaf2",
"post": "581c8c3df1325f68ffd23386"
},
"count": 1
}
]
}
I want to $project the authors array so that the return would be this:
{
"_id": "581c8c3df1325f68ffd23386",
"count": 14,
"authors": [
{
"_id": "57f246b9e01e6c6f08e1d99a",
"count": 13
},
{
"_id": "5824382511f16d0f3fd5aaf2",
"count": 1
}
]
}
How would I go about achieving that?
You can unwind the array and wind it u again after projecting.
Something like this:
db.collectionName.aggregate([
{$unwind:'$authors'},
{$project:{_id:1,count:1,'author.id':'$authors.author.author','author.count':'$authors.count'}},
{$group:{_id:{_id:'$_id',count:'$count'},author:{$push:{id:'$author.id',count:'$author.count'}}}},
{$project:{_id:0,_id:'$_id._id',count:'$_id.count',author:1}}
])
the output for above will be:
{
"_id" : "581c8c3df1325f68ffd23386",
"author" : [
{
"id" : "57f246b9e01e6c6f08e1d99a",
"count" : 13.0
},
{
"id" : "5824382511f16d0f3fd5aaf2",
"count" : 1.0
}
],
"count" : 14.0
}
I have been having the same problem and just now found a simple and elegant solution that has not been mentioned anywhere, so i thought I'd share it here:
You can iterate the array using $map and project each author. With the given structure, the aggregation should look somewhat like this
db.collectionName.aggregate([
$project: {
_id: 1,
count:1,
authors: {
$map: {
input: "$authors",
as: "author",
in: {
id: "$$author.author.author",
count: $$author.author.count
}
}
}
}
])
Hope this helps anyone who is looking, like me :)
Question:
"customFields" : [
{
"index" : "1",
"value" : "true",
"label" : "isOffline",
"dataType" : "check_box",
"placeholder" : "cf_isoffline",
"valueFormatted" : "true"
},
{
"index" : "2",
"value" : "false",
"label" : "tenure_extended",
"dataType" : "check_box",
"placeholder" : "cf_tenure_extended",
"valueFormatted" : "false"
}
],
Answer:
db.subscription.aggregate([
{$match:{"autoCollect" : false,"remainingBillingCycles" : -1,"customFields.value":"false", "customFields.label" : "isOffline"}},
{$project: {first: { $arrayElemAt: [ "$customFields", 1 ] }}}
])