In the Numeric Model for storing monetary values, the MongoDB docs state:
From the mongo shell decimal values are assigned and queried using the NumberDecimal() constructor.
Similarly, when using the Morphia Java library, BigDecimals are automatically inserted as BigDecimals.
I'm querying Mongo in Node with Mongoose and attempting to extract the numeric value of a field stored as a NumberDecimal. However, the value is oddly wrapped in query results and I'm not sure how to extract it through Mongo or Mongoose:
[
{
"openValue":{
"$numberDecimal":"119.931"
},
"timestamp":"2017-01-20T10:30:00.000Z"
},
{
"openValue":{
"$numberDecimal":"119.965"
},
"timestamp":"2017-01-20T10:31:00.000Z"
}
]
One post I read stated using parseFloat() in my application code will perform what I desire, however it's not efficient to iterate through the result to perform this transformation. Avoiding iterating and transforming would mean running the function on the NumberDecimals whenever I want their value every time, which would be annoying.
Is there a way I can use Mongo or Mongoose to convert the above JSON query-result into what's below?
[
{
"openValue": 119.931,
"timestamp":"2017-01-20T10:30:00.000Z"
},
{
"openValue": 119.965,
"timestamp":"2017-01-20T10:31:00.000Z"
},
{
"openValue": 119.975,
"timestamp":"2017-01-20T10:32:00.000Z"
}
]
I tried selecting the field as ...openValue.$numberDecimal, but this didn't work. Thank you!
Edit: Here's my Mongoose schema:
var EquityHistoryModel = new Schema({
_id: {
equityIdentifier: { type: {
exchange: { type: String, index: true },
symbol: { type: String, index: true }
}, index: true },
instant: { type: Date, index: true },
durationMinutes: { type: Number }
},
open: { type: mongoose.Schema.Types.Decimal },
high: { type: mongoose.Schema.Types.Decimal },
low: { type: mongoose.Schema.Types.Decimal },
close: { type: mongoose.Schema.Types.Decimal },
volume: { type: Number },
isDividendAdjusted: { type: Boolean },
isSplitAdjusted: { type: Boolean }
}, { collection: 'equityHistories', versionKey: false });
Here's the Mongoose query for the first JSON result above:
mongo.EquityHistoryModel.aggregate([
{
"$match":{
"_id.equityIdentifier.exchange":passed_in,
"_id.equityIdentifier.symbol":passed_in,
"_id.instant":passed_in
}
},
{
"$project":{
"_id":0,
"openValue":"$open",
"timestamp":"$_id.instant"
}
}
],
You can also overwrite the toJSON method:
// Never return '__v' field and return the 'price' as String in the JSON representation
// Note that this doesn't effect `toObject`
EquityHistoryModel.set('toJSON', {
getters: true,
transform: (doc, ret) => {
if (ret.price) {
ret.price = ret.price.toString();
}
delete ret.__v;
return ret;
},
});
Contrary to what I expected, it seems the values are automatically extracted. Stringifying the result automatically wraps the value in the NumberDecimal. See the code with the output manually placed below:
console.log(docs[0].openValue);
119.800
console.log(JSON.stringify(docs[0].openValue]);
{"$numberDecimal":"119.800"}
Also, I struggled with sending query results, due to res.json or res.send using stringify. Instead I wrote a replace function and set the property in Node.
Related
I am trying to edit a discord bot made in python (I stored data initially in python) and transferring it to javascript (node.js) and can't feature out while connecting to my old db why findOne giving me null while providing proper discord id.
Without anything inside
Code
anifarm.findOne();
Output
{
_id: 707876147324518400,
farmed: 17,
ordered: 5,
pimage: 'https://media.tenor.com/images/e830217a5d9926788ef25119955edc7f/tenor.gif',
pstatus: 'I want you to be happy. I want you to laugh a lot. I don’t know what exactly I’ll be able to do for you, but I’ll always be by your side.',
avg: 184,
speed: 2,
badges: [
'https://cdn.discordapp.com/attachments/856137319149207563/856137435696332800/Black-and-Yellow-Gaming-Badge--unscreen.gif',
'https://cdn.discordapp.com/attachments/856137319149207563/862219383866523688/Front-removebg-preview.png', 'https://cdn.discordapp.com/attachments/856137319149207563/862240758768599100/download-removebg-preview.png'
],
setBadges: 'https://cdn.discordapp.com/attachments/856137319149207563/862240758768599100/download-removebg-preview.png'
}
With id inside
Code
anifarm.findOne({
_id: 707876147324518400
});
Output
null
anifarm in the schema.
Decleared Schema
module.exports = mongoose.model('anifarm', new mongoose.Schema({
_id: Number,
farmed: {
type: Number,
default: 0
},
ordered: {
type: Number,
default: 0
},
pimage: {
type: String,
default: ""
},
pstatus: {
type: String,
default: ""
},
avg: {
type: Number,
default: 200
},
speed: {
type: Number,
default: 2
},
badges: {
type: Array,
default: []
},
setBadges: {
type: String,
default: ""
}
},
{
collection: 'anifarm',
versionKey: false
})
);
I cannot figure out what am I doing wrong. This problem also happens with .find()
Nothing inside find fetches everything by if I provide id it sends a empty array.
A Little help would be appreciated
For you problem use mongoose-long that should fix your problem.
This library will handle all long type data for mongoose since mongoose cannot handle long type data
you can't pass an id as a number, you will have to use ObjectId to convert the id to an instanceof ObjectId
Change your code like this
anifarm.findOne({
_id: mongoose.Types.ObjectId(707876147324518400);
});
If you're querying by _id, use findById() instead.
anifarm.findById("707876147324518400")
Official docs here
I am new to MangoDB and Node JS. I have always worked on SQL databases and I do not know the syntax of MongoDB well. I wanna try to filter the array that I receive from a MongoDB database. I know that JavaScript has a .filter() function to filter just the results that contain a string. Is it best practice to get all the objects from MongoDB and filter in Node or do I let MongoDB do the filtering?
My Node.JS project is a back-end project using Node.JS and Express to do CRUD operations on a MongoDB database. In the request I send a parameter called 'equalTo' that contains the value that should be filtered on.
var router = express.Router();
var Plot = require("./models/plot");
...
router.get("/plots", (req, res) => {
let query = "" + req.query.equalTo;
Plot.find((err, plots) => {
if (err) {
res.send(err);
}
res.json(plots);
});
});
The filtering should be an OR filter where all results where either the name or the cropName should CONTAIN the value of the string. If it is possible I would also like the comparison to ignore uppercase's. Here is a schema for the Plot object:
const plotSchema = mongoose.Schema({
area: {
type: String,
required: true
},
comments: {
type: String,
required: true
},
cropGroupName: {
type: String,
required: true
},
cropName: {
type: String,
required: true
},
name: {
type: String,
required: true
},
plotId: {
type: Number,
required: true
},
coords: {
type: [],
required: true
},
}, {
collection: "plots"
});
The format is the following:
Plot.find({$or:[{name: "anyname"},{cropName:"othername"}]})
For further information you can read here https://docs.mongodb.com/manual/reference/operator/query/or/
You may replace the strings above in your case with equalTo.
I'm using read-excel-file library to load an excel file with the next structure,
The problem is when the code read this value,
The returned value is the next,
And here is my code,
let ExcelLoader = window.readXlsxFile;
let input = document.getElementById('input-file');
//Task Created by Responsible person Status Created on Closed on Deadline Tags
const schema = {
'Task': {
prop: 'task',
type: String
},
'Created by': {
prop: 'createdBy',
type: String
},
'Responsible person': {
prop: 'responsiblePerson',
type: String
},
'Status': {
prop: 'status',
type: String
},
'Created on': {
prop: 'createdOn',
type: String
},
'Closed on': {
prop: 'closedOn',
type: String
},
'Deadline': {
prop: 'deadline',
type: String
},
'Tags': {
prop: 'tags',
type: String
}
};
input.addEventListener('change', () => {
console.log('The Change listener actioned!');
ExcelLoader(input.files[0], { schema })
.then(({rows, errors}) => {
console.log(rows);
//console.log(errors);
})
});
I'm doing this little excersice client side and I was trying to test other types of values in the schema paramether also I was looking for a converter from decimal to date and decimal to datetime with no success,
Can anybody knows what's going on?
I'm open to use other client side libraries...
As Tim Williams says, in Excel Dates and Times are stored as numbers.
I'm currently using AngularJS, and the backend is using NodeJS and Express. I use Mongoose to access the database. I'm trying to figure how to add attributes to nested objects and I can't for the life of me find out how to do it anywhere on the web.
My Schema looks like this:
{
id: {
type: String
},
involved: {
type: String
},
lastMsgRead: Object
}
lastMsgRead will look something like this:
{
user1: "somestringblahblah",
user2: "someotherstring",
}
and so on.
My question is, how would I update lastMsgRead with Mongoose to add another attribute to it, such as adding user3 so it now looks like:
{
user1: "somestringblahblah",
user2: "someotherstring",
user3: "anotherstring"
}
The entire document would like this after the update:
{
id: "string",
involved: "string",
lastMsgRead: {
user1: "somestringblahblah",
user2: "someotherstring",
user3: "anotherstring"
}
}
Edit:
After I add the attribute, how would I then update it in the future?
You can use .dot notation to update in nested object
db.collection.update(
{ },
{ "$set": { "lastMsgRead.user3": "anotherstring" } }
)
in mongoose 5.1.0 and up you can approach this with the MongooseMap.
You can define it in the schema as followed:
{
id: {
type: String
},
involved: {
type: String
},
lastMsgRead: {
type: Map,
of: String
}
}
you can then add a new value by .set(key, value)
myDoc.lastMsgRead.set(key, value)
and get a value by .get(key)
myDoc.lastMsgRead.get(key)
I have the following Schema:
Dates.attachSchema(new SimpleSchema({
description: {
type: String,
label: "Description",
max: 50
},
start: {
type: Date,
autoform: {
afFieldInput: {
type: "bootstrap-datepicker"
}
}
},
end: {
type: Date,
autoform: {
afFieldInput: {
type: "bootstrap-datepicker"
}
}
}
}));
How can I validate that the end date is not before start? I am using MomentJS to handle date types, however my main problem is how I can access other attributes in the custom function.
For instance:
end: {
type: Date,
autoform: {
afFieldInput: {
type: "bootstrap-datepicker"
}
},
custom: function() {
if (moment(this.value).isBefore(start)) return "badDate";
}
}
How can I access start?
Furthermore, how can I validate if the start + end date combination is unique, meaning there is no document saved in my database which has the exact same start and end date?
For the inter-field communication, you can do:
end: {
type: Date,
autoform: {
afFieldInput: {
type: "bootstrap-datepicker"
}
},
custom: function() {
// get a reference to the fields
var start = this.field('start');
var end = this;
// Make sure the fields are set so that .value is not undefined
if (start.isSet && end.isSet) {
if (moment(end.value).isBefore(start.value)) return "badDate";
}
}
}
You should of course declare the badDate error first
SimpleSchema.messages({
badDate: 'End date must be after the start date.',
notDateCombinationUnique: 'The start/end date combination must be unique'
})
Regarding the uniqueness, first of all simple schema itself does not provide uniqueness check. You should add aldeed:collection2 for that.
Furthermore, collection2 is capable of checking only a single field uniqueness. To accomplish compound indexes, you should use the ensureIndex syntax
Dates._ensureIndex( { start: 1, end: 1 }, { unique: true } )
Even after this, you will not be able to see the error from this compound index on your form because autoform needs to be aware that such error is existing.
AutoForm.hooks({
NewDatesForm: { // Use whatever name you have given your form
before: {
method: function(doc) {
var form = this;
// clear the error that gets added on the previous error so the form can proceed the second time
form.removeStickyValidationError('start');
return doc;
}
},
onSuccess: function(operation, result, template) {
if (result) {
// do whatever you want if the form submission is successful;
}
},
onError: function(operation, error) {
var form = this;
if (error) {
if (error.reason && error.reason.indexOf('duplicate key error')) {
// We add this error to the first field so it shows up there
form.addStickyValidationError('start', 'notDateCombinationUnique'); // of course you have added this message to your definition earlier on
AutoForm.validateField(form.formId, 'start');
}
}
}
}
});