Mongoose: update Array with item-wise maximum - javascript

In Mongoose, how can I update an Array property with item-wise max, and have default null upon instantiation?
I have a Mongodb collection of timeseries where values are stored in a fixed-length array (1 item per minute, 1 document per day).
{
'series': '#1',
'values': [null, null, 1, 2, 3, -4, ... ] //24h*60min=1440 items
}
I am doing computations on ~x000 timeseries on a rather high frequency (100ms), and I want to store the maximum that each of these series met during every minute. For some reason, when I update the documents, Mongoose transforms the .values property into an object, which is more space-consuming and less efficient for my use.
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test_db');
const TestSchema = new mongoose.Schema({
series: String,
values: {type: Array, default: Array(5).fill(null), required: true},
});
const Test = mongoose.model('Test', TestSchema);
async function update({series, values}){ //called every minute or so
let updated = { };
for (let {i, v} of values) {
if (updated[`values.${i}`] && updated[`values.${i}`]<v) updated[`values.${i}`]= v;
if (!updated[`values.${i}`]) updated[`values.${i}`]=v;
};
return mongoose.connection.models.Test.updateOne(
{'series':series},
{ '$max': updated },
{ upsert: true, strict: true, new: true}
);
}
async function test_update(){
//get rid of previous tentatives
await mongoose.connection.models.Test.deleteMany({});
let s = new Test({series: '#1'});
await update({series:'#1', values:[{i:3, v:-3},{i:4, v:6},{i:2, v:4}, {i:2, v:7}]});
}
test_update();
I would want my script to return
{
"_id" : ObjectId("5cb351d9d615cd456bd6a4ed"),
"series" : "#1",
"__v" : 0,
"values" : [null, null, 7, -3, 6]
}
and not the current result below:
{
"_id" : ObjectId("5cb351d9d615cd456bd6a4ed"),
"series" : "#1",
"__v" : 0,
"values" : { //values is an Object, not an Array
"2" : 7,
"3" : -3,
"4" : 6
}
}
Thanks!

