How to make query for 3 level nested object in mongodb - node.js

I am trying to query and filter objects by comparing specific fields inside 3rd level of my objects. I am not sure how to use filter with $lte or $gte in the third level. For example, in my object below I wanted to filter documents whose delivery time (delivery_rule -> time -> $lte: max) but I can't get it using this query:
if (filters.time) {
query = {
...query,
"delivery_rule.time.max": { $lte: filters.time }
};
}
and my schema is :
var VendorSchema = new mongoose.Schema({
...,
delivery_rule: {
...,
time: {
min: {
type: Number,
default: 0
},
mid: {
type: Number,
default: 0
},
max: {
type: Number,
default: 0
}
},
});
module.exports = mongoose.model("Vendor", VendorSchema);
When I run my query using filters.time = 30 in the shell it returns me [] objects, but I have 5 objects with time 60.

The one that I did works well. I got a little confusion in testing))

Related

Mongoose split documents to 2 groups with counters

I have a model of the following schema:
const mongoose = require('mongoose');
const livenessLogSchema = new mongoose.Schema({
probability: {
type: Number,
required: false,
},
timestamp: {
type: Date,
required: true,
}
}, {
timestamps: true
});
const LivenessLog = mongoose.model('LivenessLog', livenessLogSchema);
module.exports = LivenessLog;
I want to split all documents to 2 groups: those whose probability value is less than 0.001 and those whose value is greater than 0.001. Also, in each group, I want to count for each probabilty value - how many documents has the same value.
So basically if I had the following probabilities data: [0.00001, 0.000003, 0.000025, 0.000003, 0.9, 0.6, 0.6], I'd like to get as a result: { less: { 0.00001: 1, 0.000003: 2, 0.000025:1 }, greater: { 0.9: 1, 0.6: 2 }.
This is my current aggregate method:
const livenessProbilitiesData = await LivenessLog.aggregate([
{
$match: {
timestamp: {
$gte: moment(new Date(startDate)).tz('Asia/Jerusalem').startOf('day').toDate(),
$lte: moment(new Date(endDate)).tz('Asia/Jerusalem').endOf('day').toDate(),
}
}
},
{
$group: {
}
}
]);
Note that I use undeclared variables startDate, endDate. These are input I get to filter out unrelevant documents (by timestamp).

Find value from sub array within last 30 days using Mongoose

I am trying to locate a certain value in a sub array using Mongoose.js with MongoDB. Below is my Mongoose schema.
const foobarSchema = new mongoose.Schema({
foo: {
type: Array,
required: true
},
comments: {
type: Array,
required: false
},
createdAt: { type: Date, required: true, default: Date.now }
});
The value I am trying to get is inside foo, so in foo I always have one array at place [0] which contains an object that is like the below
{
_id
code
reason
createdAt
}
I'd like to get the value for reason for all records created in the last 30 days. I've looked around on stack overflow and haven't found anything I could piece together. Below is my existing but non working code
const older_than = moment().subtract(30, 'days').toDate();
Foobar.find({ ...idk.. req.body.reason, createdAt: { $lte: older_than }})
edit add mock document
{
foo: [{
_id: 'abc123',
code: '7a',
reason: 'failure',
createdAt: mongo time code date now
}],
comments: []
}
curent code half working
const reason = req.params.reason
const sevenAgo = moment().subtract(7, 'days').toISOString()
Foo.aggregate([
{
$match: {
"foo.createdAt": {
$gte: sevenAgo
},
"foo.reason": {
reason
}
}
},
{
$project: {
reason: {
$arrayElemAt: [
"$foo.reason",
0
]
}
}
}
])
Currently returns blank array - no query failure - which is wrong it should return at least 1 document/record as that is what is in the DB that matches
expected mock data
[
{
code: 7a,
reason: failure
}
{
code: 7a,
reason:failure
}
]

$and operator returns incorrect result in mongodb

