MongoDB perform join with aggregate - mongodb

I have two collections User and Post.
User
user_id
region
is_join
Post
post_id
user_id
body
is_block
From perspective of rdb, User:Post relationship is 1:N.
Each user can write multiple post.
For example, currently documents is inserted like this.
User
> db.user.find()
{ "_id" : ObjectId("5d15e41a1b48d9417ebc28d2"), "user_id" : NumberLong(1), "region" : "US", "is_join" : true }
{ "_id" : ObjectId("5d15e41a1b48d9417ebc28d5"), "user_id" : NumberLong(2), "region" : "KR", "is_join" : true }
{ "_id" : ObjectId("5d15e41a1b48d9417ebc28d8"), "user_id" : NumberLong(3), "region" : "US", "is_join" : true }
{ "_id" : ObjectId("5d15e41a1b48d9417ebc28da"), "user_id" : NumberLong(4), "region" : "KR", "is_join" : false }
{ "_id" : ObjectId("5d15e41a1b48d9417ebc28dc"), "user_id" : NumberLong(5), "region" : "US", "is_join" : true }
Post
> db.post.find()
{ "_id" : ObjectId("5d15e41a1b48d9417ebc28d3"), "post_id" : NumberLong(1), "user_id" : NumberLong(1), "body" : "first", "is_block" : false }
{ "_id" : ObjectId("5d15e41a1b48d9417ebc28d4"), "post_id" : NumberLong(4), "user_id" : NumberLong(1), "body" : "fourth", "is_block" : false }
{ "_id" : ObjectId("5d15e41a1b48d9417ebc28d6"), "post_id" : NumberLong(2), "user_id" : NumberLong(2), "body" : "second", "is_block" : false }
{ "_id" : ObjectId("5d15e41a1b48d9417ebc28d7"), "post_id" : NumberLong(3), "user_id" : NumberLong(2), "body" : "third", "is_block" : false }
{ "_id" : ObjectId("5d15e41a1b48d9417ebc28d9"), "post_id" : NumberLong(5), "user_id" : NumberLong(3), "body" : "fifth", "is_block" : true }
{ "_id" : ObjectId("5d15e41a1b48d9417ebc28db"), "post_id" : NumberLong(6), "user_id" : NumberLong(4), "body" : "sixth", "is_block" : false }
{ "_id" : ObjectId("5d15e41a1b48d9417ebc28dd"), "post_id" : NumberLong(7), "user_id" : NumberLong(5), "body" : "seven", "is_block" : true }
{ "_id" : ObjectId("5d15e41a1b48d9417ebc28de"), "post_id" : NumberLong(8), "user_id" : NumberLong(5), "body" : "eight", "is_block" : false }
To perform join via aggregate, there is more condition that have to apply.
User region='US', is_join=true
Posts created by user with the result of number 1
Post is_block=false
Sort by post_id
(Optional) If user do not wrote any post, except it. I know it can be perform through preserveNullAndEmptyArrays, but I think it cause performance issue.
Desired result
{
"posts" : [
{
"post_id" : NumberLong(1),
"body" : "first",
"is_block" : false
},
{
"post_id" : NumberLong(2),
"body" : "second",
"is_block" : false
},
{
"post_id" : NumberLong(3),
"body" : "third",
"is_block" : false
},
{
"post_id" : NumberLong(4),
"body" : "fourth",
"is_block" : false
},
{
"post_id" : NumberLong(8),
"body" : "eight",
"is_block" : false
}
]
}
post_id = 5 was excluded by is_block=true
post_id = 6 was excluded by is_join=false
post_id = 7 was excluded by is_block=true
And whole result is sorted by post_id.
I'm new at mongodb So, maybe I thinking too much in the form of a relational database.
And I don't know it can be perform on NoSQL.
Is there any way about it?
Any suggestion, very appreciate.
Thanks.

You can achieve such result using lookup pipeline operator.
const region = 'US';
const is_join = true;
const is_block = false;
const query = [
{
$match: {
region: region,
is_join: is_join
}
},
{
$lookup: {
let: { user_id: "$user_id" },
from: 'posts',
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: ["$$user_id", "$user_id"], },
{ $eq: ["$is_block", is_block] }
]
}
}
},
{
$sort: {
post_id: 1
}
}
],
as: "posts"
}
},
{
$unwind: "$posts"
},
{
$group:{
_id: "mygroup",
posts: {
$push: {
post_id: "$posts.post_id",
body: "$posts.body",
is_block: "$posts.is_block",
}
}
}
},
{
$project:{
_id: false
}
}
]
db.users.aggregate(query)
OUTPUT
{
"posts" : [
{
"post_id" : 1,
"body" : "first",
"is_block" : false
},
{
"post_id" : 4,
"body" : "fourth",
"is_block" : false
},
{
"post_id" : 8,
"body" : "eight",
"is_block" : false
}
]
}

Try this -
To join two collection, you can use aggregation as -
User.aggregate([{
'$match': { 'region':'US', 'is_join': true } // match from users collection
}, {
$lookup: { // it will aggregate the result from both collection
from: 'posts',
localField: '_id',
foreignField: 'user_id',
as: 'posts'
}
},
{"$unwind":"$posts"},
{$match : { "posts.is_block" : false } }, // check inside post collection
{$sort : { "posts.post_id" : 1}}, // sort the data
], (err, users) => {
if (err) return callback(err, null);
console.log('users :', users);
});

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"
}

Multiple condition on same column 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

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 group mongo sub document

I am newbie to mongo, i am trying to take the group by values in a subdocument, and having the mongo collection structure as like :
{
"_id" : ObjectId("589d4e4b270f8b1635d400b1"),
"myShopId" : 439,
"products" : [
{
"productId" : "1234",
"productName" : "sambarpowder 500 gm",
"productCategory" : "masala",
"mrp" : "90",
"_id" : ObjectId("589d595f6da20b72fe006ea9")
},
{
"productId" : "5678",
"productName" : "moong dhal 200 gms",
"productCategory" : "dhal",
"mrp" : "38 ",
"_id" : ObjectId("589d595f6da20b72fe006eaa")
},
{
"productId" : "5678",
"productName" : "moong dhal 200 gms",
"productCategory" : "dhal",
"mrp" : "38 ",
"_id" : ObjectId("589d595f6da20b72fe006eaa")
}
],
"isAlive" : 1,
"__v" : 3
}
Here, I want to do group by in this.
for eg in mysql:
select productCategory from products where shopId = '439' groupby productCategory
How can i achieve the group by in mongo sub document
My Expected output is like :
category : [{
productCategory : masala
_id : ObjectId("589d595f6da20b72fe006ea9")
},
{
productCategory : dhal
_id : ObjectId("589d595f6da20b72fe006eaa")
}
]
Hope this will help,
db.test.aggregate([{
$match: {
myShopId: 439
}
}, {
$unwind: "$products"
}, {
$group: {
_id: {
"productCategory": "$products.productCategory"
},
"id": {
$first: "$products._id"
}
}
}])
Output:
{ "_id" : { "productCategory" : "dhal" }, "id" : ObjectId("589d595f6da20b72fe006eaa") }
{ "_id" : { "productCategory" : "masala" }, "id" : ObjectId("589d595f6da20b72fe006ea9") }

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"
}