Multiple condition on same column mongodb - mongodb

{
"_id" : ObjectId("5ccfe06e2434de5c345d058e"),
"user_id" : 1,
"event" : "eventA",
"channel_id" : "1098",
"channel_node_id" : "2177",
"channel_name" : "New work",
"created_at" : NumberLong("1557127278000"),
"updated_at" : NumberLong("1557127278000"),
"user_raw_data" : {
"Name" : "akhilesh",
"Mobile" : "1234567890",
"Email" : "akhilesh#test.com"
},
{
"_id" : ObjectId("5ccfe06e2434de5c345d058e"),
"user_id" : 1
"event" : "eventB,
"channel_id" : "1098",
"channel_node_id" : "2177",
"channel_name" : "New work",
"created_at" : NumberLong("1557127278000"),
"updated_at" : NumberLong("1557127278000"),
"user_raw_data" : {
"Name" : "akhilesh",
"Mobile" : "1234567890",
"Email" : "akhilesh#test.com"
},
{
"_id" : ObjectId("5ccfe06e2434de5c345d058e"),
"user_id" : 1
"event" : "eventC,
"channel_id" : "1098",
"channel_node_id" : "2178",
"channel_name" : "New work",
"created_at" : NumberLong("1557127278000"),
"updated_at" : NumberLong("1557127278000"),
"user_raw_data" : {
"Name" : "akhilesh",
"Mobile" : "1234567890",
"Email" : "akhilesh#test.com"
}
,
{
"_id" : ObjectId("5ccfe06e2434de5c345d058e"),
"user_id" : 2
"event" : "eventA,
"channel_id" : "1098",
"channel_node_id" : "2178",
"channel_name" : "New work",
"created_at" : NumberLong("1557127278000"),
"updated_at" : NumberLong("1557127278000"),
"user_raw_data" : {
"Name" : "akhilesh",
"Mobile" : "1234567890",
"Email" : "akhilesh#test.com"
}
Now I want to get the count of events performed by the user so far. For eventA count will be 2, for eventB 1, and for eventC is 1. But this query will have multiple conditions so my condition is getting the count only if the user has performed (eventA or eventB) and eventC. So in that case from above doc user id 2 event will not be considered because they have not performed (eventA or eventB)
Also along with event match with mutiple and or condition I also want to apply filter on user_raw_data so my query should be like this
db.web_channel_events.aggregate([
{
$match: {
"channel_id": "1098",
"channel_node_id": "2177"
}
},
{
$group: {
"_id": {
"user_id": "$user_id",
"event": "$event"
},
"count": {
$sum: 1
}
}
},
{
$group: {
"_id": "$_id.user_id",
"event_details": {
$push: {
"k": "$_id.event",
"v": "$count"
}
}
}
},
{
$match: {
$and: [
{
$or: [
{
"event_details.k": "eventA",
"event_details.v": {
"$gte": 1
}
},
{
"event_details.k": "eventB",
"event_details.v": {
"$gte": 1
}
}
]
},
{
"event_details.k": "eventC",
"event_details.v": {
"$gte": 1
}
},
{
"user_raw_data.Name": "akhilesh"
}
]
}
},
{
"$unwind": "$event_details"
},
{
$group: {
"_id": "$event_details.k",
"count": {
$sum: "$event_details.v"
}
}
}
]).pretty();

The following query can get us the expected output:
db.collection.aggregate([
{
$group:{
"_id":{
"user_id":"$user_id",
"event":"$event"
},
"user_id":{
$first:"$user_id"
},
"event":{
$first:"$event"
},
"count":{
$sum:1
}
}
},
{
$group:{
"_id":"$user_id",
"user_id":{
$first:"$user_id"
},
"event_details":{
$push:{
"k":"$event",
"v":"$count"
}
}
}
},
{
$addFields:{
"event_details":{
$arrayToObject:"$event_details"
}
}
},
{
$match:{
$and:[
{
$or:[
{
"event_details.eventA":{
$gt:0
}
},
{
"event_details.eventB":{
$gt:0
}
}
]
},
{
"event_details.eventC":{
$gt:0
}
}
]
}
},
{
$group:{
"_id":null,
"eventA":{
$sum:"$event_details.eventA"
},
"eventB":{
$sum:"$event_details.eventB"
},
"eventC":{
$sum:"$event_details.eventC"
}
}
},
{
$project:{
"_id":0,
"event_details.eventA":"$eventA",
"event_details.eventB":"$eventB",
"event_details.eventC":"$eventC"
}
}
]).pretty()
Data set:
{
"_id" : ObjectId("5ccfe06e2434de5c345d0588"),
"event" : "eventA",
"user_id" : 1,
"created_at" : NumberLong("1557127278000"),
"updated_at" : NumberLong("1557127278000")
}
{
"_id" : ObjectId("5d8b132388edcf85b0aaa609"),
"event" : "eventA",
"user_id" : 1,
"created_at" : NumberLong("1557127278000"),
"updated_at" : NumberLong("1557127278000")
}
{
"_id" : ObjectId("5d8b132388edcf85b0aaa60a"),
"event" : "eventB",
"user_id" : 1,
"created_at" : NumberLong("1557127278000"),
"updated_at" : NumberLong("1557127278000")
}
{
"_id" : ObjectId("5d8b132388edcf85b0aaa60b"),
"event" : "eventC",
"user_id" : 1,
"created_at" : NumberLong("1557127278000"),
"updated_at" : NumberLong("1557127278000")
}
{
"_id" : ObjectId("5d8b132388edcf85b0aaa60c"),
"event" : "eventC",
"user_id" : 1,
"created_at" : NumberLong("1557127278000"),
"updated_at" : NumberLong("1557127278000")
}
{
"_id" : ObjectId("5d8b132388edcf85b0aaa60d"),
"event" : "eventC",
"user_id" : 2,
"created_at" : NumberLong("1557127278000"),
"updated_at" : NumberLong("1557127278000")
}
Output:
{ "event_details" : { "eventA" : 2, "eventB" : 1, "eventC" : 2 } }
Query analysis:
Grouping the data on the basis of user_id and event and calculating the count of that specific incident
Grouping only on the basis of user_id and pushing event and its
count into an array event_details as key-value pairs
Converting the event_details into an object
Applying the condition that the event count for ( A or B ) and C
should be greater than 0
Sum up individual event counts

Related

Problems aggregating MongoDB

I am having problems aggregating my Product Document in MongoDB.
My Product Document is:
{
"_id" : ObjectId("5d81171c2c69f45ef459e0af"),
"type" : "T-Shirt",
"name" : "Panda",
"description" : "Panda's are cool.",
"image" : ObjectId("5d81171c2c69f45ef459e0ad"),
"created_at" : ISODate("2019-09-17T18:25:48.026+01:00"),
"is_featured" : false,
"sizes" : [
"XS",
"S",
"M",
"L",
"XL"
],
"tags" : [ ],
"pricing" : {
"price" : 26,
"sale_price" : 8
},
"categories" : [
ObjectId("5d81171b2c69f45ef459e086"),
ObjectId("5d81171b2c69f45ef459e087")
],
"sku" : "5d81171c2c69f45ef459e0af"
},
And my Category Document is:
{
"_id" : ObjectId("5d81171b2c69f45ef459e087"),
"name" : "Art",
"description" : "These items are our artsy options.",
"created_at" : ISODate("2019-09-17T18:25:47.196+01:00")
},
My aim is to perform aggregation on the Product Document in order to count the number of items within each Category. So I have the Category "Art", I need to count the products are in the "Art" Category:
My current aggregate:
db.product.aggregate(
{ $unwind : "$categories" },
{
$group : {
"_id" : { "name" : "$name" },
"doc" : { $push : { "category" : "$categories" } },
}
},
{ $unwind : "$doc" },
{
$project : {
"_id" : 0,
"name" : "$name",
"category" : "$doc.category"
}
},
{
$group : {
"_id" : "$category",
"name": { "$first": "$name" },
"items_in_cat" : { $sum : 1 }
}
},
{ "$sort" : { "items_in_cat" : -1 } },
)
Which does actually work but not as I need:
{
"_id" : ObjectId("5d81171b2c69f45ef459e082"),
"name" : null, // Why is the name of the category no here?
"items_in_cat" : 4
},
As we can see the name is null. How can I aggregate the output to be:
{
"_id" : ObjectId("5d81171b2c69f45ef459e082"),
"name" : "Art",
"items_in_cat" : 4
},
We need to use $lookup to fetch the name from Category collection.
The following query can get us the expected output:
db.product.aggregate([
{
$unwind:"$categories"
},
{
$group:{
"_id":"$categories",
"items_in_cat":{
$sum:1
}
}
},
{
$lookup:{
"from":"category",
"let":{
"id":"$_id"
},
"pipeline":[
{
$match:{
$expr:{
$eq:["$_id","$$id"]
}
}
},
{
$project:{
"_id":0,
"name":1
}
}
],
"as":"categoryLookup"
}
},
{
$unwind:{
"path":"$categoryLookup",
"preserveNullAndEmptyArrays":true
}
},
{
$project:{
"_id":1,
"name":{
$ifNull:["$categoryLookup.name","NA"]
},
"items_in_cat":1
}
}
]).pretty()
Data set:
Collection: product
{
"_id" : ObjectId("5d81171c2c69f45ef459e0af"),
"type" : "T-Shirt",
"name" : "Panda",
"description" : "Panda's are cool.",
"image" : ObjectId("5d81171c2c69f45ef459e0ad"),
"created_at" : ISODate("2019-09-17T17:25:48.026Z"),
"is_featured" : false,
"sizes" : [
"XS",
"S",
"M",
"L",
"XL"
],
"tags" : [ ],
"pricing" : {
"price" : 26,
"sale_price" : 8
},
"categories" : [
ObjectId("5d81171b2c69f45ef459e086"),
ObjectId("5d81171b2c69f45ef459e087")
],
"sku" : "5d81171c2c69f45ef459e0af"
}
Collection: category
{
"_id" : ObjectId("5d81171b2c69f45ef459e086"),
"name" : "Art",
"description" : "These items are our artsy options.",
"created_at" : ISODate("2019-09-17T17:25:47.196Z")
}
{
"_id" : ObjectId("5d81171b2c69f45ef459e087"),
"name" : "Craft",
"description" : "These items are our artsy options.",
"created_at" : ISODate("2019-09-17T17:25:47.196Z")
}
Output:
{
"_id" : ObjectId("5d81171b2c69f45ef459e087"),
"items_in_cat" : 1,
"name" : "Craft"
}
{
"_id" : ObjectId("5d81171b2c69f45ef459e086"),
"items_in_cat" : 1,
"name" : "Art"
}

MongoDB sort by join result with aggregate lookup

I have two collections user and post
> db.user.find().pretty()
{
"_id" : ObjectId("5d1473bc1b48d9309580a9de"),
"user_id" : NumberLong(1),
"region" : "US",
"is_join" : true
}
{
"_id" : ObjectId("5d1473bc1b48d9309580a9df"),
"user_id" : NumberLong(2),
"region" : "KR",
"is_join" : true
}
{
"_id" : ObjectId("5d1473bc1b48d9309580a9e0"),
"user_id" : NumberLong(3),
"region" : "US",
"is_join" : true
}
{
"_id" : ObjectId("5d1473bc1b48d9309580a9e1"),
"user_id" : NumberLong(4),
"region" : "US",
"is_join" : true
}
{
"_id" : ObjectId("5d1487fc1b48d9321ff5dc1f"),
"user_id" : NumberLong(5),
"region" : "US",
"is_join" : true
}
> db.post.find().pretty()
{
"_id" : ObjectId("5d1473bc1b48d9309580a9e2"),
"post_id" : NumberLong(1),
"user_id" : NumberLong(3),
"body" : "hi"
}
{
"_id" : ObjectId("5d1473bc1b48d9309580a9e3"),
"post_id" : NumberLong(2),
"user_id" : NumberLong(1),
"body" : "hello"
}
{
"_id" : ObjectId("5d1473bc1b48d9309580a9e4"),
"post_id" : NumberLong(3),
"user_id" : NumberLong(2),
"body" : "go"
}
{
"_id" : ObjectId("5d1473bc1b48d9309580a9e5"),
"post_id" : NumberLong(4),
"user_id" : NumberLong(4),
"body" : "python"
}
{
"_id" : ObjectId("5d14941b1b48d93314907345"),
"post_id" : NumberLong(5),
"user_id" : NumberLong(1),
"body" : "aa"
}
I want to join via mongo aggregate lookup operation.
So I made query like this.
db.user.aggregate([
{
'$match': {
'region': 'US',
}
},
{
'$lookup': {
'from': 'post',
'localField': 'user_id',
'foreignField': 'user_id',
'as': 'user'
}
},
{
'$project': {
'_id': 0,
'user.post_id': 1
}
}
])
Result
{ "user" : [ { "post_id" : NumberLong(2) }, { "post_id" : NumberLong(5) } ] }
{ "user" : [ { "post_id" : NumberLong(1) } ] }
{ "user" : [ { "post_id" : NumberLong(4) } ] }
{ "user" : [ ] }
As you know that post_id is unordered.
But I want to sort it descending.
Desired result
{ "user" : [ { "post_id" : NumberLong(1) } ] }
{ "user" : [ { "post_id" : NumberLong(2) } ] }
{ "user" : [ { "post_id" : NumberLong(4) } ] }
{ "user" : [ { "post_id" : NumberLong(5) } ] }
Even Better
{ "user" : [ 1, 2, 4, 5 }] }
How can I modify aggregate query?
Thanks.
You can do following to get what you want:
1) $unwind the user array
2) use $sort to sort all documents with post_id
3) use $group as the next stage in your aggregation query, and push user.post_id to form one sorted user array from all the docs:
db.user.aggregate([
{
'$match': {
'region': 'US',
}
},
{
'$lookup': {
'from': 'post',
'localField': 'user_id',
'foreignField': 'user_id',
'as': 'user'
}
},
{
$unwind : {
path : "$user",
preserveNullAndEmptyArrays : true
}
},
{
$sort : {
'user.post_id' : 1
}
},
{
$group : {
_id : null,
user : {
$push : "$user.post_id"
}
}
}
])
Read more about $sort , $unwind and $group for more information.