`I THINK it may be your schema
Instead of:
const TestSchema = new mongoose.Schema({
series: String,
values: {type: Array, default: Array(5).fill(null), required: true},
});
You should make values an array of numbers like this
const TestSchema = new mongoose.Schema({
series: String,
values: {type: [Number], default: Array(5).fill(null), required: true},
});

Related

How to filter MongoDB documents from a list of options (Node.js + Mongoose)?

I have a collection of products, where each one has an array of properties:
const productSchema = new mongoose.Schema({
properties: [{
name: {type: String, required: true},
value: {type: mongoose.Schema.Types.Mixed, required: true},
unit: {type: String, required: false}
}]
})
I would like to query all the products who's properties match the options I provide
for example, that's the Input:
const propertiesToFilter = {
name: ['intel cpu', 'amd cpu'],
clockSpeed: [2.6, 3.7],
generation: [10]
}
This would query all the products who's:
Name is 'intel cpu' or 'amd cpu'
Clock speed is 2.6 or 3.7
Generation is 10
I tried something like this:
const findObject: any = {}
for(const [key, value] of Object.entries(propertiesToFilter)) {
findObject['properties.' + key] = {$in: value}
}
const products = await this.productModel.find(findObject).exec()
doesn't really work
Have you checked Mongoose query constructor docs?
In your case specifically $and and $in?
https://mongoosejs.com/docs/api.html#query_Query
{ $and: [{ cpu: { $in: [‘intel’, ‘amd’] }}, { speed: { $in: [2.6, 3.7] }}, { generation: 10 }] }

Why parameter multi doesn't work in mongo request?

I try to update several items in mongo collection with a single request:
// [1, 2, 3] - numbers array.
const days = req.body.days;
const updated = await Item.update(
{shift: shiftId, day: {$in: days}},
{multi : true},
{update: {
name: 'one value for all objects witch corresponding condition',
},
function(err, docs) {
console.log(docs);
}
);
This Item schema:
const itemSchema = new Schema({
shift: {
ref: 'shift',
type: Schema.Types.ObjectId,
required: true
},
day: {
type: Number,
required: true
},
name: {
type: String
}
});
But then I call this code updating only one objects.
I had many Items with have the same shift. But every Item had a unique day and I need update all Items which contains in days array.
For example, if we have shiftId = 'abc' and days = [1, 2], I need update all Items which have shiftId = 'abc' and have day = 1 OR day = 2 both should be updated.
Why my solution have unexpected behavior and updating only one object meanwhile I set {multi : true}? How to fix it? Thank You.
multi: true is the last parameter in an update query and you have used it second parameter.
So you have to use multi: true in last parameter and need to use $set to update a field.
const updated = await Item.update(
{ "shift": shiftId, "day": { "$in": days }},
{ "$set": { "name": "one value for all objects witch corresponding condition" }},
{ "multi": true }
)

Creating a mongoose model containing array types return undefined for those in my docker container

I have a mongoose model containing 2 properties Array of String and some others:
var Model = new Schema({
_id: {type: Number, required: true, unique: true},
name: {type: String, required: true},
oneList: [String],
anotherList: [String]
});
I create the model:
var model = new Model({
_id: 1,
name: 'model',
oneList: ['a', 'b'],
anotherList: ['c', 'd']
})
but when I inspect the model all the list are undefined:
model._doc === {
_id: 1,
name: 'model',
oneList: undefined,
anotherList: undefined
}
I tried some variations:
change the model definition from [String] to [ ]
create separately the data outside the model then pass it
create the model without list data then add it to the model
Even when I create an empty model:
var model = new Model({})
model._doc.oneList === undefined
model._doc.anotherList === undefined
Context:
The problem occurs on a docker container but not on my local machine
node: v4.4.7
mongoose: v4.6.0
GitHub
I had the same issue, apparently when you have a nested array within your model, mongoose has an open issue 1335 that saves an empty array when a property references a schema. I experimented with presaves to force the property to be an empty array if the property's length is 0 or undefined.
Also be careful when specifying unique=true in the property's schema, as empty or undefined properties will violate the indexing and throw an error.
Note:
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
var barSchema = new mongoose.Schema({
baz: String
});
var fooSchema = new mongoose.Schema({
bars: [barSchema]
});
var Foo = mongoose.model('Foo', fooSchema);
var foo = new Foo();
console.log(foo); // { _id: 55256e20e3c38434687034fb, bars: [] }
foo.save(function(err, foo2) {
console.log(foo2); // { __v: 0, _id: 55256e20e3c38434687034fb, bars: [] }
foo2.bars = undefined;
foo2.save(function(err, foo3) {
console.log(foo3); // { __v: 0, _id: 55256e20e3c38434687034fb, bars: undefined }
Foo.findOne({ _id: foo3._id }, function(err, foo4) {
console.log(foo4); // { _id: 55256e20e3c38434687034fb, __v: 0, bars: [] }
mongoose.disconnect();
});
});
});
You can do like this:
var schema = new Schema({
_id: {
type: Number,
required: true,
unique: true
},
name: {
type: String,
required: true
},
oneList: [String],
anotherList: [String]
}),
Model = mongoose.model('Model', schema),
m = new Model();
m._id = 1;
m.name = 'model';
m.oneList.push('a', 'b');
m.anotherList.push('c', 'd');

Create array of array Schema using mongoose for NodeJS

I want to create a DB Schema to store the data as below
{
name : "xyz",
admin : "admin",
expense : [
jan: [{expenseObject},{expenseObject}],
feb: [[{expenseObject},{expenseObject}]
]
}
Expense Object
var expenseSchema = new Schema({
particular : String,
date : {type : Date, default: Date.now},
paid_by : String,
amount : Number
});
Can someone help me create a schema for the same.
Any suggestions for a better Schema for the same concept are welcome.
You can use Sub Docs
var parentSchema = new Schema({
name: { type: String },
admin: { type: String },
expense: [expenseSchema]
});
Or, if you need the expenseObjects to be stored in a seperate collection you can use refs, where Expense would be the name of another model
var parentSchema = new Schema({
name: { type: String },
admin: { type: String },
expense: [{ type: Schema.Types.ObjectId, ref: 'Expense' }],
});
var expenseSchema = new Schema({
particular : String,
date : {type : Date, default: Date.now},
paid_by : String,
amount : Number
});
// your schema
var mySchema = new Schema({
name : {type: String, trim: true},
admin : {type: String, trim: true},
expense: [expenseSchema]
});
--- UPDATE:
With this update now expense is an array of expenseSchema without any categorisation of month. Then if you want to get all expenses in a particular month you can simply do an aggregation like this:
db.users.aggregate(
[
// this match is for search the user
{ $match: { name: "<ADMIN NAME>"} },
// this unwind all expenses of the user selected before
{ $unwind: "$expense" },
// this project the month number with the expense
{
$project: {
expense: 1,
month: {$month: '$expense.date'}
}
},
// this search all the expenses in a particular month (of the user selected before)
{ $match: { month: 8 } },
// this is optional, it's for group the result by _id of the user
//(es {_id:.., expenses: [{}, {}, ...]}. Otherwise the result is a list of expense
{
$group: {
_id:"$month",
expenses: { $addToSet: "$expense"}
}
}
]);

Mongoose FindOneAndUpdate override my value

I was trying to set up bank account like for a Discord server and here is my problem:
I have this schema:
var ladderboardSchema = new Schema({
name: String,
victories: {type: Number, default: 0},
bank: {type: Number, default: 5000},
date: {type: Date, default: Date.now}
});
followed by this model :
var ladderboardPlayer = mongoose.model('Ladder', ladderboardSchema);
So as you can see, the basic value of my bank should be 5000, and when my function is called, I want my bank value to increase for the specified amount for this player!
function setMoney(playerName, amount){
var query = {name: playerName};
var update = {$inc: {bank:amount} };
var options = {upsert: true, new: true, setDefaultsOnInsert: true};
ladderboardPlayer.findOneAndUpdate(query, update, options, function(err, doc){
if(!err) console.error('updated!');
});
}
For example, lets say I run setMoney('John', 500);, MongoDB will return me:
{ "_id" : ObjectId("57a67d1e874dfe1eb4163888"), "name" : "John", "bank" : 500, "__v" : 0, "victories" : 0, "date" : ISODate("2016-08-07T00:13:18.192Z") }
It should return 5500 but im kinda lost since I already tried a lot of solutions and none of works even if 'new' and 'setDefaultsOnInsert' are set to true

Categories

Resources