Pretty new to Mongodb and I am trying to construct a query using Mongoose to get a desired result, if possible.
Test data:
{ id: 1, display_time: '01:00', name: 'test1' },
{ id: 2, display_time: '03:00', name: 'test2' },
{ id: 3, display_time: '01:00', name: 'test3' },
{ id: 4, display_time: '04:00', name: 'test4' },
{ id: 5, display_time: '01:00', name: 'test5' }
Desired result:
{
"01:00": [
{ id: 1, display_time: '01:00', name: 'test1' },
{ id: 3, display_time: '01:00', name: 'test3' },
{ id: 5, display_time: '01:00', name: 'test5' }
],
"03:00": [
{ id: 2, display_time: '03:00', name: 'test2' }
],
"04:00": [
{ id: 4, display_time: '04:00', name: 'test4' }
],
}
Basically it groups the documents based on the display_time field and returns it in that format. Is this possible with Mongo?
You can try somthing like this:
db.collection.aggregate(
[
{ $group : { _id : "$display_time", test: { $push: "$$ROOT" } } }
]
)
{
"_id" : "01:00",
"test" :
[
{ id: 1, display_time: '01:00', name: 'test1' },
{ id: 3, display_time: '01:00', name: 'test3' }
]
}
{
"_id" : "04:00",
"test" :
[
{ id: 4, display_time: '04:00', name: 'test4' }
]
}
For moar information http://docs.mongodb.org/manual/reference/operator/aggregation/group/
Related
I have a collection of motorcycles in my MongoDB database. Each motorcycle (collection) has an array of units and each unit has an array of parts with the name and SKU number. I'm trying to fetch only those units which contain given SKU number (in this example PT00002).
I tried to use aggregation, project and filter, but every time I get empty array of units:
let responseData = await Model.aggregate([
{
$match: {
'units.parts.SKU': sku,
}
},
{
$project: {
'units': {
$filter: {
input: '$units',
as: 'unit',
cond: {
$eq: [
'$$unit.parts.SKU', sku
]
}
}
}
}
}
]);
Documents look like this:
{
_id: ObjectId('6351183b841ef5ca0e090482'),
name: 'Yamaha Tenere 2022',
units: [
{
name: 'Front wheel',
parts: [
{
SKU: 'PT00001',
name: 'Bolt m7'
},
{
SKU: 'PT00002',
name: 'Oring'
},
]
},
{
name: 'Rear wheel',
parts: [
{
SKU: 'PT00003',
name: 'Bolt m7'
},
{
SKU: 'PT00002',
name: 'Oring'
},
]
}
]
}
and
{
_id: ObjectId('6351183b841ef5ca0e090483'),
name: 'Yamaha Tenere 2021',
units: [
{
name: 'Exhaust system',
parts: [
{
SKU: 'PT00012',
name: 'Screw torx'
},
{
SKU: 'PT00002',
name: 'Oring'
},
]
},
{
name: 'Suspension',
parts: [
{
SKU: 'GGG02',
name: 'Front fork'
},
{
SKU: 'GGG02',
name: 'Rear fork'
},
]
}
]
}
Expected output (for SKU PT00002 ) should be like this (no "Suspension unit" in array):
{
_id: ObjectId('6351183b841ef5ca0e090482'),
name: 'Yamaha Tenere 2022',
units: [
{
name: 'Front wheel',
parts: [
{
SKU: 'PT00001',
name: 'Bolt m7'
},
{
SKU: 'PT00002',
name: 'Oring'
},
]
},
{
name: 'Rear wheel',
parts: [
{
SKU: 'PT00003',
name: 'Bolt m7'
},
{
SKU: 'PT00002',
name: 'Oring'
},
]
}
]
},
{
_id: ObjectId('6351183b841ef5ca0e090483'),
name: 'Yamaha Tenere 2021',
units: [
{
name: 'Exhaust system',
parts: [
{
SKU: 'PT00012',
name: 'Screw torx'
},
{
SKU: 'PT00002',
name: 'Oring'
},
]
},
]
}
Looks like I had small error with my aggregate query. The one below works:
let responseData = await Model.aggregate([
{
$match: {
'units.parts.SKU': sku,
}
},
{
$project: {
'name': 1,
'year': 1,
'code': 1,
'category': 1,
'units': {
$filter: {
input: '$units',
as: 'unit',
cond: {
$in: [
sku, '$$unit.parts.SKU'
],
}
}
}
}
}
]);
I'm getting "MongoServerError: Invalid $project :: caused by :: Unrecognized parameter to $cond: $if" for this code. Whats wrong with it and how do i fix it ?
{
$project: {
_id: 1,
date: 1,
slot: 1,
franchiseId: 1,
status: 1,
order: {
$cond: {
$if: { $gt: ['$carspa', null] },
$then: {
price: '$carspa.price',
mode: '$carspa.mode',
address: '$carspa.address',
addOn: '$carspa.addOn',
service: '$carspa_service.name'
},
$else: {
$if: { $gt: ['$mechanical', null] },
$then: {
price: '$mechanical.price',
mode: '$mechanical.mode',
address: '$mechanical.address',
addOn: '$mechanical.addOn',
service: '$mechanical_service.name'
},
$else: {
price: '$quickhelp.price',
mode: '$quickhelp.mode',
address: '$quickhelp.address',
addOn: '$quickhelp.addOn',
service: '$quickhelp_service.name'
}
},
},
},
}
}
Try
{
$project: {
_id: 1,
date: 1,
slot: 1,
franchiseId: 1,
status: 1,
order: {
$cond: {
if: { $gt: ['$carspa', null] },
then: {
price: '$carspa.price',
mode: '$carspa.mode',
address: '$carspa.address',
addOn: '$carspa.addOn',
service: '$carspa_service.name'
},
else: {
if: { $gt: ['$mechanical', null] },
then: {
price: '$mechanical.price',
mode: '$mechanical.mode',
address: '$mechanical.address',
addOn: '$mechanical.addOn',
service: '$mechanical_service.name'
},
else: {
price: '$quickhelp.price',
mode: '$quickhelp.mode',
address: '$quickhelp.address',
addOn: '$quickhelp.addOn',
service: '$quickhelp_service.name'
}
},
},
},
}
}
I have a collection A with data like
{
id: 2,
name: "test"
},
{
id: 4,
name: "test4"
}
and I have a second collection B with data like:
{
id: 444,
name: "a",
colA_id: 2
},
{
id: 555,
name: "b",
colA_id: 2
},
{
id: 555,
name: "c",
colA_id: 10
},
After I aggregate both collections, I want an output like:
{
id: 2,
name: "test",
list_of_b: {
{
id: 444,
name: "a",
colA_id: 2
},
{
id: 555,
name: "b",
colA_id: 2
},
}
}
I tried to merge them, but every time I only get one record of the database.
Its just a simple look up
The DB is
db={
"collA": [
{
id: 2,
name: "test"
},
{
id: 4,
name: "test4"
}
],
"collB": [
{
id: 444,
name: "a",
colA_id: 2
},
{
id: 555,
name: "b",
colA_id: 2
},
{
id: 555,
name: "c",
colA_id: 10
}
]
}
And the aggregation is
db.collA.aggregate([
{
"$lookup": {
"from": "collB",
"localField": "id",
"foreignField": "colA_id",
"as": "list_of_b"
}
}
])
Working Mongo playground
I have a collection of documents that look like this:
[
{ group_id: 1, value: 'foo' },
{ group_id: 1, value: 'bar' },
{ group_id: 1, value: 'bar' },
{ group_id: 1, value: 'bar' },
{ group_id: 2, value: 'bar' },
{ group_id: 2, value: 'foo' },
{ group_id: 2, value: 'foo' },
{ group_id: 2, value: 'foo' }
]
For each group_id I want to return the value that occurs the most. So my output should look something like this...
[
{ group_id: 1, maxValue: 'bar', maxValueCount: 3 },
{ group_id: 2, maxValue: 'foo', maxValueCount: 3 }
]
How would you do this using the Mongoose aggregate function?
Update:
This is as far as I've gotten, I just need to return the value of the maximum count now...
const records = await Record.aggregate([
{
$group: {
_id: {
id: '$group_id',
value: '$value'
},
count: { $sum: 1 }
}
}
])
You may achieve your goal by
grouping by group_id/value (and counting the occurrences of value)
then grouping by group_id and pushing all the found values within with their count
finally keeping from the array the value having the max count
data=[
{ group_id: 1, value: 'foo' },
{ group_id: 1, value: 'bar' },
{ group_id: 1, value: 'bar' },
{ group_id: 1, value: 'bar' },
{ group_id: 1, value: 'bar' },//one more added
{ group_id: 2, value: 'bar' },
{ group_id: 2, value: 'foo' },
{ group_id: 2, value: 'foo' },
{ group_id: 2, value: 'foo' }
]
db.products.remove({})
db.products.insert(data)
const stages = [
{
$group: {
_id: {
group_id: '$group_id',
value: '$value'
},
n: { $sum: 1 }
}
},
{
$group: {
_id: '$_id.group_id',
values: {
$push: {
value: '$_id.value',
n: '$n'
}
}
}
},
{
$project: {
group_id:1,
best: {
$reduce: {
input: '$values',
initialValue: { n: 0, value: ''},
in: {
$cond: [
{
$lt: ['$$value.n', '$$this.n']
},
'$$this',
'$$value'
]
}
}
}
},
},
{
$project: {
group_id: 1,
value: '$best.value',
maxValue: '$best.n'
}
}
]
printjson(db.products.aggregate(stages).toArray())
playground
i'm fairly new to MongoDB and Mongoose and i'm working on a bug here. Apparently $sum is not working with a field whose type is SchemaTypes.Double. This double type is available thanks to a package called mongoose-double.
I don't know if MongoDB doesn't support Double so that's why we have this package, or is this because of MongoDB Version (it's on 3.6 AFAIK). But anyways, here's the code:
Schedule.aggregate([{
$match: findTerm
},
{
$facet: {
totalizer: [{
$group: {
_id: '$store',
totalServices: {
$sum: 1
},
totalValue: {
$sum: '$value'
},
totalComission: {
$sum: '$comissionValue'
}
}
}
],
data: [{
$project: {
'employee.name': 1,
'customer.name': 1,
'service.name': 1,
'info.channel': 1,
value: 1,
scheduleDate: 1,
scheduleStart: 1,
scheduleEnd: 1,
comissionValue: 1,
status: 1,
paymentMethod: 1
}
},
{
$sort: sort
},
{
$skip: req.body.limit * req.body.page
},
{
$limit: req.body.limit
}
]
}
}
]).exec((e, response) => {
if (e) {
// handle error
}
res.status(200).send(response[0]);
});
This findTerm is sent by the frontend app and has this format:
{ store: '5b16cceb56a44e2f6cd0324b',
status: { '$in': [ 0, 1, 2, 3 ] },
paymentMethod: { '$in': [ 0, 1, 2, 3, 4, 5 ] },
'info.channel': { '$in': [ 'app', 'admin' ] },
scheduleStart: { '$gte': '2019-11-01 00:00' },
scheduleEnd: { '$lte': '2020-03-31 23:59' }
}
My comissionValue field is in the root of my Schedule Schema:
comissionValue: {
type: SchemaTypes.Double,
default: 0
},
But my result is the following, as shown in my console.log in the frontend
As you can see my totalComission inside my totalizer is null, but my first object inside data has a comissionValue of 0.6.
How can i kno what's wrong here? I've tried different combinations of $facet, filtering only Schedules that has a comissionValue not equal 0 and null, but i only got a result of 0 for totalComission.
EDIT
Here's some sample data:
A Schedule object:
customer: {
id: "5e41a7ba11340930742aa689",
name: "renan lima",
phone: "+5511999999999",
email: "sampleemail#gmail.com",
doc: "00000000000",
avatar: null
},
employee: {
id: "5b16cebd29bcf613f02b6fb4",
name: "Anderson Zanardi",
avatar: "anderson.jpg"
},
service: {
noValue: false,
filters: [],
id: "5b637acd634e14086c9a3aea",
name: "Barba Masculina"
},
info: {
channel: "app",
id: "5e41a7ba11340930742aa689",
name: "renan lima"
},
comissionType: null,
comissionValue: 0,
paymentMethod: 0,
_id: "5e41a7c011340930742aa68a",
store: "5b16cceb56a44e2f6cd0324b",
scheduleDate: "2020-03-16T15:00:00.000Z",
scheduleStart: "2020-03-16 09:00",
scheduleEnd: "2020-03-16 09:30",
status: 2,
value: 30,
color: "blue",
logStatus: [],
__v: 0,
created: "2020-02-10T18:58:08.845Z",
updated: "2020-02-10T18:58:08.845Z"
My response received for the Schedule.aggregate:
{
"totalizer": [{
"_id": null,
"storesCount": [{
"store": "5b16cceb56a44e2f6cd0324b",
"count": 12
}],
"totalValue": 410.5,
"totalServices": 12,
"totalComission": 75
}],
"data": [{
"_id": "5e5d04dcb4a2f42204598ebf",
"service": {
"name": "Outros"
},
"info": {
"channel": "admin"
},
"comissionValue": 0,
"paymentMethod": 0,
"customer": {
"name": "teste"
},
"employee": {
"name": "Gabriel Barreto"
},
"scheduleDate": "2020-03-02T13:06:00.000Z",
"scheduleStart": "2020-03-02 10:06",
"scheduleEnd": "2020-03-02 10:36",
"status": 0,
"value": null
}]
}
Here the comission is 75, i don't know if it's because of the scheduleStart and scheduleDate in the findTerm on my $match that this time is starting at 2020-03-01 and ends at 2020-03-31 and in that range there's 3 schedules with 25 of comission.
Maybe my pagination is making it return null? Since i need it to sum all my comission for a given start/end range, even if in a certain page it doesn't have a comission.
EDIT 2
I added a sample data in Mongo Playground, the schedule array in the configuration column matchs the query used in the $match property on the query column.
Here's the link: https://mongoplayground.net/p/nmyAsY4g7LS