How to return all project employees?

I have datas of following format collection(projects) inside my database:
{ "_id" : ObjectId("5981a80f223e491a58230e5d"), "id" : 2, "name" : "gbqplhlqxzwl", "managerId" : 65151, "startDate" : "03.11.1999", "finishDate" : "02.01.2003", "projectStatus" : "POSTPONED", "participants" : [ ], "estimatedBudget" : 6017891.811079914 }
{ "_id" : ObjectId("5981a80f223e491a58230e5e"), "id" : 3, "name" : "erfekfsdgryu", "managerId" : 83749, "startDate" : "07.07.2007", "finishDate" : "26.12.2027", "projectStatus" : "POSTPONED", "participants" : [ 19229, 81856, 79270, 5509, 70344, 39424 ], "estimatedBudget" : 3086213.8981674756 }
{ "_id" : ObjectId("5981a80f223e491a58230e5f"), "id" : 1, "name" : "jvbzobhppntd", "managerId" : 18925, "startDate" : "29.04.1999", "finishDate" : "13.10.2008", "projectStatus" : "OPEN", "participants" : [ 46100, 96968, 6676, 56121, 4716, 68901, 43990, 48587, 62547, 30292, 65153, 17551, 27083, 20261, 27097, 50036, 86585, 69890, 18790, 22592, 60774, 93709, 78471, 27157, 4328, 36501, 47296, 16831 ], "estimatedBudget" : 3581496.7068344904 }
{ "_id" : ObjectId("5981a80f223e491a58230e60"), "id" : 4, "name" : "cdspkkqwvwld", "managerId" : 62042, "startDate" : "13.03.1998", "finishDate" : "20.06.2007", "projectStatus" : "OPEN", "participants" : [ 53480, 60897, 23677, 22064, 60807, 66637, 84609, 28378, 87143, 27675, 79283, 94992, 20429, 48769, 91671, 41747, 21651, 91134, 41684, 57228, 51949, 18756, 45679, 87781, 67287, 6902, 27526 ], "estimatedBudget" : 2126283.953787842 }
....
I need to find the busiest employee and list all his projects.
participants array contains employee ids who participate in the project.
I use the following query to find the busiest employee:
db.projects.aggregate(
{
$unwind: '$participants'
},
{
$addFields: {
count: 1
}
},
{
$group: {
_id : '$participants',
participation_count : {
'$sum':'$count'
}
}
},
{
$sort:{participation_count:-1}
},
{
$limit:1
}
)
and this work correctly. But I have no ideas how to list all his projects.
any ideas?
db.projects.aggregate(
[
{
$unwind: '$participants'
},
{
$addFields: {
count: 1
}
},
{
$group: {
_id : '$participants',
participation_count : {'$sum':'$count'},
projectId : {$push: '$id'}
}
},
{
$sort:{participation_count:-1}
},
{
$limit:1
}
],
{
allowDiskUse:true
}
)

