MongoDB Aggregate by date on many fields - mongodb

I have the following schema (abbreviated!)
{
date: {
_id: false,
day: { type: Number },
month: { type: Number },
year: { type: Number },
},
flightTimes: {
_id: false,
totalTime: Number,
...
},
loggedTimes: {
_id: false,
P1: {
type: Number,
},
P2: {
type: Number,
},
...
},
customFields: [
{
_id: false,
value: String,
type: {
type: String,
enum: ["text", "number", "time", "yesno", "paragraph"],
required: true,
},
},
],
}
I am trying to produce the following output using aggregation, but struggling.
{
total: [10,20,30],
P1: [45, 55, 12],
P2: [0, 12, 15],
}
Where the 1st item in the total array is the sum of any documents where Month = 1 (January), the second is the sum for February etc.
Any suggestions on how to proceed? For bonus points: the array should have a 0 if no documents match the current month. For more bonus points: how could I create an additional array for the sum of values in each custom field where type = time.
Ultimately looking to produce a graph with Charts.JS which shows the sum per month for each category of time.

Related

mongoose query within a time frame

I have this mongoose schema, it stores user's salary
const salarySchema = new mongoose.Schema(
{
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true,
trim: true,
},
amount: {
type: String,
required: true,
trim: true,
},
paymentType: {
type: String,
required: true,
enum: [PAYMENT_TYPE],
default: PAYMENT_TYPE.MONTHLY,
},
startDate: {
type: Date,
required: true,
},
endDate: {
type: Date,
required: false,
default: null,
},
}
);
If I have a time frame given I want to query all salaries applicable to that time frame using startDate and endDate attributes.
This is the graphical representation of all possible combinations, The given time frame is April to March,
APRIL MARCH
| —------------FILTER--------------- |
| —-------- |
| —-------------------------------------------------- |
| —--------------------- |
| —------------------------- |
| —----- |
| —------- |
The final two should not be included in the result because it's not applicable to the given time frame.
And the there can be salary the endDate can be null and it represent the user active salary,
Basicaly I am storing the salary history also using start,end date.
{
$or: [
// DB record range is completely within provided range
{
startDate: { $gte: start, $lte: end },
endDate: { $gte: start, $lte: end },
},
// Provided range is completely within DB record range
{
startDate: { $lte: start },
$or: [{ endDate: { $gte: end } }, { endDate: null }],
},
// Left intersection
{
startDate: { $lte: start },
endDate: { $lte: end, $gte: start },
},
// Right intersection
{
startDate: { $gte: start, $lte: end },
$or: [{ endDate: { $gte: end } }, { endDate: null }],
},
],
},

Find an item within an array in a collection with the highest score overall and return with that score

Schema:
const restaurantSchema = new mongoose.Schema({
address: {
building: String,
coord: [Number, Number],
street: String,
zipcode: String,
},
borough: String,
cuisine: String,
grades: [{ date: Date, grade: String, score: Number }],
name: String,
restaurant_id: String,
});
I am trying to find a restaurant with the highest grades.score and return what that score is.
What I've tried doing:
Restaurant.find({}, { "grades.score": 1, _id: 0 }).sort({ "grades.score": -1 }).limit(1).exec((err, docs) => {
if (err) {
console.log(err);
} else {
console.log(docs);
}
});
What it returns:
[
{
grades: [ [Object], [Object], [Object], [Object], [Object], [Object] ]
}
]
What I want:
[
{
score: 131
}
]
Query
you can use the $max aggregate operator that works as accumulator and on arrays like here
this is for the local(inside the document array) max-score if you want for the global(in all the collection) you can sort desc by max-score
and $limit 1
Playmongo
aggregate(
[{"$project": {"_id": 0, "max-score": {"$max": "$grades.score"}}}])

Algolia retrieve results by multiple facets

First of all, I am using Algolia JavaScript API Client V3 (Deprecated)
I have the following records
{
category: SEDAN,
manufacturer: Volkswagen,
id: '123'
},
{
category: COUPE,
manufacturer: Renault,
id: '234'
},
{
category: SEDAN,
manufacturer: Fiat,
id: '345'
},
{
category: COUPE,
manufacturer: Peugeot,
id: '456'
},
{
category: SUV,
manufacturer: Volkswagen,
id: '567'
}
I want to query Algolia and get something similar to the following json
{
categories: {
SEDAN: {
count: 2
items: [{
Volkswagen: {
count 1,
items: [{
id: '123'
}]
}
},
{
Fiat: {
count 1,
items: [{
id: '345'
}]
}
}]
},
COUPE: {
count: 2
items: [{
Renault: {
count 1,
items: [{
id: '234'
}]
}
},
{
Peugeot: {
count 1,
items: [{
id: '456'
}]
}
}]
},
SUV: {
count: 1,
items: [{
Volkswagen: {
count 1,
items: [{
id: '567'
}]
}
}]
}
}
}
I have been trying to query Algolia
index
.search({
query: '',
facets: ['category', 'manufacturer'],
attributesToRetrieve: []
})
.then((result) => {
console.log(result.facets);
});
But I am not sure if it is possible to combine the facets
facets added to a query doesn't work that way. It will simply return the record count for each facet value, not the actual records (https://www.algolia.com/doc/api-reference/api-parameters/facets/)
You can create filters around facets and use those to display results by facet value, but there isn't a way to build a single response JSON that is already grouped by facets like you show above. https://www.algolia.com/doc/api-reference/api-parameters/filters/

