Retrieve nested MongoDB object after querying nested object - mongodb

Given the following MongoDB collection groups:
[{
_id: 'g1',
name: 'Group 1',
projects: [{
_id: 'p1',
name: 'Project 1',
tasks: [{
_id: 't1',
name: 'Task 1'
}, {
_id: 't2',
name: 'Task 2'
}]
}]
}]
I need to find task with _id = t2 and return the whole task object:
{
_id: 't2',
name: 'Task 2'
}
I'm able to find the group, either using mongodb:
db.groups.find({ 'projects.tasks._id': ObjectId('t2') })
Or with Mongoose:
Groups.findOne({ 'projects.tasks._id': 't2' })
But of course, this returns the entire group document, and not the task.
I've tried:
db.groups.aggregate([
{
$match: { 'projects.tasks._id': ObjectId('t2') }
}, {
$replaceRoot: {
newRoot: '$chapters.sections'
}
}
])
But this throws an error.

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] } } } ,])

Aggregate data and populate in one request

I am a bit puzzled by populate in MongoDB.
I've got a Schema:
import { Schema, Document, model } from "mongoose";
export interface ProductGroupType {
id: Schema.Types.ObjectId,
title: String,
name: String,
description: String,
}
const ProductGroupSchema: Schema<Document<ProductGroupType>> = new Schema({
title: { type: String, trim: true },
name: { type: String, trim: true },
description: { type: String, trim: true },
}, { collection: "productGroups", timestamps: true });
export const ProductGroupModel = model('ProductGroup', ProductGroupSchema);
and products
import { Schema, Document, model } from "mongoose";
import { plugin as autocomplete } from 'mongoose-auto-increment';
const ProductSchema: Schema<Document<IProduct>> = new Schema({
article: Number,
name: String,
category: { type: Schema.Types.ObjectId, ref: 'ProductCategory' },
group: { type: Schema.Types.ObjectId, ref: 'ProductGroup' },
price: { type: Number, default: 0 },
discount: { type: Number, default: 0 },
stock: {
available: { type: Number, default: 0 },
reserved: { type: Number, default: 0 },
},
images: [Object],
description: String,
productDetails: Object,
}, { collection: "products", timestamps: true });
ProductSchema.plugin(autocomplete, {
model: 'Product',
field: 'article',
startAt: 10000,
});
export const ProductModel = model('Product', ProductSchema);
I need to make a request and group on the MongoDB side data by the field 'group'.
I can make this like this:
await ProductModel.aggregate([
{ $match: { category: Types.ObjectId(queryCategory.id) } },
{
$group: {
_id: '$group',
products: {
$push: {
id: '$_id',
name: '$name',
article: '$article',
price: '$price',
discount: '$discount',
description: '$description',
group: '$groupName',
}
},
count: { $sum: 1 },
}
},
]);
but the output here is:
[
{ _id: 61969583ad32e113f87d0e99, products: [ [Object] ], count: 1 },
{
_id: 61993fff452631090bfff750,
products: [ [Object], [Object] ],
count: 2
}
]
almost what I need but I've been playing around with population and I cannot make it work with Aggregation framework.
I already tried to use the 'lookup' operator but it returns an empty array and doesn't want to work.
That's how I wanted to make it work:
const products: Array<IProduct> = await ProductModel.aggregate([
{ $match: { category: Types.ObjectId(queryCategory.id) } },
{
$group: {
_id: '$group',
products: {
$push: {
id: '$_id',
name: '$name',
article: '$article',
price: '$price',
discount: '$discount',
description: '$description',
group: '$groupName',
}
},
count: { $sum: 1 },
}
},
{
$lookup: {
"from": "productGroups",
"localField": "group",
"foreignField": "_id",
"as": "groupName"
},
},
]);
Is it possible to get the same result as I've got now but populate in the same query group field?
So far the only way I've managed to populate it like this as the second request:
await ProductGroupModel.populate( products.map( (product: any) => {
return {
_id: new ProductGroupModel(product),
products: product.products,
count: product.count,
}
} ), { "path": "_id" } )
In a MongoDB aggregation pipeline, the $group stage passes along only those field explicitly declared in the stage.
In the same pipeline you show, the documents passed along by the $group stage would contain the fields:
_id
products
count
When the exector arrives a the $lookup stage, none of the documents contain a field named group.
However, the value previously contained in the group field still exists, in the _id field.
In the $lookup stage, use
"localField": "_id",
to find documents based on that value.

