I have collection with this data :
[
{
_id: "id1",
parentId: null,
text: "text 1"
},
{
_id: "id2",
parentId: null,
text: "text 2"
},
{
_id: "id3",
parentId: null,
text: "text 3"
},
{
_id: "id4",
parentId: "id1",
text: "text 4"
},
{
_id: "id5",
parentId: "id1",
text: "text 5"
},
{
_id: "id6",
parentId: "id2",
text: "text 6"
},
{
_id: "id7",
parentId: "id5",
text: "text 7"
}
]
I want every child to come after its parent like this :
[
{
_id: "id1",
parentId: null,
text: "text 1"
},
{
_id: "id4",
parentId: "id1",
text: "text 4"
},
{
_id: "id5",
parentId: "id1",
text: "text 5"
},
{
_id: "id7",
parentId: "id5",
text: "text 7"
},
{
_id: "id2",
parentId: null,
text: "text 2"
},
{
_id: "id6",
parentId: "id2",
text: "text 6"
},
{
_id: "id3",
parentId: null,
text: "text 3"
}
]
but I didn't find any query for this in mongodb . Is that possible ?
I use this schema for store comments and I need to sort like that.
for sql query I found this video on youtube: https://www.youtube.com/watch?v=yA-YqKBNyNc
Here is a solution using aggregate framework :
db.test.aggregate([{
$addFields: {
parentIdId: {
$concat: [{
$ifNull: [{
$toString: "$parentId"
}, ""]
}, {
$toString: "$_id"
}]
}
}
}, {
$sort: {
parentIdId: 1
}
}, {
$project: {
parentIdId: 0
}
}])
The result :
{"_id":"id1","parentId":null,"text":"text 1","parentIdId":"id1"}
{"_id":"id4","parentId":"id1","text":"text 4","parentIdId":"id1id4"}
{"_id":"id5","parentId":"id1","text":"text 5","parentIdId":"id1id5"}
{"_id":"id2","parentId":null,"text":"text 2","parentIdId":"id2"}
{"_id":"id6","parentId":"id2","text":"text 6","parentIdId":"id2id6"}
{"_id":"id3","parentId":null,"text":"text 3","parentIdId":"id3"}
{"_id":"id7","parentId":"id5","text":"text 7","parentIdId":"id5id7"}
In this solution, I create a new field in your query result that is the concatenation of parentId and Id and, then I sort on it.
If you want to remove this technical field, you can add this pipeline stage :
{$project: {parentIdId: 0}}
Your final query would be :
db.test.aggregate([{
$addFields: {
parentIdId: {
$concat: [{
$ifNull: [{
$toString: "$parentId"
}, ""]
}, {
$toString: "$_id"
}]
}
}
}, {
$sort: {
parentIdId: 1
}
}, {
$project: {
parentIdId: 0
}
}])
Related
I have the following documents:
_id: "Team 1"
count: 1200
_id: "Team 2"
count: 1170
_id: "Team 3"
count: 1006
_id: "Team 4"
count: 932
_id: "Team 5"
count: 931
_id: "Team 6"
count: 899
_id: "Team 7"
count: 895
I want to convert the above documents to the following object:
{
"categories": [
"Team 2",
"Team 5",
"Team 1",
"Team 7",
"Team 6",
"Team 4",
"Team 3"
],
"series": [
{
"counts": [
1170,
931,
1200,
895,
899,
932,
1006
]
}
]
}
The overall order is not important, however, the categories order has to be consistent with the counts order. Meaning if "Team 1" is number 3 in the categories array, its count should also be number 3 in the counts array. How do I achieve this?
I've managed to do it like this:
{
'$sort': {
'_id': 1
}
}, {
'$group': {
'_id': null,
'categories': {
'$push': '$_id'
},
'series': {
'$push': '$count'
}
}
}, {
'$group': {
'_id': null,
'categories': {
'$push': '$categories'
},
'series': {
'$push': {
'counts': '$series'
}
}
}
}, {
'$project': {
'_id': 0,
'categories': 1,
'series': 1
}
}
But I'm pretty sure there is a more clean way of doing it, maybe only using group once?
You can remove the extra group stage and do it in project stage,
$sort same as you did
$group same as you did
$project just correct the series field array construction
[
{ $sort: { _id: -1 } },
{
$group: {
_id: null,
categories: { $push: "$_id" },
series: { $push: "$count" }
}
},
{
$project: {
_id: 0,
categories: 1,
series: [{ counts: "$series" }]
}
}
]
Playground
I am using MongoDB 4.2.9 and have the following requirements:
Collection 'A' has multiple documents with a string field 'status' that I need to filter on
Collection 'B' has multiple documents
Collection A
{ _id: "1",
status: "Report",
type: "Academy",
rating: "Excellent",
ReportNo: "A1"
},
{ _id: "2",
status: "Open",
type: "Academy",
rating: "",
ReportNo: ""
},
{ _id: "3",
status: "Draft",
type: "Academy",
rating: "",
ReportNo: ""
},
{ _id: "4",
status: "Report",
type: "Academy",
rating: "Great",
ReportNo: "A4"
}
Collection B
{ _id: "98",
status: "Archived",
type: "Academy",
rating: "So So",
ReportNo: "X2"
},
{ _id: "99",
status: "Archived",
type: "Academy",
rating: "Great",
ReportNo: "X1"
}
Resulting View
{ _id: "1",
status: "Report",
type: "Academy",
rating: "Excellent",
ReportNo: "A1"
},
{ _id: "4",
status: "Report",
type: "Academy",
rating: "Great",
ReportNo: "A4"
},
{ _id: "98",
status: "Archived",
type: "Academy",
rating: "So So",
ReportNo: "X2"
},
{ _id: "99",
status: "Archived",
type: "Academy",
rating: "Great",
ReportNo: "X1"
}
My goal is to create an aggregation view so that I can filter on a status value in Collection 'A' and then merge those results with Collection 'B' and show in the view ?
I can filter on Collection 'A' using the match call, just can't see how to merge resulting documents into Collection 'B'
From my understandings, your "merge" behaviour is actually a union view of filtered view of collection A and collection B.
With MongoDB v4.2, you can use $facet to handle collection A and collection B separately.
simply perform filtering on A
perform uncorrelated $lookup on B
wrangle the result and merge them together to get the union view that you are looking for.
db.createCollection(
"unionView",
{
"viewOn" : "A",
"pipeline" : [
{
"$facet": {
"A": [
{
"$match": {
status: "Report"
}
}
],
"B": [
{
$limit: 1
},
{
"$lookup": {
"from": "B",
"pipeline": [],
"as": "B"
}
},
{
$unwind: "$B"
},
{
"$replaceRoot": {
"newRoot": "$B"
}
}
]
}
},
{
$project: {
all: {
"$setUnion": [
"$A",
"$B"
]
}
}
},
{
$unwind: "$all"
},
{
"$replaceRoot": {
"newRoot": "$all"
}
}
]
}
)
Here is the Mongo Playground for your reference.
With MongoDB v4.4+, you can create a view with $unionWith
db.createCollection(
"unionView",
{
"viewOn" : "A",
"pipeline" : [
{
"$match": {
status: "Report"
}
},
{
"$unionWith": {
"coll": "B"
}
}
]
}
)
Here is the Mongo playground for your reference.
Let's say I have a document like this:
{
_id: ObjectId("1234567890"),
author: "ABC1",
text:"this is a Post",
details: {
time: "14/05/2015",
Edit: "none"
},
comments: [
{
comment_text: "Hello",
user: "alan",
time:"20/05/2014 20:44"
},
{
comment_text: "Hi Every One",
user: "bob",
time:"20/05/2014 20:44"
},
{
comment_text: "Good morning , Alan",
user: "Alan",
time:"20/05/2014 20:44"
},
{
comment_text: "I'm bob",
user: "bob",
time:"20/05/2014 20:44"
},
],
category: "IT"
}
I want to build a query that returns this object but with only Bob's comments in the comments array.
{
author: "ABC1",
text:"this is a Post",
details: {
time: "14/05/2015",
Edit: "none"
},
comments: [
{
comment_text: "Hi Every One",
user: "bob",
time:"20/05/2014 20:44"
},
{
comment_text: "I'm bob",
user: "bob",
time:"20/05/2014 20:44"
},
],
category: "IT"
}
How can this be done?
Using $unwind (and $match) in an aggregation pipeline will get me the correct subdocuments, but not as an array of objects within the original document.
Using $elemMatch within a projection (with find() or findOne()) only returns the first matching comment (subdocument).
You can use the $filter operator in the $project stage of an aggregation pipeline:
db.collection.aggregate([
{
$project: {
_id: 0,
author: 1,
text: 1,
details: 1,
category: 1,
comments: {
$filter: {
input: "$comments",
as: "comment",
cond: {
$eq: [
"$$comment.user",
"bob"
]
}
}
}
}
}
])
Example mongoplayground
I have a mongo collection of documents having category, subcategory and item names.
Category is an enum ["A", "B", "C"], subCategory and item are Strings.
{ _id: 1, category: "A", subCategory: "a1", item: "item1"}
{ _id: 2, category: "A", subCategory: "a1", item: "item2"}
{ _id: 3, category: "A", subCategory: "a1", item: "item3"}
{ _id: 4, category: "A", subCategory: "a2", item: "item4"}
{ _id: 5, category: "B", subCategory: "b1", item: "item5"}
{ _id: 6, category: "B", subCategory: "b1", item: "item6"}
I am trying to write a query which will return the data in the following format:
{
A: [
{ subCategory: 'a1', items: ["item1", "item2", "item3"]},
{ subCategory: 'a2', items: ["item4",]}
],
B: [
{ subCategory: 'b1', items: ["item5", "item6"]}
]
}
Here is what I have tried so far:
const items = await Items.aggregate([
{
$group: {
_id: { category: "$category", subCategory: "$subCategory" },
items: { $push: "$item" },
},
},
{
$group: {
_id: "$_id.category",
subCategories: {
$push: {
subCategory: "$_id.subCategory",
items: "$items",
},
},
},
},
{
$project: {
_id: 0,
category: "$_id",
subCategories: 1,
},
},
{
$addFields: {
array: [
{
k: "$category",
v: "$subCategories",
},
],
},
},
{
$replaceRoot: {
newRoot: { $arrayToObject: "$array" },
},
},
]);
And here is the output (close, but not exactly what I need):
[
{
A: [
{ subCategory: 'a1', items: ["item1", "item2", "item3"]},
{ subCategory: 'a2', items: ["item4",]}
],
},
{
B: [
{ subCategory: 'b1', items: ["item5", "item6"]}
]
},
]
Please advise, how can I achieve the required result
Try below aggregation query :
db.collection.aggregate([
/** Group on `category + subCategory` & push item's to `items` array */
{
$group: { _id: { category: "$category", subCategory: "$subCategory" }, items: { $push: "$item" } }
},
/** Group on `category` field & push objects({subCategory:...,items:...}) to `v` field */
{
$group: { _id: "$_id.category", v: { $push: { subCategory: "$_id.subCategory", items: "$items" } } }
},
/** remove `_id` field & add `k` field & project `v` field */
{
$project: { _id: 0, k: "$_id", v: 1 }
},
/** Replace root doc with arrayToObject convert root doc */
{
$replaceRoot: { newRoot: { $arrayToObject: [ [ "$$ROOT" ] ] } }
},
/** Group on empty will just group on all docs without a filter & merge all objects */
{
$group: { _id: "", data: { $mergeObjects: "$$ROOT" } }
},
{
$replaceRoot: { newRoot: "$data" }
}
])
Test : mongoplayground
Ref : aggregation-pipeline
I need to group the data with set of fields using mongodb aggregate. Here is my data in the collection
[
{
bookName: "aaaa",
bookNo: "1",
registeredDate: "2018-02-01T06:51:16.738Z"
},
{
bookName: "bbbb",
bookNo: "2",
registeredDate: "2018-02-01T06:51:29.244Z"
}
{
bookName: "cccc",
bookNo: "1",
registeredDate: "2018-02-01T06:51:29.244Z"
}
{
bookName: "dddd",
bookNo: "2",
registeredDate: "2018-02-01T06:51:29.244Z"
}
]
I need to aggregate the above data into group by and show the data inside the group. Here is the output that i need,
{
Books: [
{
bookNo: "1",
books: [{
bookName: "aaaa",
registeredDate: "2018-02-01T06:51:16.738Z"
},
{
bookName: "cccc",
registeredDate: ""2018-02-01T06:51:29.244Z"
}]
},
{
bookNo: "2",
books: [{
bookName: "bbbb",
registeredDate: "2018-02-01T06:51:29.244Z"
},
{
bookName: "dddd",
registeredDate: "2018-02-01T06:51:29.244Z"
}]
}
]
}
Someone help me find a solution.
Thanks in Advance,
Try following aggregation:
db.books.aggregate([
{
$group: {
_id: "$bookNo",
books: {
$push: {
bookName: "$bookName",
registeredDate: "$registeredDate"
}
}
}
},
{
$project:{
_id: 0,
bookNo: "$_id",
books: 1
}
}
])
In $group you can specify which fields will be $pushed from each document. Then you can use $project to just rename _id to bookNo field
db.collection.aggregate(
// Pipeline
[
// Stage 1
{
$group: {
_id: '$bookNo',
//Books:{$addToSet:{bookNo:'$bookNo'}},
books: {
$addToSet: {
bookname: '$bookName',
registeredDate: '$registeredDate'
}
},
}
},
// Stage 2
{
$group: {
_id: null,
Books: {
$addToSet: {
books: '$books',
bookNo: '$_id'
}
}
}
},
// Stage 3
{
$project: {
_id: 0,
Books: 1
}
}
]
);