Mongodb - multi-stages query - calculate, compare and return results

I have mongodb documents as follows:
[
{
_id: '5e839a223552e431fxd3c979',
dateTime: '2020-03-31T19:29:41.259Z',
regionId: 'eg',
transId: 'serviceFee',
revenue: 30,
details: {
serviceId: 'serv_a'
}
},
{
_id: '5e839a223559e431fxd3c979',
dateTime: '2020-03-31T19:29:42.259Z',
regionId: 'eg',
transId: 'serviceFee',
revenue: 10,
details: {
serviceId: 'serv_b'
}
},
{
_id: '5e839a223552ed82fxd3c979',
dateTime: '2020-03-31T19:29:43.259Z',
regionId: 'eg',
transId: 'serviceFee',
revenue: 15,
details: {
serviceId: 'serv_a'
}
},
{
_id: '5e8902223552e431fxd3c979',
dateTime: '2020-03-31T19:29:44.259Z',
regionId: 'eg',
transId: 'serviceFee',
revenue: 55,
details: {
serviceId: 'serv_a'
}
},
{
_id: '5e839a223552e43jjid3c979',
dateTime: '2020-03-31T19:29:45.259Z',
regionId: 'eg',
transId: 'serviceFee',
revenue: 7,
details: {
serviceId: 'serv_b'
}
},
{
_id: '5e839a223552e431fx0c3979',
dateTime: '2020-03-31T19:29:46.259Z',
regionId: 'eg',
transId: 'serviceFee',
revenue: 120,
details: {
serviceId: 'serv_c'
}
},
{
_id: '5e839a22ggt2e431fxd3c979',
dateTime: '2020-03-31T19:29:47.259Z',
regionId: 'us',
transId: 'serviceFee',
revenue: 500,
details: {
serviceId: 'serv_a'
}
},
]
looking to the documents we can say:
there are two regions, as per regionId: [eg,us],
there are three services, as per serviceId: [serv_a, serv_b, serv_c],
each document shows the revenue of a particular service in a particular region.
now here is what I want:
I want to know the best performing service (with highest total revenue) in a particular region (ex. eg).
I also want to know the worst performing service in the same region.
So expected result should be:
Best performing service in region 'eg' is: serv_c
Worst performing service in the region is: serv_b
So from my basic understanding, I think I need to find a way to calculate the total revenues of each service in the region (ex. eg) and then compare between the totals to decide which is the highest and which is the lowest! I think I should use mongodb aggregate function, but I don't know how to use it in this case.
I think I figured this out.
Here is an example in javascript showing how did I do it:
var bestPerformingService = 'ph_besterviceId'; // <-- value placeholder
var worstPerformingService = 'ph_worstServiceId' // <-- value placeholder
var trId = 'serviceFee'; // <-- selected transId
var regId = 'eg'; // <-- selected regionId
var resultArray = await records_revenues_model.aggregate([
{
$match: {
regionId: regId,
transId: trId,
}
},
{
$group: {
_id: '$details.serviceId',
totalRevenues: { $sum: '$revenue' },
}
},
{
$sort: { totalRevenues: -1 }
},
]).exec() || [];
try {
bestPerformingService = resultArray[0]._id;
worstPerformingService = resultArray[resultArray.length - 1]._id;
} catch (error) {
methods.log(`safed error : ${error}`);
}
console.log(`best performing service: ${bestPerformingService}`);
console.log(`worst performing service: ${worstPerformingService}`);
// expected outcome:
// best performing service: serv_c
// worst performing service: serv_b
I hope this can be useful to someone someday.

How to make a comma separated list of array values?

I have the following documents
User Schema:
var UserSchema = new Schema({
name: String,
email: { type: String, lowercase: true },
offers: [],
anyCountry: {type: Boolean, default: false},
city: String,
});
Tags Schema
var TagSchema = new Schema({
text: String,
dateCreated: { type: Date, default: Date.now}
});
I am aggregating it this way:
User.aggregate(
{$match: {
$or: [
{'isBlocked': false},
{'isBlocked': {$exists: false}}
]}},
{ $project: {"offers": 1, _id: 0, city: 1, name: 1}},
{ $unwind: "$offers" },
{
$match: {
$and: [
{'offers': { $not: { $size: 0} }},
{'offers.type': type}
]
}
},
{ $sort: {"offers.dateCreated": -1} },
function (err, result) {
if (!err) {
return res.json({status: 'success', data: result});
} else {
return res.send(err);
}
}
)
The output is ok, but it contains tags as array. What I need is:
to have array values assigned to a computed field "offers.tagsList" as a coma separated sting {offers.tagsList = 'tag1, tag2, tag3, ...'}.
check if filed offers.anyCountry doesn't exists and add it to the output with value false.
Thanks!