Nested aggregation subgrouping results with Mongodb aggregation framework

I have a dataset with metrics collected from a group of sensors.
My dataset looks like this:
{type: 1, display: 'foo', value: 'A'}
{type: 2, display: 'bar', value: 'B'}
{type: 2, display: 'foo', value: 'B'}
I am trying to aggregate the results and get some meaning insights via a REST API. I am somehow trying to produce aggregated results as:
[{
type: 1,
displays: [
{
name: 'foo',
count: 1
}
],
values: [
{
name: 'A',
count: 1
}
],
total_count: 1
},{
type: 2,
displays: [
{
name: 'foo',
count: 1
} , {
name: 'bar',
count: 1
}
],
values: [
{
name: 'B',
count: 2
}
],
total_count: 2
}]
Summarizing the aggregated results and producing shallow results is straight forward, I am struggling though as I can't created the nested counters for types and displays all together.
I have tried to use various aggregation operators with no luck.
Basically I can get one group by types or displays as:
db.logs.aggregate([
{
$group: {
_id: {
type: '$type',
display: '$display'
},
count: { $sum: 1 }
}
}, {
$group: {
_id: '$_id.type',
displays: {
$push: {
name: "$_id.display",
count: "$count"
}
}
}
}
]);
Any help will be highly appreciated.

Update nested array objects based on a property in MongoDB

I have a MongoDB collection which has documents which look like this:
{
createdTimestamp: 111111111111,
items: [{
itemName: 'Name 1',
quantity: 10
}, {
itemName: 'Name 2'
quantity: 20
}]
}
Now, I want to update all documents so that itemName: 'Name 1' would be updated to itemName: 'New Name'.
After an update, the above document should look like as below:
{
createdTimestamp: 111111111111,
items: [{
itemName: 'New Name',
quantity: 10
}, {
itemName: 'Name 2'
quantity: 20
}]
}
Is there any way to do this, without iterating over all documents myself?
You need to use $ positional operator to update an array element and with multi: true option you can update multiple document with the same match
db.collection.update(
{ 'items': { '$elemMatch': { 'itemName': 'Name 1' }}},
{ '$set': { 'items.$.itemName': 'New Name' }},
{ 'multi': true }
)
and with the mongodb 3.6 arrayFilters
db.collection.update(
{ 'items': { '$elemMatch': { 'itemName': 'Name 1' }}},
{ '$set': { 'items.$[item].itemName': 'New Name' }},
{ 'arrayFilter': [{ 'item.itemName': 'Name 1' }], 'multi': true }
)
you can use mongoDb arrayFilters
db.collection.update(
{ },
{ "$set": { "items.$[elem].itemName": 'new Name' } },
{ "arrayFilters": [{ "elem.itemName": 'Name 1' }], "multi": true }
)

Mongodb schema for trello

I'm trying to make something similar to Trello.com. There will be many boards, and each board can have many lists, and each lists can have many items. The order of the lists and items can change often by the user, so I came up with something like this that would minimize queries. I just read some of the mongo documentation last night so I would appreciate any feedbacks and suggestions
Here is a list of boards:
{
boards: [
{
_id: 901292190
name: 'board1'
}
]
}
Here is when you view a single board:
{
_id: 901292190
name: 'board 1',
lists: [
{
_id: 932092,
name: 'todo',
items: [
{
_id: 9320903,
name: 'go shopping',
priority: 1,
createdby: 'user1',
assignedto: 'user2'
},
{
_id: 3902901,
name: 'go to school',
priority: 2,
createdby: 'user1',
assignedto: 'user2'
}
]
},
{
name: 'finished',
items: [
{
_id: 91209,
name: 'programming',
priority: 1,
createdby: 'user1',
assignedto: 'user2'
}
]
}
]
}