Mongodb : get whether a document is the latest with a field value and filter on the result

I am trying to port an existing SQL schema into Mongo.
We have document tables, with sometimes several times the same document, with a different revision but the same reference. I want to get only the latest revisions of the documents.
A sample input data:
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC305",
"code" : "305-D",
"title" : "Document 305",
"creationdate" : ISODate("2011-11-24T15:13:28.887Z"),
"creator" : "X"
},
{
"Uid" : "xxx",
"status" : "COMMENTED",
"reference" : "DOC306",
"code" : "306-A",
"title" : "Document 306",
"creationdate" : ISODate("2011-11-28T07:23:18.807Z"),
"creator" : "X"
},
{
"Uid" : "xxx",
"status" : "COMMENTED",
"reference" : "DOC306",
"code" : "306-B",
"title" : "Document 306",
"creationdate" : ISODate("2011-11-28T07:26:49.447Z"),
"creator" : "X"
},
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC501",
"code" : "501-A",
"title" : "Document 501",
"creationdate" : ISODate("2011-11-19T06:30:35.757Z"),
"creator" : "X"
},
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC501",
"code" : "501-B",
"title" : "Document 501",
"creationdate" : ISODate("2011-11-19T06:40:32.957Z"),
"creator" : "X"
}
Given this data, I want this result set (sometimes I want only the last revision, sometimes I want all revisions with an attribute telling me whether it's the latest):
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC305",
"code" : "305-D",
"title" : "Document 305",
"creationdate" : ISODate("2011-11-24T15:13:28.887Z"),
"creator" : "X",
"lastrev" : true
},
{
"Uid" : "xxx",
"status" : "COMMENTED",
"reference" : "DOC306",
"code" : "306-B",
"title" : "Document 306",
"creationdate" : ISODate("2011-11-28T07:26:49.447Z"),
"creator" : "X",
"lastrev" : true
},
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC501",
"code" : "501-B",
"title" : "Document 501",
"creationdate" : ISODate("2011-11-19T06:40:32.957Z"),
"creator" : "X",
"lastrev" : true
}
I already have a bunch of filters, sorting, and skip/limit (for pagination of data), so the final result set should be mindful of these constraints.
The current "find" query (built with the .Net driver), which filters fine but gives me all revisions of each document:
coll.find(
{ "$and" : [
{ "$or" : [
{ "deletedid" : { "$exists" : false } },
{ "deletedid" : null }
] },
{ "$or" : [
{ "taskid" : { "$exists" : false } },
{ "taskid" : null }
] },
{ "objecttypeuid" : { "$in" : ["xxxxx"] } }
] },
{ "_id" : 0, "Uid" : 1, "lastrev" : 1, "title" : 1, "code" : 1, "creator" : 1, "owner" : 1, "modificator" : 1, "status" : 1, "reference": 1, "creationdate": 1 }
).sort({ "creationdate" : 1 }).skip(0).limit(10);
Using another question, I have been able to build this aggregation, which gives me the latest revision of each document, but with not enough attributes in the result:
coll.aggregate([
{ $sort: { "creationdate": 1 } },
{
$group: {
"_id": "$reference",
result: { $last: "$creationdate" },
creationdate: { $last: "$creationdate" }
}
}
]);
I would like to integrating the aggregate with the find query.
I have found the way to mix aggregation and filtering:
coll.aggregate(
[
{ $match: {
"$and" : [
{ "$or" : [
{ "deletedid" : { "$exists" : false } },
{ "deletedid" : null }
] },
{ "$or" : [
{ "taskid" : { "$exists" : false } },
{ "taskid" : null }
] },
{ "objecttypeuid" : { "$in" : ["xxx"] } }
]
}
},
{ $sort: { "creationdate": 1 } },
{ $group: {
"_id": "$reference",
"doc": { "$last": "$$ROOT" }
}
},
{ $sort: { "doc.creationdate": 1 } },
{ $skip: skip },
{ $limit: limit }
],
{ allowDiskUse: true }
);
For each result node, this gives me a "doc" node with the document data. It has too much data still (it's missing projections), but it's a start.
Translated in .Net:
FilterDefinitionBuilder<BsonDocument> filterBuilder = Builders<BsonDocument>.Filter;
FilterDefinition<BsonDocument> filters = filterBuilder.Empty;
filters = filters & (filterBuilder.Not(filterBuilder.Exists("deletedid")) | filterBuilder.Eq("deletedid", BsonNull.Value));
filters = filters & (filterBuilder.Not(filterBuilder.Exists("taskid")) | filterBuilder.Eq("taskid", BsonNull.Value));
foreach (var f in fieldFilters) {
filters = filters & filterBuilder.In(f.Key, f.Value);
}
var sort = Builders<BsonDocument>.Sort.Ascending(orderby);
var group = new BsonDocument {
{ "_id", "$reference" },
{ "doc", new BsonDocument("$last", "$$ROOT") }
};
var aggregate = coll.Aggregate(new AggregateOptions { AllowDiskUse = true })
.Match(filters)
.Sort(sort)
.Group(group)
.Sort(sort)
.Skip(skip)
.Limit(rows);
return aggregate.ToList();
I'm pretty sure there are better ways to do this, though.
You answer is pretty close. Instead of $last, $max is better.
About $last operator:
Returns the value that results from applying an expression to the last document in a group of documents that share the same group by a field. Only meaningful when documents are in a defined order.
Get the last revision in each group, see code below in mongo shell:
db.collection.aggregate([
{
$group: {
_id: '$reference',
doc: {
$max: {
"creationdate" : "$creationdate",
"code" : "$code",
"Uid" : "$Uid",
"status" : "$status",
"title" : "$title",
"creator" : "$creator"
}
}
}
},
{
$project: {
_id: 0,
Uid: "$doc.Uid",
status: "$doc.status",
reference: "$_id",
code: "$doc.code",
title: "$doc.title",
creationdate: "$doc.creationdate",
creator: "$doc.creator"
}
}
]).pretty()
The output as your expect:
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC501",
"code" : "501-B",
"title" : "Document 501",
"creationdate" : ISODate("2011-11-19T06:40:32.957Z"),
"creator" : "X"
}
{
"Uid" : "xxx",
"status" : "COMMENTED",
"reference" : "DOC306",
"code" : "306-B",
"title" : "Document 306",
"creationdate" : ISODate("2011-11-28T07:26:49.447Z"),
"creator" : "X"
}
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC305",
"code" : "305-D",
"title" : "Document 305",
"creationdate" : ISODate("2011-11-24T15:13:28.887Z"),
"creator" : "X"
}

