How to remove item in nested collection by id - mongoose - mongodb

I want to remove a topic by id from the following collection. Here is the hierarchy of the collection - Area.
Area ---
|
---Subjects---
|
Courses---
|
Chapters----
|
Topics
The Area example is as following.
{
"_id" : ObjectId("607f7c67f64e993a5c9b9793"),
"name" : "Automatic Engineering",
"description" : "this is the description.",
"created" : ISODate("2021-04-21T01:14:15.736Z"),
"subjects" : [
{
"description" : "dddd",
"name" : "Transfer Function",
"_id" : ObjectId("607f7c7af64e993a5c9b9794"),
"created" : ISODate("2021-04-21T01:14:34.943Z"),
"courses" : [
{
"description" : "dddd",
"name" : "conception",
"_id" : ObjectId("607f7c9af64e993a5c9b9796"),
"created" : ISODate("2021-04-21T01:15:06.696Z"),
"chapters" : [
{
"description" : "chapter 1",
"name" : "Chapter 1",
"_id" : ObjectId("607f7cb4f64e993a5c9b9798"),
"created" : ISODate("2021-04-21T01:15:32.855Z"),
"topics" : [
{
"description" : "1",
"name" : "Topic 1",
"_id" : ObjectId("607f7ccef64e993a5c9b979b"),
"created" : ISODate("2021-04-21T01:15:58.064Z")
},
{
"description" : "2",
"name" : "Topic 2",
"_id" : ObjectId("607f7cd5f64e993a5c9b979c"),
"created" : ISODate("2021-04-21T01:16:05.663Z")
},
{
"description" : "3",
"name" : "Topic 3",
"_id" : ObjectId("607f7cdcf64e993a5c9b979d"),
"created" : ISODate("2021-04-21T01:16:12.336Z")
}
]
},
{
"description" : "chapter2",
"name" : "Chapter 2",
"_id" : ObjectId("607f7cbcf64e993a5c9b9799"),
"created" : ISODate("2021-04-21T01:15:40.231Z"),
"topics" : []
},
{
"description" : "chapter 3",
"name" : "Chapter 3",
"_id" : ObjectId("607f7cc5f64e993a5c9b979a"),
"created" : ISODate("2021-04-21T01:15:49.000Z"),
"topics" : []
}
]
},
{
"description" : "dddd",
"name" : "exercise",
"_id" : ObjectId("607f7ca6f64e993a5c9b9797"),
"created" : ISODate("2021-04-21T01:15:18.465Z"),
"chapters" : []
}
]
},
{
"description" : "this is the example",
"name" : "State Space Equation",
"_id" : ObjectId("607f7c8ef64e993a5c9b9795"),
"created" : ISODate("2021-04-21T01:14:54.056Z"),
"courses" : []
}
],
"__v" : 0
}
And I need to remove topic object in chapters array. So this is what I have done.
areaModel.findOneAndUpdate({
'_id': req.body.area_id,
'subjects._id': req.body.subject_id,
'courses._id': req.body.course_id,
'chapter._id': req.body.chapter_id
}, {
"$pull": {
"subjects.$.courses.$.chapters.$.topics": {
_id: req.body.topic_id
}
}
}, (err, result) =>{
if (err) {
return res.json({success: false, msg: err});
} else {
return res.json({success: true, msg:"successfully removed" });
}
});
By using $pull method of mongoose, I could remove the course from the area easily. But it was impossible for me to remove the more nested items like chapters and topics.
How can I solve this issue?

you can do so with the use of arrayFilters like so:
db.collection.updateOne(
{
_id: ObjectId("607f7c67f64e993a5c9b9793")
},
{
$pull: {
"subjects.$[s].courses.$[c].chapters.$[ch].topics": {
_id: ObjectId("607f7ccef64e993a5c9b979b")
}
}
},
{
arrayFilters: [
{
"s._id": { $eq: ObjectId("607f7c7af64e993a5c9b9794") }
},
{
"c._id": { $eq: ObjectId("607f7c9af64e993a5c9b9796") }
},
{
"ch._id": { $eq: ObjectId("607f7cb4f64e993a5c9b9798") }
}
]
})
https://mongoplayground.net/p/kVyV27nnkII

Try it:
areaModel.findOneAndUpdate(
{ _id: req.body.area_id },
{
$pull: {
"subjects.$[].courses.$[].chapters.$[].topics": {
_id: req.body.topic_id,
},
},
}
);

Related