I am trying to find one product who's id matches given id and also want to know if it's quantity will still be greater or equal to the sold quantity (after adding the sold quantity which will be sold later in the process).
const product = await Product.findOne(
{
$expr: {
$and: [
{ _id: id },
{ $gte: ["$quantity", { $sum: ["$soldQuantity", quantity] }] }
]
}
}
);
(here quantity is a constant who's value is 2 and id is an ObjectId)
what I've achieved so far is if I turn $gte into $lte I get the right result but $gte returns me false result, which should return an empty object instead if conditions did not meet.
I'm using mongoDB version 4.2
Schema has:
Schema({
quantity: {
type: Number,
required: true
},
soldQuantity: {
type: Number,
default: 0
}
})

Aggregate results from multiple Mongoose models

There are several models that are considerably different and belong to different collections, yet they have common fields that will be used when query results are aggregated together.
const BlogPost = mongoose.model('BlogPost', new mongoose.Schema({
title: String,
body: String,
promoteAtMainPage: {
type: Boolean,
default: false
},
timestamp: Date,
active: Boolean,
/ * the rest are different */
});
const Article = mongoose.model('Article', new mongoose.Schema({
title: String,
body: String,
promoteAtMainPage: {
type: Boolean,
default: false
},
timestamp: Date,
active: Boolean,
/ * the rest are different */
});
Only common fields (title, body, timestamp) are used from the result.
Additionally, if promoteAtMainPage is missing in document, this should be treated differently in these models (it defaults to true in one case and false in another).
Currently this is done with result processing:
let blogPosts = await BlogPost.aggregate([{ $match { active: true } }, { $sort: { timestamp: -1} }, { $limit: 100 } }]);
for (let blogPost of blogPosts)
blogPost.promoteAtMainPage = ('promoteAtMainPage' in blogPost)
? blogPost.promoteAtMainPage
: false;
let articles = await Article.aggregate([{ $match { active: true } }, { $sort: { timestamp: -1} }, { $limit: 100 } }]);
for (let article of articles)
article .promoteAtMainPage = ('promoteAtMainPage' in article )
? article .promoteAtMainPage
: true;
let mainPagePosts = [...blogPosts, ...articles]
.filter(post => post.promoteAtMainPage)
.sort((a, b) => b.timestamp - a.timestamp)
.slice(0, 100);
This results in requesting 200 documents instead of 100 and doing extra sort.
Is this possible in this case to aggregate the results from different collections by means of Mongoose or Mongodb only?
Can the case with missing promoteAtMainPage field be handled by Mongodb as well, or the only reasonable way to handle this is to apply a migration to all existing documents and add default promoteAtMainPage value?
Have a look at $mergeObjects from the MongoDB documentation. It allows you to combine fields from multiple collections and return a new temporary collection with documents combined from the original collections.
https://docs.mongodb.com/manual/reference/operator/aggregation/mergeObjects/
You might be able to use that to combine your BlogPost and Article collections.

How to create subdocument on mongodb dynamically

I have a mongodb database with a collection as follow:
var mongoose = require('mongoose');
var journalSchema = mongoose.Schema({
title : String,
journalid: {type:String, index: { unique: true, dropDups: true }},
articleCount : type:Number, default:1,
});
module.exports = mongoose.model('Journal', journalSchema);
Now that my database is growing, I would like to have a "articles count" field per year.
I could make an array as follow years : [{articleCount : Number}] and fill it up by accessing journal.years[X] for a specific year, where X correspond to 0 for 1997, 1 for 1998, etc..
However, my data are scrapped dynamically and I would like to have a function in my express server where articleCountis increased based on the year.
For example:
function updateYear(journalid, year, callback) {
Journal.findOneAndUpdate(
{'journalid':journalid},
{$inc : {'articleCount' : 1}}, // DO SOMETHING WITH year HERE
function() {
callback();
});
}
This does increase the article count but I don't know where to include the "year"...
What would be the fastest way of doing that, knowing that I have to fetch through quite a lot of articles (10 millions +) and I would like to be able to get/update the article count for a given year efficiently.
Hope I'm clear enough, Thanks!
Make your array a set of objects with a year and count:
journalYears: [
{
year: String, // or Number
count: Number
}
]
e.g.
journalYears: [
{ year: "2014", count: 25 },
{ year: "2015", count: 15 }
]
Then for your update:
function updateYear(journalId, year, callback) {
Journal.findOneAndUpdate(
{_id: journalId, "journalYears.year": year},
{ $inc: { "journalYears.$.count": 1 } },
callback
);
}
The index of the first match from your query is saved in $. It's then used to update that specific element in your array.

Resources