How to get this parent child query data in Mongo DB - mongodb

I am building a simple messaging system and I have a messageSchema which is setup as below:
const messageSchema = new mongoose.Schema({
replyTo: {
type: mongoose.Schema.ObjectId,
ref: 'Message',
default: null,
},
name: {
type: Object,
required: [true, 'Message has a name'],
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
},
email: {
type: Object,
required: [true, 'Message has a email'],
},
subject: {
type: String,
required: [true, 'Message has a subject'],
},
content: {
type: String,
required: [true, 'Message has content'],
},
createdAt: {
type: Date,
default: Date.now(),
},
status: {
type: String,
default: 'unread',
required: [true, 'Message has a status'],
},});
Sample data below:
[
{
_id: ObjectId('53ed7efca75ca1a5248a281a'),
name: 'Person 1',
createdAt: ISODate('2021-01-01T01:00:00.000Z'),
subject: 'M1',
content: 'M1 content',
replyTo: null,
},
{
_id: ObjectId('53ed80bba75ca1a5248a281b'),
name: 'Person 2',
subject: 'M2 - Reply 1 to M1',
content: 'M2 content',
createdAt: ISODate('2021-01-01T02:00:00.000Z'),
replyTo: ObjectId('53ed7efca75ca1a5248a281a'),
},
{
_id: ObjectId('53ed80bba75ca1a5248a281c'),
name: 'Person 3',
subject: 'M3 - Reply 2 to M1',
content: 'M3 content',
createdAt: ISODate('2021-01-01T03:00:00.000Z'),
replyTo: ObjectId('53ed7efca75ca1a5248a281a'),
},
{
_id: ObjectId('53ed80bba75ca1a5248a281d'),
name: 'Person 4',
subject: 'M4',
content: 'M4 content',
createdAt: ISODate('2021-01-01T02:30:00.000Z'),
replyTo: null,
},
];
I am now trying to query the sample above to produce an inbox style response, so that the root message (no replyTo) is the top-level message, has a latest node with latest message info, and has children(if any) in a children node. See below for desired output.
[
{
_id: ObjectId('53ed7efca75ca1a5248a281a'),
name: 'Person 1',
createdAt: ISODate('2021-01-01T01:00:00.000Z'),
subject: 'M1',
content: 'M1 content',
replyTo: null,
latest: {
_id: ObjectId('53ed80bba75ca1a5248a281c'),
name: 'Person 3',
subject: 'M3 - Reply 2 to M1',
content: 'M3 content',
createdAt: ISODate('2021-01-01T03:00:00.000Z'),
replyTo: ObjectId('53ed7efca75ca1a5248a281a'),
},
children: [
{
_id: ObjectId('53ed80bba75ca1a5248a281b'),
name: 'Person 2',
subject: 'M2 - Reply 1 to M1',
content: 'M2 content',
createdAt: ISODate('2021-01-01T02:00:00.000Z'),
replyTo: ObjectId('53ed7efca75ca1a5248a281a'),
},
{
_id: ObjectId('53ed80bba75ca1a5248a281c'),
name: 'Person 3',
subject: 'M3 - Reply 2 to M1',
content: 'M3 content',
createdAt: ISODate('2021-01-01T03:00:00.000Z'),
replyTo: ObjectId('53ed7efca75ca1a5248a281a'),
},
]
},
{
_id: ObjectId('53ed80bba75ca1a5248a281d'),
name: 'Person 4',
subject: 'M4',
content: 'M4 content',
createdAt: ISODate('2021-01-01T02:30:00.000Z'),
replyTo: null,
latest: {
_id: ObjectId('53ed80bba75ca1a5248a281d'),
name: 'Person 4',
subject: 'M4',
content: 'M4 content',
createdAt: ISODate('2021-01-01T02:30:00.000Z'),
replyTo: null,
},
children: []
},];
Appreciate any help in getting this query sorted. Thanks.

You can use aggregation pipeline stages,
$match replyTo us null condition
$graphLookup to join same collection to get reply messages in children
$addFields to check condition is children is empty then return root document otherwise return last element from children
db.collection.aggregate([
{ $match: { replyTo: null } },
{
"$graphLookup": {
"from": "collection",
"startWith": "$_id",
"connectFromField": "_id",
"connectToField": "replyTo",
"as": "children"
}
},
{
$addFields: {
latest: {
$cond: [
{ $eq: [{ $size: "$children" }, 0] },
"$$ROOT",
{ $arrayElemAt: [{$slice: ["$children", -1]}, 0] }
]
}
}
}
])
Playground
For query optimization you can do last stage $addFields process in your client side.

Related

mongodb match in aggregate is not working