Spring Data Mongo - How to get the nested distinct array for nested value?

I'm taking a reference from : Spring Data Mongo - Perform Distinct, but doesn't wants to pull embedded documents in results and asking another questions.
I want to find technology list where "subdeptCd" : "1D". How can we do that ?
{
"firstName" : "Laxmi",
"lastName" : "Dekate",
.....
.......
.....
"departments" : {
"deptCd" : "Tax",
"deptName" : "Tax Handling Dept",
"status" : "A",
"subdepts" : [
{
"subdeptCd" : "1D",
"subdeptName" : "Tax Clearning",
"desc" : "",
"status" : "A"
"technology" : [
{
"technologyCd" : "4C",
"technologyName" : "Cloud Computing",
"desc" : "This is best certficate",
"status" : "A"
}
]
}
]
},
},
{
"firstName" : "Neha",
"lastName" : "Parate",
.....
.......
.....
"departments" : {
"deptCd" : "Tax Rebate",
"deptName" : "Tax Rebate Handling Dept",
"status" : "A",
"subdepts" : [
{
"subdeptCd" : "1D",
"subdeptName" : "Tax Clearning",
"desc" : "",
"status" : "A"
"technology" : [
{
"technologyCd" : "9C",
"technologyName" : "Spring Cloud",
"desc" : "This is best certficate post Google",
"status" : "A"
}
]
}
]
},
}
You can get distinct technologies (technology array elements) with this aggregation:
db.depts.aggregate( [
{
$unwind: "$departments.subdepts"
},
{
$unwind: "$departments.subdepts.technology"
},
{
$match: { "departments.subdepts.subdeptCd": "1D" }
},
{
$group: { _id: "$departments.subdepts.technology.technologyCd", tech: { $first: "$departments.subdepts.technology" } }
},
{
$replaceRoot: { newRoot: "$tech" }
}
] )

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

How to create view to read from two collections in mongoDB?

Started with mongoDB syntax and use in project.
I am looking for a solution where I can combine more than two collections with couple of condition to create a view.
Here is my collection Range
/* 1 */
{
"_id" : ObjectId("1"),
"range" : {
"start" : "00"
},
"products" : [
{
"id" : "01",
"name" : "FirstProduct",
"type" : "First Type"
},
{
"id" : "02",
"name" : "Second Product",
"type" : "Second Type"
},
{
"id" : "03",
"name" : "Third Product",
"type" : "Third Type"
},
]
}
/* 2 */
{
"_id" : ObjectId("2"),
"range" : {
"start" : "100",
},
"products" : [
{
"id" : "01",
"name" : "First Product",
"type" : "First Type"
},
{
"id" : "02",
"name" : "Second Product",
"type" : "Second Type"
}
]
}
/* 3 */
{
"_id" : ObjectId("3"),
"range" : {
"start" : "500",
},
"products" : [
{
"id" : "01",
"name" : "First Product",
"type" : "First Type"
},
{
"id" : "02",
"name" : "Second Product",
"type" : "Second Type"
}
]
}
Second Collection. Stock
/* 1 */
{
"_id" : ObjectId("1"),
"range" : {
"start" : "00"
},
"products" : [
{
"id" : "01",
"expired" : false,
"returned" : false
},
{
"id" : "02",
"expired" : false,
"returned" : false
}
]
}
/* 2 */
{
"_id" : ObjectId("02"),
"range" : {
"start" : "100"
},
"products" : [
{
"id" : "01",
"expired" : true,
"returned" : true
},
{
"id" : "02",
"expired" : true,
"returned" : true
}
{
"id" : "03",
"expired" : true,
"returned" : true
}
]
}
Now want to have a view with combine result from above two collection above.
For each range document in Range collections
if Range.range.start = Stock.range.start
if Range.products.id = Stock.products.id
copy "expired" and "returned" field from Stock for that product and
add to Range.product
end if
end if
Return Range
So final result will something like below.
/* 1 */
{
"_id" : ObjectId("1"),
"range" : {
"start" : "00"
},
"products" : [
{
"id" : "01",
"name" : "FirstProduct",
"type" : "First Type"
"expired" : false,
"returned" : false
},
{
"id" : "02",
"name" : "Second Product",
"type" : "Second Type"
"expired" : false,
"returned" : false
}
]
}
/* 2 */
{
"_id" : ObjectId("2"),
"range" : {
"start" : "100",
},
"products" : [
{
"id" : "01",
"name" : "First Product",
"type" : "First Type",
"expired" : true,
"returned" : true
},
{
"id" : "02",
"name" : "Second Product",
"type" : "Second Type",
"expired" : true,
"returned" : true
}
]
}
/* 3 */
{
"_id" : ObjectId("3"),
"range" : {
"start" : "500",
},
"products" : [
{
"id" : "01",
"name" : "First Product",
"type" : "First Type"
},
{
"id" : "02",
"name" : "Second Product",
"type" : "Second Type"
}
]
}
I started with aggregate pipeline stages with fail to get right queries.
if anyone can help with right syntax and proper aggregate function.
Thanks in advance.
You need $lookup to merge the data from both collections but then you have to use $unwind to be able to match corresponding documents by product.id. In the last step you can use $group to get back an array:
db.Range.aggregate([
{
$lookup: {
from: "Stock",
localField: "range.start",
foreignField: "range.start",
as: "stock"
}
},
{
$unwind: "$stock"
},
{
$unwind: "$products"
},
{
$unwind: "$stock.products"
},
{
$match: { $expr: { $eq: [ "$products.id", "$stock.products.id" ] } }
},
{
$group: {
_id: "$_id",
"range": { $first: "$range" },
products: {
$push: {
id: "$products.id",
name: "$products.name",
type: "$products.type",
expired: "$stock.products.expired",
returned: "$stock.products.returned"
}
}
}
}
])
EDIT: Alternative solution which operates directly on arrays using $map and $filter below. The drawback is that the code is less readable but the good part is that it should return documents when there's no match and you should get better performance using this approach
db.Range.aggregate([
{
$lookup: {
from: "Stock",
localField: "range.start",
foreignField: "range.start",
as: "stock"
}
},
{
$unwind: "$stock"
},
{
$addFields: {
products: {
$map: {
input: "$products",
as: "p",
in: {
$let: {
vars: {
stockItem: {
$arrayElemAt: [
{ $filter: { input: "$stock.products", cond: { $eq: [ "$$p.id", "$$this.id" ] } } }, 0
]
}
},
in: {
$cond: [
{ $eq: [ "$$stockItem", undefined ] },
"$$p",
{
id: "$$p.id",
name: "$$p.name",
type: "$$p.type",
expired: "$$stockItem.expired",
returned: "$$stockItem.returned",
}
]
}
}
}
}
}
}
},
{
$project: {
stock: 0
}
}
])

