How does the Sequelize POJO JSON.stringify magic work? - node.js

In sequelize if you do a
db.MyTable.findAll({}).function(response){
console.log(response);
}
You will see output that looks like this (depending on what your table looks like, of course):
[ { dataValues:
{ id: 1,
text: 'sdf',
complete: false,
createdAt: Thu Jan 19 2017 11:55:38 GMT-0500 (Eastern Standard Time),
updatedAt: Thu Jan 19 2017 11:55:38 GMT-0500 (Eastern Standard Time) },
_previousDataValues:
{ id: 1,
text: 'sdf',
complete: false,
createdAt: Thu Jan 19 2017 11:55:38 GMT-0500 (Eastern Standard Time),
updatedAt: Thu Jan 19 2017 11:55:38 GMT-0500 (Eastern Standard Time) },
_changed: {},
'$modelOptions':
{ timestamps: true,
instanceMethods: {},
classMethods: {},
validate: {},
freezeTableName: false,
underscored: false,
underscoredAll: false,
paranoid: false,
rejectOnEmpty: false,
whereCollection: null,
schema: null,
schemaDelimiter: '',
defaultScope: {},
scopes: [],
hooks: {},
indexes: [],
name: [Object],
omitNul: false,
sequelize: [Object],
uniqueKeys: {},
hasPrimaryKeys: true },
'$options':
{ isNewRecord: false,
'$schema': null,
'$schemaDelimiter': '',
raw: true,
attributes: [Object] },
hasPrimaryKeys: true,
__eagerlyLoadedAssociations: [],
isNewRecord: false } ]
Suffice to say, it is a big complex object with a bunch of metaData on it.
However, if you try and turn that big complicated object into a string using either:
console.log(JSON.stringify(dbTodo));
or
res.json(dbTodo);
you will get back just the information for the actual entity (the items in the dataValues property of the big complex object). For my test table here, it looks like this:
[{
"id":1,
"text":"sdf",
"complete":false,
"createdAt":"2017-01-19T16:55:38.000Z",
"updatedAt":"2017-01-19T16:55:38.000Z"
}]
Which is significantly simpler (and just what I want).
How does this magic happen? Is sequelize creating their own versions of JSON.stringify() and express' res.json?
What are the rules around when it happens and when it doesn't happen?
I've searched through the sequelize docs and haven't found a good explanation.