mongodb aggregation $group and then $push a object

this is my data :
> db.bookmarks.find({"userId" : "56b9b74bf976ab70ff6b9999"}).pretty()
{
"_id" : ObjectId("56c2210fee4a33579f4202dd"),
"userId" : "56b9b74bf976ab70ff6b9999",
"items" : [
{
"itemId" : "28",
"timestamp" : "2016-02-12T18:07:28Z"
},
{
"itemId" : "29",
"timestamp" : "2016-02-12T18:07:29Z"
},
{
"itemId" : "30",
"timestamp" : "2016-02-12T18:07:30Z"
},
{
"itemId" : "31",
"timestamp" : "2016-02-12T18:07:31Z"
},
{
"itemId" : "32",
"timestamp" : "2016-02-12T18:07:32Z"
},
{
"itemId" : "33",
"timestamp" : "2016-02-12T18:07:33Z"
},
{
"itemId" : "34",
"timestamp" : "2016-02-12T18:07:34Z"
}
]
}
I want to have something like (actually i hope the _id can become userId too) :
{
"_id" : "56b9b74bf976ab70ff6b9999",
"items" : [
{ "itemId": "32", "timestamp": "2016-02-12T18:07:32Z" },
{ "itemId": "31", "timestamp": "2016-02-12T18:07:31Z" },
{ "itemId": "30", "timestamp": "2016-02-12T18:07:30Z" }
]
}
What I have now :
> db.bookmarks.aggregate(
... { $match: { "userId" : "56b9b74bf976ab70ff6b9999" } },
... { $unwind: '$items' },
... { $sort: { 'items.timestamp': -1} },
... { $skip: 2 },
... { $limit: 3},
... { $group: { '_id': '$userId' , items: { $push: '$items.itemId' } } }
... ).pretty()
{ "_id" : "56b9b74bf976ab70ff6b9999", "items" : [ "32", "31", "30" ] }
i tried to read the document in mongo and find out i can $push, but somehow i cannot find a way to push such object, which is not defined anywhere in the whole object. I want to have the timestamp also.. but i don't know how should i modified the $group (or others??) to do so. thanks for helping!
This code, which I tested in the MongoDB 3.2.1 shell, should give you the output format that you want:
> db.bookmarks.aggregate(
{ "$match" : { "userId" : "Ursula" } },
{ "$unwind" : "$items" },
{ "$sort" : { "items.timestamp" : -1 } },
{ "$skip" : 2 },
{ "$limit" : 3 },
{ "$group" : { "_id" : "$userId", items: { "$push" : { "myPlace" : "$items.itemId", "myStamp" : "$items.timestamp" } } } } ).pretty()
Running the above will produce this output:
{
"_id" : "Ursula",
"items" : [
{
"myPlace" : "52",
"myStamp" : ISODate("2016-02-13T18:07:32Z")
},
{
"myPlace" : "51",
"myStamp" : ISODate("2016-02-13T18:07:31Z")
},
{
"myPlace" : "50",
"myStamp" : ISODate("2016-02-13T18:07:30Z")
}
]
}
In MongoDB version 3.2.x, you can also use the $out operator in the very last stage of the aggregation pipeline, and have the output of the aggregation query written to a collection. Here is the code I used:
> db.bookmarks.aggregate(
{ "$match" : { "userId" : "Ursula" } },
{ "$unwind" : "$items" },
{ "$sort" : { "items.timestamp" : -1 } },
{ "$skip" : 2 },
{ "$limit" : 3 },
{ "$group" : { "_id" : "$userId", items: { "$push" : { "myPlace" : "$items.itemId", "myStamp" : "$items.timestamp" } } } },
{ "$out" : "ursula" } )
This gives me a collection named "ursula":
> show collections
ursula
and I can query that collection:
> db.ursula.find().pretty()
{
"_id" : "Ursula",
"items" : [
{
"myPlace" : "52",
"myStamp" : ISODate("2016-02-13T18:07:32Z")
},
{
"myPlace" : "51",
"myStamp" : ISODate("2016-02-13T18:07:31Z")
},
{
"myPlace" : "50",
"myStamp" : ISODate("2016-02-13T18:07:30Z")
}
]
}
>
Last of all, this is the input document I used in the aggregation query. You can compare this document to how I coded the aggregation query to see how I built the new items array.
> db.bookmarks.find( { "userId" : "Ursula" } ).pretty()
{
"_id" : ObjectId("56c240ed55f2f6004dc3b25c"),
"userId" : "Ursula",
"items" : [
{
"itemId" : "48",
"timestamp" : ISODate("2016-02-13T18:07:28Z")
},
{
"itemId" : "49",
"timestamp" : ISODate("2016-02-13T18:07:29Z")
},
{
"itemId" : "50",
"timestamp" : ISODate("2016-02-13T18:07:30Z")
},
{
"itemId" : "51",
"timestamp" : ISODate("2016-02-13T18:07:31Z")
},
{
"itemId" : "52",
"timestamp" : ISODate("2016-02-13T18:07:32Z")
},
{
"itemId" : "53",
"timestamp" : ISODate("2016-02-13T18:07:33Z")
},
{
"itemId" : "54",
"timestamp" : ISODate("2016-02-13T18:07:34Z")
}
]
}