query collection A with filtered items from collection B - mongodb-query

given the following 2 collections songs and play_log
i need a one mongo aggregation query with lookup to do the magic
to get the play log of all songs belonging to Bowie's "Scary Monsters" album with the song related info.
collection songs
[
{artist: 'David Bowie', title: 'Ashes to Ashes', album: 'Scary Monsters', year:'1980', track_number: 4 ,label: 'RCA Records'},
{artist: 'David Bowie', title: 'Fashion', album: 'Scary Monsters', year:'1980', track_number: 5 ,label: 'RCA Records'},
....
{artist: 'U2', title: 'Sunday Bloody Sunday', album: 'war', year '1983', track_number: 1, label: 'Island Records'},
{artist: 'U2', title: 'New Year's Day', album: 'war', year '1983', track_number: 3, label: 'Island Records'},
{artist: 'U2', title: 'The Refugee', album: 'war', year '1983', track_number: 6, label: 'Island Records'},
....
]
collection play_log
[
{ created: '2019-02-08T11:05:33', station: 'BBC Radio 6', artist: 'David Bowie', title: 'Ashes to Ashes' },
{ created: '2019-01-17T01:33:57', station: 'BBC Radio 1', artist: 'U2', title: 'Sunday Bloody Sunday' },
{ created: '2018-09-08T12:21:32', station: 'BBC Radio 2', artist: 'Morrissey', title: 'Every day is like Sunday' },
{ created: '2019-02-08T11:11:11', station: 'BBC Radio 4', artist: 'David Bowie', title: 'Fashion' },
...
]
expected result
[
{ created: '2019-02-08T11:05:33', station: 'BBC Radio 6', artist: 'David Bowie', title: 'Ashes to Ashes', album:'Scary Monsters', year:'1980', track_number: 4 ,label: 'RCA Records'},
{ created: '2019-02-08T11:11:11', station: 'BBC Radio 4', artist: 'David Bowie', title: 'Fashion', album: 'Scary Monsters', year:'1980', track_number: 5 ,label: 'RCA Records'},
...
]

This can be done through aggregation using $lookup operator as below:
db.play_log.aggregate([
// Join using artist fields
{
$lookup:
{
from: "songs",
localField: "artist",
foreignField: "artist",
as: "play_songs_logs"
}
},
// Filter any empty array found in newly created collection: play_songs_logs
{
$match: { "play_songs_logs": { $ne: [] } }
},
// Match only required album, this can be done before filter also if make aggregate on songs collection
{
$match:
{
"play_songs_logs.album" : "Scary Monsters"
}
},
// Push all elements or merged the elements
{
$replaceRoot: { newRoot: { $mergeObjects: [ { $arrayElemAt: [ "$play_songs_logs", 0 ] }, "$$ROOT" ] } }
},
// Filter not required fields
{ $project: { play_songs_logs: 0, _id:0 } }
])
Output of the above query:
{ "artist" : "David Bowie", "title" : "Ashes to Ashes", "album" : "Scary Monsters", "year" : "1980", "track_number" : 4, "label" : "RCA Records", "created" : "2019-02-08T11:05:33", "station" : "BBC Radio 6" }
{ "artist" : "David Bowie", "title" : "Fashion", "album" : "Scary Monsters", "year" : "1980", "track_number" : 4, "label" : "RCA Records", "created" : "2019-02-08T11:11:11", "station" : "BBC Radio 4" }
Before running above query, I have inserted the provided data and find query
> db.play_log.find()
{ "_id" : ObjectId("5c5f8c561765cd7b27eb4731"), "created" : "2019-02-08T11:05:33", "station" : "BBC Radio 6", "artist" : "David Bowie", "title" : "Ashes to Ashes" }
{ "_id" : ObjectId("5c5f8c561765cd7b27eb4732"), "created" : "2019-01-17T01:33:57", "station" : "BBC Radio 1", "artist" : "U2", "title" : "Sunday Bloody Sunday" }
{ "_id" : ObjectId("5c5f8c561765cd7b27eb4733"), "created" : "2018-09-08T12:21:32", "station" : "BBC Radio 2", "artist" : "Morrissey", "title" : "Every day is like Sunday" }
{ "_id" : ObjectId("5c5f8c561765cd7b27eb4734"), "created" : "2019-02-08T11:11:11", "station" : "BBC Radio 4", "artist" : "David Bowie", "title" : "Fashion" }
>
> db.songs.find()
{ "_id" : ObjectId("5c5f8c961765cd7b27eb4735"), "artist" : "David Bowie", "title" : "Ashes to Ashes", "album" : "Scary Monsters", "year" : "1980", "track_number" : 4, "label" : "RCA Records" }
{ "_id" : ObjectId("5c5f8c961765cd7b27eb4736"), "artist" : "David Bowie", "title" : "Fashion", "album" : "Scary Monsters", "year" : "1980", "track_number" : 5, "label" : "RCA Records" }
{ "_id" : ObjectId("5c5f8c961765cd7b27eb4737"), "artist" : "U2", "title" : "Sunday Bloody Sunday", "album" : "war", "year" : "1983", "track_number" : 1, "label" : "Island Records" }
{ "_id" : ObjectId("5c5f8c961765cd7b27eb4738"), "artist" : "U2", "title" : "New Year's Day", "album" : "war", "year" : "1983", "track_number" : 3, "label" : "Island Records" }
{ "_id" : ObjectId("5c5f8c961765cd7b27eb4739"), "artist" : "U2", "title" : "The Refugee", "album" : "war", "year" : "1983", "track_number" : 6, "label" : "Island Records" }
>
For details you can refer the official documents here: https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/

Related

hot to get subdocument values as normal document field's value in mongodb

{
"eID" : 101,
"eName" : "Radhey",
"phone" : 85482245,
"add" : "ffw djf ffvf",
"city" : "U.P",
"Salary Details" : [
{
"Salary" : 56458
}
]
}
as
{
"eID" : 101,
"eName" : "Radhey",
"phone" : 85482245,
"add" : "ffw djf ffvf",
"city" : "U.P",
"Salary" : 56458
}
If Salary Details is always an array with a single object in it, as #user20042973 suggested, you can use:
db.collection.find({},
{
"eID": 1,
"eName": 1,
"phone": 1,
"add": 1,
"city": 1,
"Salary Details": {$first: "$Salary Details.Salary"}
})

i want to query the double nested dates in mongodb

----------Query i have tried----------
db.getCollection('rates').aggregate([
{ $match: { "userId" : "5d4c4f69341b7b1746c80d13"}},
{ $unwind: '$ratewithdate.daywiserates'},{$match : {"$and" :
[{"ratewithdate.daywiserates.date" :{$gte :new ISODate("2019-09-
23T00:00:00.000Z")} },
{"ratewithdate.daywiserates.date" :{$lte :new ISODate("2019-09-
27T00:00:00.000Z")}}]}}])
-------------------------------------------------------------
In My query is here i want to know the query between two dates ,i want
get the data between two dates in the array? am unable to do that ,can
any one send me query,
here my question is i want get the date range from given date to next 30
days,i have tried with aggregate but data become slow..can any one suggest
any better solution for the making query formation
i have tried with aggregation as well find queries am not able find any
results,my goal here is find get the data between two dates,for example
if i selected 2019-09-23T10:43:14.239Z thi date from this date i wanna i
want to show the data.
please send me your value able suggestions to, am not bale to query with
double nested array queries in mongodb,please send me your value able
suggestions to,am not bale to query with double nested array queries in
mongodb please send me your value able suggestions to,
am not bale to query with double nested array queries in mongodb.
please send me your value able suggestions to, am not bale to query with
double nested array queries in mongodb,please send me your value able
suggestions to,am not bale to query with double nested array queries in
mongodb please send me your value able suggestions to,
am not bale to query with double nested array queries in mongodb
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1bff"),
"alloted_roomid" : [],
"name" : "working_rate3",
"description" : "bitcpin",
"type" : "room",
"value" : null,
"inclusive" : "General",
"refundable" : {
"cancellationWindow" : "",
"outsideWindowPenalty" : "",
"insideWindowPenalty" : ""
},
"nonRefundable" : true,
"cancellationWindow" : "",
"daysWiseRate" : "30",
"insideWindowPenalty" : "",
"outsideWindowPenalty" : "",
"deviations" : 980,
"policy" : "",
"funds" : "nonRefundable",
"vat" : 890,
"other_tax" : 90,
"roomRates" : [
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c00"),
"roomId" : ObjectId("5d7c8f2950a6c766c64b2a46"),
"roomName" : "Basic",
"rate" : 9888
}
],
"userId" : "5d4c4f69341b7b1746c80d13",
"hotelCode" : 10034,
"ratewithdate" : [
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c01"),
"roomCategory" : "Basic",
"roomId" : ObjectId("5d7c8f2950a6c766c64b2a46"),
"createdAt" : ISODate("2019-09-21T10:43:14.243Z"),
"daywiserates" : [
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c1f"),
"date" : ISODate("2019-09-21T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c1e"),
"date" : ISODate("2019-09-22T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c1d"),
"date" : ISODate("2019-09-23T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c1c"),
"date" : ISODate("2019-09-24T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c1b"),
"date" : ISODate("2019-09-25T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c1a"),
"date" : ISODate("2019-09-26T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c19"),
"date" : ISODate("2019-09-27T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c18"),
"date" : ISODate("2019-09-28T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c17"),
"date" : ISODate("2019-09-29T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c16"),
"date" : ISODate("2019-09-30T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c15"),
"date" : ISODate("2019-10-01T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c14"),
"date" : ISODate("2019-10-02T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c13"),
"date" : ISODate("2019-10-03T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c12"),
"date" : ISODate("2019-10-04T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c11"),
"date" : ISODate("2019-10-05T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c10"),
"date" : ISODate("2019-10-06T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c0f"),
"date" : ISODate("2019-10-07T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c0e"),
"date" : ISODate("2019-10-08T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c0d"),
"date" : ISODate("2019-10-09T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c0c"),
"date" : ISODate("2019-10-10T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c0b"),
"date" : ISODate("2019-10-11T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c0a"),
"date" : ISODate("2019-10-12T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c09"),
"date" : ISODate("2019-10-13T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c08"),
"date" : ISODate("2019-10-14T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c07"),
"date" : ISODate("2019-10-15T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c06"),
"date" : ISODate("2019-10-16T10:43:14.239Z"),
"rate" : 9888
},
{
"_id" : ObjectId("5d85fec2e8652a5c20ae1c05"),
"date" : ISODate("2019-10-17T10:43:14.239Z"),
"rate" : 9888
},
]
}
],
"id" : "rat-W262IxTjk",
"__v" : 0
}
Try this:
db.getCollection('rates').aggregate([
{ $match: { "userId" : "5d4c4f69341b7b1746c80d13"}},
{ $unwind: '$ratewithdate'},
{ $unwind: '$ratewithdate.daywiserates'},
{ $match : {
"$and" :[
{ "ratewithdate.daywiserates.date" :{$gte :new ISODate("2019-09-23T00:00:00.000Z")} },
{ "ratewithdate.daywiserates.date" :{$lte :new ISODate("2019-09-27T00:00:00.000Z")} }
]
}
},
{ $addFields: {result: "$ratewithdate.daywiserates"}},
{ $project: {result: 1, _id: 0}}
])

mongodb query multiple pairs using $in

I have a collection names with data (_id is omitted):
{first:"John", last:"Smith"},
{first:"Alice", last:"Johnson"},
{first:"Bob", last:"Williams"},
...
and {first, last} is a unique index.
I want to find a lot of names in names like:
db.names.find({$or: [{first:"Alice", last:"Brown"}, {first:"Bob", last:"White"}, ...]}
Can I use $in instead of $or to simplify this query?
===
I know in MySQL this following query:
SELECT * FROM names WHERE (first = 'Alice' AND last = 'Brown') OR (first = 'Bob' AND last = 'White') OR ...
can be simplified as:
SELECT * FROM names WHERE (first, last) IN (('Alice', 'Brown'), ('Bob','White') OR ...)
But I am unable to find the equivalent query syntax in MongoDB.
The following query can get us the expected output:
db.names.find({
$expr:{
$in:[
{
"first":"$first",
"last":"$last"
},
[
{
"first" : "Alice",
"last" : "Johnson"
},
{
"first" : "Bob",
"last" : "Williams"
}
]
]
}
}).pretty()
Data set:
{
"_id" : ObjectId("5d81c3b7a832f81a9e02337b"),
"first" : "John",
"last" : "Smith"
}
{
"_id" : ObjectId("5d81c3b7a832f81a9e02337c"),
"first" : "Alice",
"last" : "Johnson"
}
{
"_id" : ObjectId("5d81c3b7a832f81a9e02337d"),
"first" : "Bob",
"last" : "Williams"
}
Output:
{
"_id" : ObjectId("5d81c3b7a832f81a9e02337c"),
"first" : "Alice",
"last" : "Johnson"
}
{
"_id" : ObjectId("5d81c3b7a832f81a9e02337d"),
"first" : "Bob",
"last" : "Williams"
}
Query Analysis: This query won't use the unique index you have created and I would recommend to go with the $or query.
Following are the winning plans:
$or query:
{
"stage" : "COLLSCAN",
"filter" : {
"$expr" : {
"$in" : [
{
"first" : "$first",
"last" : "$last"
},
{
"$const" : [
{
"first" : "Alice",
"last" : "Johnson"
},
{
"first" : "Bob",
"last" : "Williams"
}
]
}
]
}
},
"direction" : "forward"
}
$in query:
{
"stage" : "SUBPLAN",
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "OR",
"inputStages" : [
{
"stage" : "IXSCAN",
"keyPattern" : {
"first" : 1,
"last" : 1
},
"indexName" : "first_1_last_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"first" : [ ],
"last" : [ ]
},
"isUnique" : true,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"first" : [
"[\"Alice\", \"Alice\"]"
],
"last" : [
"[\"Brown\", \"Brown\"]"
]
}
},
{
"stage" : "IXSCAN",
"keyPattern" : {
"first" : 1,
"last" : 1
},
"indexName" : "first_1_last_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"first" : [ ],
"last" : [ ]
},
"isUnique" : true,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"first" : [
"[\"Bob\", \"Bob\"]"
],
"last" : [
"[\"White\", \"White\"]"
]
}
}
]
}
}
}
You can see that the index is properly utilized in $or query and not used in the $in query.
For this mysql query
SELECT * FROM names
WHERE (first = 'Alice' AND last = 'Brown')
OR (first = 'Bob' AND last = 'White')
OR ...
can be use in mongodb as
db.names.find({
$or: [
{ first: "Alice", last: "Brown" },
{ first: "Bob", last: "White" },
...
]})
For below mysql query
SELECT * FROM names
WHERE (first, last) IN (('Alice', 'Brown'), ('Bob','White') OR ...)
You can not create mongodb $in query
db.names.find({
$or: [
{ first: { $in: ["John", "Alice", "Bob"] } },
{ last: { $in: ["Smith", "Johnson", "Williams" ] } },
]})
Because it pass this result too
("John", "Johnson"),("John", "Williams"),("John", "Smith"),("John", "Williams")..
the best way is $or as ask in question
db.names.find({
$or: [
{ first: "Alice", last: "Brown" },
{ first: "Bob", last: "White" },
...
]})
May be better solution present out there. I will update my answer if find any. Thanks
Yes, it's possible to use $in to query MongoDb collections based on multiple values.
Try using the below syntax:
db.names.find.({ field: { $in: [<value1>, <value2>, ... <valueN> ] } })
for example in your case it looks like:
db.names.find({first: {$in: ["Alice","Brown","Bob", "White"]}})

Remove Subdocument items with $pull

I'm trying to remove items from subdocuments using ExpressJS and Mongoose but it is only removing the first items, not the sub items.
So I want to remove "subitem 2" in the messages Array
This is the structure:
{
"_id" : ObjectId("5c4ee94b30ebd71cbed89a35"),
"title" : "Test",
"subitem" : [
{
"_id" : ObjectId("5c4ee95630ebd71cbed89a36"),
"title" : "Item 1",
"messages" : [
{
"_id" : ObjectId("5c4ee95f30ebd71cbed89a37"),
"type" : "single_article",
"date" : "Jan 28, 2019",
"title" : "subitem 1",
"text" : ""
}
]
},
{
"_id" : ObjectId("5c4ee96830ebd71cbed89a38"),
"title" : "item 2",
"messages" : [
{
"_id" : ObjectId("5c4ee96e30ebd71cbed89a39"),
"type" : "single_article",
"date" : "Jan 28, 2019",
"title" : "subitem 2",
"text" : ""
}
]
}
],
"__v" : 0
}
And this is the $pull method:
getController.deleteRec = function(req,res,collection){
var id = req.params.id;
console.log(id);
collection.updateOne({'subitem.messages._id': id}, {$pull: {'subitem.0.messages': {"_id": id}}}).
then(function(result){
console.log(result);
});
};
Now I know why it is only deleting the first item because I have "subitem.0.messages". How can I loop over this, so it can delete all items?
You can use $ as a wildcard index, removing all elements in the array matching your query like this:
{$pull: {'subitem.$.messages': {"_id": id}}}
if you want to remove multiple documents:
{$pull: {'subitem.$.messages': {"_id": {$in : [id, id2, id3...]}}}}

How to arrive at the difference in values for minimum and maximum dates for each year in mongodb

How to arrive at the difference in values of a specific column for the records with minimum and maximum dates for each year in a collection using the aggregate pipeline or map reduce in mongodb?
I have the following collection:
/* 1 */
{
"_id" : 1,
"item" : "abc",
"price" : 10,
"quantity" : 2,
"date" : ISODate("2014-01-01T08:00:00.000Z")
},
/* 2 */
{
"_id" : 2,
"item" : "jkl",
"price" : 20,
"quantity" : 1,
"date" : ISODate("2014-02-03T09:00:00.000Z")
},
/* 3 */
{
"_id" : 3,
"item" : "xyz",
"price" : 5,
"quantity" : 5,
"date" : ISODate("2014-02-03T09:05:00.000Z")
},
/* 4 */
{
"_id" : 4,
"item" : "abc",
"price" : 10,
"quantity" : 10,
"date" : ISODate("2014-02-15T08:00:00.000Z")
},
/* 5 */
{
"_id" : 5,
"item" : "xyz",
"price" : 5,
"quantity" : 10,
"date" : ISODate("2014-02-15T09:05:00.000Z")
},
/* 6 */
{
"_id" : 6,
"item" : "abc",
"price" : 10,
"quantity" : 2,
"date" : ISODate("2013-01-01T08:00:00.000Z")
},
/* 7 */
{
"_id" : 7,
"item" : "jkl",
"price" : 20,
"quantity" : 1,
"date" : ISODate("2013-02-03T09:00:00.000Z")
},
/* 8 */
{
"_id" : 8,
"item" : "xyz",
"price" : 5,
"quantity" : 5,
"date" : ISODate("2013-02-03T09:05:00.000Z")
},
/* 9 */
{
"_id" : 9,
"item" : "abc",
"price" : 10,
"quantity" : 10,
"date" : ISODate("2013-02-15T08:00:00.000Z")
},
/* 10 */
{
"_id" : 10,
"item" : "xyz",
"price" : 5,
"quantity" : 10,
"date" : ISODate("2013-02-15T09:05:00.000Z")
},
/* 11 */
{
"_id" : 11,
"item" : "abc",
"price" : 10,
"quantity" : 2,
"date" : ISODate("2012-01-01T08:00:00.000Z")
},
/* 12 */
{
"_id" : 12,
"item" : "jkl",
"price" : 20,
"quantity" : 1,
"date" : ISODate("2012-02-03T09:00:00.000Z")
},
/* 13 */
{
"_id" : 13,
"item" : "xyz",
"price" : 5,
"quantity" : 5,
"date" : ISODate("2012-02-03T09:05:00.000Z")
},
/* 14 */
{
"_id" : 14,
"item" : "abc",
"price" : 10,
"quantity" : 10,
"date" : ISODate("2012-02-15T08:00:00.000Z")
},
/* 15 */
{
"_id" : 15,
"item" : "xyz",
"price" : 5,
"quantity" : 10,
"date" : ISODate("2012-02-15T09:05:00.000Z")
},
I would like the result to take the following form:
{
{"year": 2014}, {"minDtQuantity": 2}, {"maxDtQuantity": 10}, {"quantityDiff": 8},
{"year": 2013}, {"minDtQuantity": 2}, {"maxDtQuantity": 10}, {"quantityDiff": 8},
{"year": 2012}, {"minDtQuantity": 2}, {"maxDtQuantity": 10}, {"quantityDiff": 8},
}
For each year, we need to find the minimum and maximum dates and group them by year and then find the "quantity" values on those dates and then find the difference between the quantities for the min and max dates for each year.
Is that even possible with aggregate pipelines or map-reduce in mongodb?
This can be done using aggregation pipelines by sorting by date, then pushing the quantities into arrays when grouping by the year (Use a the $year operator to extract the year from the date object). The quantities for the minimum and maximum dates in that year are then the first and last values in the array respectively. These can be taken out of the array using $arrayElemAt.
db.collection.aggregate(
[
{
$sort: {
"date": 1
}
},
{
$group: {
"_id": { "$year": "$date" },
"quantityArray": { "$push": "$quantity" },
}
},
{
$project: {
"_id": 0,
"year": "$_id",
"minDtQuantity": { "$arrayElemAt": [ "$quantityArray", 0 ] },
"maxDtQuantity": { "$arrayElemAt": [ { "$reverseArray": "$quantityArray" }, 0 ] },
"quantityDiff": { "$subtract": [
{ "$arrayElemAt": [ { "$reverseArray": "$quantityArray" }, 0 ] },
{ "$arrayElemAt": [ "$quantityArray", 0 ] },
] }
}
},
]
);
This aggregation returns these results on your data:
{
"year" : NumberInt(2014),
"minDtQuantity" : NumberInt(2),
"maxDtQuantity" : NumberInt(10),
"quantityDiff" : NumberInt(-8)
},
{
"year" : NumberInt(2013),
"minDtQuantity" : NumberInt(2),
"maxDtQuantity" : NumberInt(10),
"quantityDiff" : NumberInt(-8)
},
{
"year" : NumberInt(2012),
"minDtQuantity" : NumberInt(2),
"maxDtQuantity" : NumberInt(10),
"quantityDiff" : NumberInt(-8)
}
This is not quite the format you specified. I am not exactly sure what you required, did you need the results returned in one document?