There are two ways this behavior could manifest.
The missing properties are non-enumerable. Properties defined by Object.defineProperty set to enumerable: false (or simply don't supply a value for the enumerable setting) are non-enumerable properties, and are ignored by JSON.stringify. You can check a property's enumerable setting in the output of Object.getOwnPropertyDescriptor(obj, propName).
The object has (or has in its prototype chain) a toJSON method:
If an object being stringified has a property named toJSON whose value is a function, then the toJSON() method customizes JSON stringification behavior: instead of the object being serialized, the value returned by the toJSON() method when called will be serialized.
If the object has or inherits a toJSON method, then the return value of that method will be the target for stringification. You can check if one exists on some object obj simply by reading the value of obj.toJSON.

Related

Sails v1.x stores null and empty values to mongodb database

Sails v1.x stores empty properties in database mongoDb, when POST api call is made with request having few properties only from actual model defined in sails model, such properties which are not available in request is saved with empty values in database collection which was not the case in sails v0.12
Since I have migrated project from sails v0.12 to sails v1.x, I am facing this issue which is leading to code breakage on api response.
I am using JOI library to validate the request properties and below is the data that is being sent to <ModelName>.create(topic).fetch()
topic = {
published: false,
closed: false,
name: 'Topic G -01',
communityId: '57d640b3-cda6-4703-8080-2ef604de2086',
activityId: '23ef04f4-699d-47c7-94ad-3fb5b1c18889',
createdByUserId: '3cc6dddd-9425-42b8-83fc-4ba3f068128d',
board: '5f7cc128c2380d00172225ee'
}
Data getting saved in database collection as below:
{
_id: ObjectId("61c9d4ec5ceca05c4b0016cb"),
published: false,
closed: false,
name: 'Topic G -01',
communityId: '57d640b3-cda6-4703-8080-2ef604de2086',
activityId: '23ef04f4-699d-47c7-94ad-3fb5b1c18889',
createdByUserId: '3cc6dddd-9425-42b8-83fc-4ba3f068128d',
board: '5f7cc128c2380d00172225ee',
createdAt: '2021-12-28T16:11:07.229Z',
updatedAt: '2021-12-28T16:11:07.229Z',
description: '',
content: null,
official: false,
locked: false,
singleResponses: false,
responseTierLimit: 0,
deleteOwnResponses: false,
showQuantResults: false,
probeQuantResponse: false,
ratingSystem: '',
responseVisibility: '',
allowParticipantQuestions: false,
qIndexVisibility: false,
disablePostReplies: false,
publishedAt: null,
questionIds: null,
updatedByUserId: '',
includedRoles: null,
}
However in sails v0.12, on the contrary it used to store as below:
{
_id: ObjectId("61c9d4ec5ceca05c4b0016cb"),
published: false,
closed: false,
name: 'Topic G -01',
communityId: '57d640b3-cda6-4703-8080-2ef604de2086',
activityId: '23ef04f4-699d-47c7-94ad-3fb5b1c18889',
createdByUserId: '3cc6dddd-9425-42b8-83fc-4ba3f068128d',
board: '5f7cc128c2380d00172225ee',
createdAt: '2021-12-28T16:11:07.229Z',
updatedAt: '2021-12-28T16:11:07.229Z',
}

Nodejs spread operator return some unexpected keys in returned object [duplicate]

This question already has answers here:
How do you turn a Mongoose document into a plain object?
(9 answers)
Closed 2 years ago.
There I have res is original object
{
time: 2020-07-26T10:39:38.135Z,
isTransfered: true,
_id: 5f1d5d6b60755e75b48770a6,
receiverAccountNumber: '12345678',
transfererAccountNumber: '11111111',
receiverName: 'Lê Công Tuyền',
transfererName: 'Nguyễn Thanh Tuấn',
amount: 1000000,
content: "test chuyefo'seajfg",
payFeeBy: 'transferer',
type: { name: 'internal', bankCode: 'TUB' },
__v: 0
}
And I got this result (called res2) is returned object using spread operator res2 = {...res} :
{
'$__': InternalCache {
strictMode: true,
selected: {},
// alot of key-value
'$setCalled': Set(0) {},
ownerDocument: undefined,
fullPath: undefined,
emitter: EventEmitter {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: 0,
[Symbol(kCapture)]: false
},
'$options': { skipId: true, isNew: false, willInit: true }
},
isNew: false,
errors: undefined,
_doc: {
time: 2020-07-26T10:39:38.135Z,
isTransfered: true,
_id: 5f1d5d6b60755e75b48770a6,
receiverAccountNumber: '12345678',
transfererAccountNumber: '11111111',
receiverName: 'Lê Công Tuyền',
transfererName: 'Nguyễn Thanh Tuấn',
amount: 1000000,
content: "test chuyefo'seajfg",
payFeeBy: 'transferer',
type: { name: 'internal', bankCode: 'TUB' },
__v: 0
},
'$locals': {},
'$op': null,
'$init': true
}
I really dont know about this behavior of spread operator, that a lot of new key-value generated and the object I want to get is in _doc key.
Code is run on Nodejs v12 (nvm use 12)
This is probably because the res here is a mongoose document which has these values.
When you are doing a mongoose query by default it returns a document object. In order to receive a plain object use lean(). If you use lean then you won't be getting these unnecessary data when using spread operator.
Schema.findOne(query).lean()
However, if you need a document object from mongoose then in this case you can try the following to get rid of other values you don't need.
let res2 = { ...res.toObject() };

Sequelize: isNewRecord is always false

I have a Sequelize model called Test with a unique field value (primary key). I'm using SQLite as the database management system.
If I use the bulkCreate() option ignoreDuplicates: true, then bulkCreate will ignore the new data if they already exist in the database. That works great, but the objects returned by bulkCreate() always have isNewRecord: false, even when a new record was inserted into the database.
Code
const items = [ {value: 'a'}, {value: 'b'} ]; // Items to be saved in database.
const results = await Test.bulkCreate(items, {
ignoreDuplicates: true // Ignore duplicate records
});
After the first execution of bulkCreate() and the database empty, the value of results:
[
Test {
dataValues: {
value: 'a',
createdAt: 2020-07-12T12:01:08.695Z,
updatedAt: 2020-07-12T12:01:08.695Z
},
_previousDataValues: {
value: 'a',
createdAt: 2020-07-12T12:01:08.695Z,
updatedAt: 2020-07-12T12:01:08.695Z
},
_changed: Set {},
_options: {
isNewRecord: true,
_schema: null,
_schemaDelimiter: '',
include: undefined
},
isNewRecord: false
},
Test {
dataValues: {
value: 'b',
createdAt: 2020-07-12T12:01:08.695Z,
updatedAt: 2020-07-12T12:01:08.695Z
},
_previousDataValues: {
value: 'b',
createdAt: 2020-07-12T12:01:08.695Z,
updatedAt: 2020-07-12T12:01:08.695Z
},
_changed: Set {},
_options: {
isNewRecord: true,
_schema: null,
_schemaDelimiter: '',
include: undefined
},
isNewRecord: false
}
]
I expected isNewRecord: true after the first execution. What am I missing?
Environment
Windows 10 Pro
NodeJS v12.16.2
sequelize: 6.3.3
sqlite3: 5.0.0
Sequelize docs
isNewRecord of Model:
ignoreDuplicates of bulkCreate():
You need to access _options.isNewRecord.
As this issue states that the isNewRecord only be set to true if the instance has not yet been persisted to the database.
In your case, because you have saved the instance (the first time or second time, it doesn't matter, you already save it) so isNewRecord is set to false.
So as I mention above, if you want to check if your instance is just saved the first time or not, access _options.isNewRecord.
The isNewRecord field is true if the Model instance does not exist in the database.
const user = new User({ ...userData });
console.log(user.isNewRecord); // true
await user.save();
console.log(user.isNewRecord); // false

Using Sequelize with ES6 Promises?

I'm using Sequelize to connect to a Postgres database. I have this code:
return Promise.resolve()
.then(() => {
console.log('checkpoint #1');
const temp = connectors.IM.create(args);
return temp;
})
.then((x) => console.log(x))
.then((args) =>{
console.log(args);
args = Array.from(args);
console.log('checkpoint #2');
const temp = connectors.IM.findAll({ where: args }).then((res) => res.map((item) => item.dataValues))
return temp;
}
)
.then(comment => {
return comment;
})
.catch((err)=>{console.log(err);});
In the first .then block at checkpoint #1, the new record is successfully added to the Postgres database. In the console.log(x) in the next then block, this gets logged to the console:
{ dataValues:
{ id: 21,
fromID: '1',
toID: '2',
msgText: 'Test from GraphIQL',
updatedAt: Wed Oct 12 2016 09:52:05 GMT-0700 (PDT),
createdAt: Wed Oct 12 2016 09:52:05 GMT-0700 (PDT) },
_previousDataValues:
{ fromID: '1',
toID: '2',
msgText: 'Test from GraphIQL',
id: 21,
createdAt: Wed Oct 12 2016 09:52:05 GMT-0700 (PDT),
updatedAt: Wed Oct 12 2016 09:52:05 GMT-0700 (PDT) },
_changed:
{ fromID: false,
toID: false,
msgText: false,
id: false,
createdAt: false,
updatedAt: false },
'$modelOptions':
{ timestamps: true,
instanceMethods: {},
classMethods: {},
validate: {},
freezeTableName: false,
underscored: false,
underscoredAll: false,
paranoid: false,
rejectOnEmpty: false,
whereCollection: null,
schema: null,
schemaDelimiter: '',
defaultScope: {},
scopes: [],
hooks: {},
indexes: [],
name: { plural: 'IMs', singular: 'IM' },
omitNul: false,
sequelize:
{ options: [Object],
config: [Object],
dialect: [Object],
models: [Object],
modelManager: [Object],
connectionManager: [Object],
importCache: {},
test: [Object],
queryInterface: [Object] },
uniqueKeys: {},
hasPrimaryKeys: true },
'$options':
{ isNewRecord: true,
'$schema': null,
'$schemaDelimiter': '',
attributes: undefined,
include: undefined,
raw: undefined,
silent: undefined },
hasPrimaryKeys: true,
__eagerlyLoadedAssociations: [],
isNewRecord: false }
In the .then((args) => code block at checkpoint #2, args comes in as undefined.
How do I get args to contain an array of results from checkpoint #1?
.then((x) => console.log(x))
.then((args) =>{
is like doing
.then((x) => {
console.log(x);
return undefined;
})
.then((args) =>{
because console.log returns undefined. That means the undefined value will be what gets passed to the next .then.
The easiest approach would be to explicitly
.then((x) => {
console.log(x);
return x;
})
or in a shorter version using the comma operator
.then((x) => (console.log(x), x))

moment returns wrong value

I'm parsing a date using momentjs:
var startDate = moment.utc('2016-02-20T07:00:00.000Z')
If I'm writing startDate.toISOString() to the console, I get this:
2016-02-19T21:00:00.000Z
The startDate object looks like this:
{ [Number: 1455915600000]
_isAMomentObject: true,
_i: '2016-02-20T07:00:00.000Z',
_f: 'YYYY-MM-DDTHH:mm:ss.SSSSZ',
_tzm: -0,
_isUTC: true,
_pf:
{ empty: false,
unusedTokens: [],
unusedInput: [],
overflow: -1,
charsLeftOver: 0,
nullInput: false,
invalidMonth: null,
invalidFormat: false,
userInvalidated: false,
iso: true },
_locale:
Locale {
_ordinalParse: /\d{1,2}(th|st|nd|rd)/,
ordinal: [Function],
_abbr: 'en',
_ordinalParseLenient: /\d{1,2}(th|st|nd|rd)|\d{1,2}/ },
_d: Fri Feb 19 2016 22:00:00 GMT+0100 (CET),
_isValid: true,
_offset: 0 }
Where does this difference result from?

Resources