I have the following 2 schemas:
bookSchema = new mongoose.Schema(
{
name: {
type: String,
required: [true, 'Name required']
},
author:{
type:mongoose.Schema.ObjectId,
ref: 'Author'
}
authorSchema = new mongoose.Schema(
{
name: {
type: String,
trim: true,
})
I have an array of Author object references and I need to filter books based on these:
authors: [
{
_id: new ObjectId("6358edf901dbe13bc738b63b"),
name: 'Name 2',
__v: 0
},
{
_id: new ObjectId("63592660ca309b7c1a94fb04"),
name: 'Name 1',
__v: 0
}
]
I have tried with below code but it gives empty array as result:
Book.aggregate([ { $match: { $expr: { $in: ['Author', authors] } } } ,])

MongoDB aggregate data using $agg

So i try to aggregate the data from mongoDB using $match and $group to get the exact value an d format with data from mongoDB in JSON as Below:
[ {
_id: new ObjectId("6357b4237cf79bba3e66c096"),
userId: new ObjectId("635762fe85b94eac7466d965"),
formId: new ObjectId("6357921d49de88bb7fffcfe4"),
title: 'Quality',
username: 'Jansenstan24#gmail.coms',
date: '2022-10-25',
answer: {
'6357921d49de88bb7fffcfe8': 'john#nabatisnack.com',
Text: 'john#nabatisnack.com',
'6357b331235053a9d4e8d037': 'john#dose.com',
Email: 'john#dose.com',
'6357b335235053a9d4e8d03a': 'Maja',
Plant: 'Maja'
},
createdAt: 1666692131,
updatedAt: 1666692131,
__v: 0
},
{
_id: new ObjectId("6357b54922866fae378af370"),
userId: new ObjectId("635762fe85b94eac7466d965"),
formId: new ObjectId("6357921d49de88bb7fffcfe4"),
title: 'Quality',
username: 'Jansenstan24#gmail.coms',
date: '2022-10-25',
answer: {
'6357921d49de88bb7fffcfe8': 'john#dose.com',
Text: 'john#dose.com',
'6357b331235053a9d4e8d037': 'john#nabatisnack.com',
Email: 'john#nabatisnack.com',
'6357b335235053a9d4e8d03a': 'Cica',
Plant: 'Cica'
},
createdAt: 1666692425,
updatedAt: 1666692425,
__v: 0
},
{
_id: new ObjectId("6357b6a2a13ab3c4a462be8f"),
userId: new ObjectId("635647c1e24d0a4482e3c0a9"),
formId: new ObjectId("6357921d49de88bb7fffcfe4"),
title: 'Quality',
username: 'adam#wegodev.com',
date: '2022-10-25',
answer: {
'6357921d49de88bb7fffcfe8': '12',
Text: '12',
'6357b331235053a9d4e8d037': 'john#dose.coms',
Email: 'john#dose.coms',
'6357b335235053a9d4e8d03a': 'Ranca',
Plant: 'Ranca'
},
createdAt: 1666692770,
updatedAt: 1666692770,
__v: 0
}]
using this line of code in javascript:
forms = await Answer.aggregate([
{
$match: { title: "Quality" },
},
{ $group: { title: "Quality"} },
]);
what i expected is the result like this:
{
formId: new ObjectId("6357921d49de88bb7fffcfe4"),
title: 'Quality',
username: [{'Jansenstan24#gmail.coms',adam#wegodev.com}]
date: [{'2022-10-25','2022-10-26}]
answer: [{
'6357921d49de88bb7fffcfe8': 'john#nabatisnack.com',
Text: 'john#nabatisnack.com',
'6357b331235053a9d4e8d037': 'john#dose.com',
Email: 'john#dose.com',
'6357b335235053a9d4e8d03a': 'Maja',
Plant: 'Maja'
},{
'6357921d49de88bb7fffcfe8': 'john#dose.com',
Text: 'john#dose.com',
'6357b331235053a9d4e8d037': 'john#nabatisnack.com',
Email: 'john#nabatisnack.com',
'6357b335235053a9d4e8d03a': 'Cica',
Plant: 'Cica'
},...etc],
},
i try to use more complex query but it seems it not return the result i want and i've try to seek any related problem but it wont return the same result as i want
You have the right idea using $group, you just need to leverage it's syntax properly, like so:
db.collection.aggregate([
{
$match: {
title: "Quality"
}
},
{
$group: {
_id: "$title",
title: {
$first: "$title"
},
username: {
"$addToSet": "$username"
},
date: {
$addToSet: "$date"
},
formId: {
$addToSet: "$formId"
},
answer: {
$push: "$answer"
}
}
}
])
Mongo Playground

MongoDB $graphLookup omits repeat records

I have collection in which I have defined all my products and their sub-products. The sub-products are not defined as sub-documents but as a separate document in the collection.
const productTypeSchema = mongoose.Schema({
name: { type: String, required: true },
category: { type: String, required: true },
subProducts: [subProductsSchema],
})
const subProductsSchema = mongoose.Schema({
name: { type: String, required: true },
subProductId: {
type: mongoose.Schema.Types.ObjectId,
ref: "product",
},
});
A sample record be like
{ _id: 1, name: 'Steel Grid', Category: 'Finished Product',
subProducts: [{ _id: 6, name: 'Load Bar', subProductId: 2},
{ _id: 7, name: 'Cross Bar', subProductId: 2}]
},
{ _id: 2, name 'Steel Bar', Category: 'Raw Material'}
To fetch a product and all its sub-products, I use the #graphLookup
$graphLookup: {
from: "product",
startWith: "$_id",
connectFromField: "subProducts.subProductId",
connectToField: "_id",
depthField: "subpartlevel",
as: "subParts",
},
My issue is that the $graphLookup fetches only one subproduct as both of them are pointing to a single document in the collection. Is there a way to fetch both records?
Experts please help. Thank you.

How to query to get specific objects from array of objects?

Currently I am learning mongodb. Suppose I have one collection named post in mongodb which data is :
[{
id: 123,
uId: 111,
msg: 'test 1',
attachments: [
{name: 'attach1', url: 'https://example.com', isDeleted: false},
{name: 'attach2', url: 'https://example.com', isDeleted: true}
]
},
{
id: 456,
uId: 222,
msg: 'test 2',
attachments: [
{name: 'attach1', url: 'https://example.com', isDeleted: true}
]
},
{
id: 789,
uId: 333,
msg: 'test 3',
attachments: [
{name: 'attach1', url: 'https://example.com', isDeleted: false}
]
}]
Now i want the result of all post where attachments.isDeleted is false which look like :
[{
id: 123,
uId: 111,
msg: 'test 1',
attachments: [
{name: 'attach1', url: 'https://example.com', isDeleted: false}
]
},
{
id: 456,
uId: 222,
msg: 'test 2',
attachments: []
},
{
id: 789,
uId: 333,
msg: 'test 3',
attachments: [
{name: 'attach1', url: 'https://example.com', isDeleted: false}
]
}]
I tried this db.post.find({attachments: {$elemMatch: {isDeleted: false}}}) but it is not working. I have taken help from [https://stackoverflow.com/questions/62953855/how-get-query-for-array-of-objects-in-mongodb]
I think this is what you are looking for. It uses the Mongodb aggregation framework. You can take a look the documentation to see details. In brief, the $project stage allow us select fields to show or to hide, and it admits calculated fields. We calculated a new attachments field using $filter stage with a the given condition (isDeleted equals to false).
db.collection.aggregate([
{
"$project": {
_id: 1,
uId: 1,
msg: 1,
attachments: {
"$filter": {
"input": "$attachments",
"as": "attachment",
"cond": {
"$eq": [
"$$attachment.isDeleted",
false
]
}
}
}
}
}
])
You can try it in: https://mongoplayground.net/p/YDvpkbZIZ2H
Note that I changed id by _id, but it was only for style purpose.
Hope I helped.

Strapi mongodb project specific fields in embedded documents

I'm trying to select especific fields from this document
{
_id: 60d9295db34f1c0144c9b8d4,
title: 'Este es el titulo',
slug: 'este-es-el-titulo',
date: '2021-06-27',
category: {
_id: 60d92921b34f1c0144c9b8d2,
title: 'Categoria 1',
slug: 'categoria-1',
id: '60d92921b34f1c0144c9b8d2'
},
cover: {
_id: 60d72c815e57e6015e38a4c7,
name: 'project4.png',
alternativeText: '',
caption: '',
hash: 'project4_3e8c906bdf',
ext: '.png',
mime: 'image/png',
size: 540.02,
width: 1895,
height: 883,
url: '/uploads/project4_3e8c906bdf.png',
formats: [Object],
provider: 'local',
related: [Array],
createdAt: 2021-06-26T13:32:49.389Z,
updatedAt: 2021-06-28T01:43:57.469Z,
__v: 0,
created_by: '60d69246c6f06d00f64312b0',
updated_by: '60d69246c6f06d00f64312b0',
id: '60d72c815e57e6015e38a4c7'
},
id: '60d9295db34f1c0144c9b8d4'
}
and I want get something like this:
{
_id: 60d9295db34f1c0144c9b8d4,
title: 'Este es el titulo',
slug: 'este-es-el-titulo',
date: '2021-06-27',
category: {
_id: 60d92921b34f1c0144c9b8d2,
title: 'Categoria 1',
slug: 'categoria-1',
id: '60d92921b34f1c0144c9b8d2'
},
cover: { url: '/uploads/project4_3e8c906bdf.png'}
id: '60d9295db34f1c0144c9b8d4'
}
I'm using Strapi with Mongodb and y was trying with the following code:
const requiredData = {
title: 1,
slug: 1,
date: 1,
category: 1,
cover: { url: 1 }
}
const populateData = [
{
path: 'category',
select: ['title', 'slug']
}
]
let editorsPick = await strapi.query('posts')
.model.find({ editorsPick: true}, requiredData)
.limit(3)
.populate(populateData)
But I only have this:
{
_id: 60d9295db34f1c0144c9b8d4,
title: 'Este es el titulo',
slug: 'este-es-el-titulo',
date: '2021-06-27',
category: {
_id: 60d92921b34f1c0144c9b8d2,
title: 'Categoria 1',
slug: 'categoria-1',
id: '60d92921b34f1c0144c9b8d2'
},
id: '60d9295db34f1c0144c9b8d4'
}
(Without cover image), and then try with:
let editorsPick = await strapi.query('posts')
.model.aggregate([
{"$match": { editorsPick: true}},
{"$project": requiredData}
])
But I got an empty array []
Please I need a little help, and sorry for my english