Should I choose mongo aggregation or should do it at application level service

I have the below collections.
Company:
{
"_id" : ObjectId("5a7848e8ca70273218e9d743"),
"name" : "Google",
"departments" : [
{
"name" : "IT",
"_id" : "1234567890"
},
{
"name" : "Sales",
"_id" : "1234567891"
}
]
}
User:
{
"_id" : ObjectId("5a784977ca70273218e9d759"),
"name" : "Sankarshan",
"company" : ObjectId("5a7848e8ca70273218e9d743"),
"department" : "1234567890"
}
topicCategories
{
"_id" : ObjectId("5a784a10ca70273218e9d76f"),
"name" : "topicCtegoryOne",
"order" : 1
}
topics
{
"_id" : ObjectId("5a784cc3ca70273218e9d7d3"),
"topicCategory" : ObjectId("5a784a10ca70273218e9d76f"),
"name" : "TopicOne",
"order" : 1,
"label" : "oneTopic",
"color" : "red"
}
dimentions
{
"_id" : ObjectId("5a784fdcca70273218e9d869"),
"name" : "dimentionOne"
}
queries
{
"_id" : ObjectId("5a78519aca70273218e9d8d7"),
"topic" : ObjectId("5a784cc3ca70273218e9d7d3"),
"dimention" : ObjectId("5a784fdcca70273218e9d869"),
"order" : 1,
"label" : "queryLabelOne",
"statement" : "This is one question - Top1"
}
User_responses
{
"_id" : ObjectId("5a7859f5ca70273218e9da7d"),
"user" : ObjectId("5a784977ca70273218e9d759"),
"company" : ObjectId("5a7848e8ca70273218e9d743"),
"department" : "1234567890",
"response" : {
"question" : ObjectId("5a78519aca70273218e9d8d7"),
"topic" : ObjectId("5a784cc3ca70273218e9d7d3"),
"topicCategory" : ObjectId("5a784a10ca70273218e9d76f"),
"dimention" : ObjectId("5a784fdcca70273218e9d869"),
"rating" : 5,
"color" : "red"
}
}
What is happening above
Each User belongs to a company.
Each Topic belongs to a Topic category.
Each Query belongs to a Topic
Each query has a dimention.
A user can answer a Query in terms of rating.
The user response to each question will be stored in User_responses.
What I want to achieve
An aggregated result of responses based on Topic category and Dimension
Expected aggregated result format
The query is based on one topic category.
All questions on each topic is aggregated based on Topic
Each Topic will have calculated average ratings based on dimension.
Response
{
"topicCategory": "topic category",
"topics": [
{
"topicLabel": "label of the topic one",
"dimentions": [
{
"name": "dimention name one",
"avgValue": 5.6
}
{
"name": "dimention name two",
"avgValue": 4.5
}
]
},
{
"topicLabel": "label of the topic two",
"dimentions": [
{
"name": "dimention name one",
"avgValue": 6.6
}
{
"name": "dimention name two",
"avgValue": 9.5
}
]
}
]
}
Can I implement this using Mongo aggregation or should I do it at service level?
What I have tried doing
db.getCollection('collResp').aggregate([
{
$match:
{
company: ObjectId("5a7848e8ca70273218e9d743"),
department: "1234567890"
}
},
{
$group: {
_id: {
topic: "$response.topic",
category: "$response.topicCategory"
},
responses: {
$push: "$response"
}
}
},
{
$lookup: {
from: 'topics',
localField: '_id.topic',
foreignField: '_id',
as: "topic"
}
},
{
$unwind: {
path: "$topic"
}
},
{
$lookup: {
from: 'topicCategories',
localField: '_id.category',
foreignField: '_id',
as: "category"
}
},
{
$unwind: {
path: "$category"
}
}
])
What I got
/* 1 */
{
"_id" : {
"topic" : ObjectId("5a78512cca70273218e9d8bd"),
"category" : ObjectId("5a784a10ca70273218e9d76f")
},
"responses" : [
{
"question" : ObjectId("5a7851a6ca70273218e9d8d9"),
"topic" : ObjectId("5a78512cca70273218e9d8bd"),
"topicCategory" : ObjectId("5a784a10ca70273218e9d76f"),
"dimention" : ObjectId("5a784fdcca70273218e9d869"),
"rating" : 8,
"color" : "red"
},
{
"question" : ObjectId("5a78580eca70273218e9da10"),
"topic" : ObjectId("5a78512cca70273218e9d8bd"),
"topicCategory" : ObjectId("5a784a10ca70273218e9d76f"),
"dimention" : ObjectId("5a7851d5ca70273218e9d8ee"),
"rating" : 7,
"color" : "red"
}
],
"topic" : {
"_id" : ObjectId("5a78512cca70273218e9d8bd"),
"topicCategory" : ObjectId("5a784a10ca70273218e9d76f"),
"name" : "TopicTwo",
"order" : 2,
"label" : "twoTopic",
"color" : "green"
},
"category" : {
"_id" : ObjectId("5a784a10ca70273218e9d76f"),
"name" : "topicCtegoryOne",
"order" : 1
}
}
/* 2 */
{
"_id" : {
"topic" : ObjectId("5a784cc3ca70273218e9d7d3"),
"category" : ObjectId("5a784a10ca70273218e9d76f")
},
"responses" : [
{
"question" : ObjectId("5a78519aca70273218e9d8d7"),
"topic" : ObjectId("5a784cc3ca70273218e9d7d3"),
"topicCategory" : ObjectId("5a784a10ca70273218e9d76f"),
"dimention" : ObjectId("5a784fdcca70273218e9d869"),
"rating" : 5,
"color" : "red"
},
{
"question" : ObjectId("5a785807ca70273218e9da0e"),
"topic" : ObjectId("5a784cc3ca70273218e9d7d3"),
"topicCategory" : ObjectId("5a784a10ca70273218e9d76f"),
"dimention" : ObjectId("5a7851d5ca70273218e9d8ee"),
"rating" : 3,
"color" : "red"
}
],
"topic" : {
"_id" : ObjectId("5a784cc3ca70273218e9d7d3"),
"topicCategory" : ObjectId("5a784a10ca70273218e9d76f"),
"name" : "TopicOne",
"order" : 1,
"label" : "oneTopic",
"color" : "red"
},
"category" : {
"_id" : ObjectId("5a784a10ca70273218e9d76f"),
"name" : "topicCtegoryOne",
"order" : 1
}
}
What more is required
Each response had a question. And each question has a dimention
I need to get average rating based on dimention.
Thank you.

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