143

I'm not very familiar with javascript, and stunning, because i can't add new property, to object, that fetched from database using ORM names Sequelize.js.

To avoid this, i use this hack:

db.Sensors.findAll({
    where: {
        nodeid: node.nodeid
    }
}).success(function (sensors) {
        var nodedata = JSON.parse(JSON.stringify(node)); // this is my trick
        nodedata.sensors = sensors;
        nodesensors.push(nodedata);
        response.json(nodesensors);
});

So, what normally way to add new properties to object.

If it can help, i use sequelize-postgres version 2.0.x.

upd. console.log(node):

{ dataValues: 
   { nodeid: 'NodeId',
     name: 'NameHere',
     altname: 'Test9',
     longname: '',
     latitude: 30,
     longitude: -10,
     networkid: 'NetworkId',
     farmid: '5',
     lastheard: Mon Dec 09 2013 04:04:40 GMT+0300 (FET),
     id: 9,
     createdAt: Tue Dec 03 2013 01:29:09 GMT+0300 (FET),
     updatedAt: Sun Feb 23 2014 01:07:14 GMT+0300 (FET) },
  __options: 
   { timestamps: true,
     createdAt: 'createdAt',
     updatedAt: 'updatedAt',
     deletedAt: 'deletedAt',
     touchedAt: 'touchedAt',
     instanceMethods: {},
     classMethods: {},
     validate: {},
     freezeTableName: false,
     underscored: false,
     syncOnAssociation: true,
     paranoid: false,
     whereCollection: { farmid: 5, networkid: 'NetworkId' },
     schema: null,
     schemaDelimiter: '',
     language: 'en',
     defaultScope: null,
     scopes: null,
     hooks: { beforeCreate: [], afterCreate: [] },
     omitNull: false,
     hasPrimaryKeys: false },
  hasPrimaryKeys: false,
  selectedValues: 
   { nodeid: 'NodeId',
     name: 'NameHere',
     longname: '',
     latitude: 30,
     longitude: -110,
     networkid: 'NetworkId',
     farmid: '5',
     lastheard: Mon Dec 09 2013 04:04:40 GMT+0300 (FET),
     id: 9,
     createdAt: Tue Dec 03 2013 01:29:09 GMT+0300 (FET),
     updatedAt: Sun Feb 23 2014 01:07:14 GMT+0300 (FET),
     altname: 'Test9' },
  __eagerlyLoadedAssociations: [],
  isDirty: false,
  isNewRecord: false,
  daoFactoryName: 'Nodes',
  daoFactory: 
   { options: 
      { timestamps: true,
        createdAt: 'createdAt',
        updatedAt: 'updatedAt',
        deletedAt: 'deletedAt',
        touchedAt: 'touchedAt',
        instanceMethods: {},
        classMethods: {},
        validate: {},
        freezeTableName: false,
        underscored: false,
        syncOnAssociation: true,
        paranoid: false,
        whereCollection: [Object],
        schema: null,
        schemaDelimiter: '',
        language: 'en',
        defaultScope: null,
        scopes: null,
        hooks: [Object],
        omitNull: false,
        hasPrimaryKeys: false },
     name: 'Nodes',
     tableName: 'Nodes',
     rawAttributes: 
      { nodeid: [Object],
        name: [Object],
        altname: [Object],
        longname: [Object],
        latitude: [Object],
        longitude: [Object],
        networkid: [Object],
        farmid: [Object],
        lastheard: [Object],
        id: [Object],
        createdAt: [Object],
        updatedAt: [Object] },
     daoFactoryManager: { daos: [Object], sequelize: [Object] },
     associations: {},
     scopeObj: {},
     primaryKeys: {},
     primaryKeyCount: 0,
     hasPrimaryKeys: false,
     autoIncrementField: 'id',
     DAO: { [Function] super_: [Function] } } }

I think next, what you think will be: "Ok, that is easy, just add your property to dataValues."

node.selectedValues.sensors = sensors;
node.dataValues.sensors = sensors;

I add this lines, and this don't work

Ruslan
  • 14,229
  • 8
  • 49
  • 67

12 Answers12

236

you can use the query options {raw: true} to return the raw result. Your query should like follows:

db.Sensors.findAll({
  where: {
    nodeid: node.nodeid
  },
  raw: true,
})

also if you have associations with include that gets flattened. So, we can use another parameter nest:true

db.Sensors.findAll({
  where: {
    nodeid: node.nodeid
  },
  raw: true,
  nest: true,
})

enter image description here

lutaoact
  • 4,149
  • 6
  • 29
  • 41
  • 48
    "raw: true" will somehow flattens array injected by associated models. The only work around I found so far is to configs = JSON.parse(JSON.stringify(configs)); – Soichi Hayashi Oct 14 '15 at 01:51
  • 1
    That's simple and better approach than accepted answer. thanks – pyprism Mar 10 '16 at 15:34
  • 1
    @SoichiHayashi Actually, its the reverse when you think about it... ;) Without raw: true, Sequelize somehow unflattens the result into objects. The results from the sql query are already flattened. I've found it useful a few times to get a flat result... +1 for this answer. – PRS Sep 06 '16 at 08:02
  • 8
    @SoichiHayashi to get raw but nested result, you can use `nest: true` along with `raw: true`. – 1valdis Oct 08 '19 at 10:02
  • 1
    any idea how to pass the `raw:true` as a global setting, so there is no need to pass it to every query? Thanks – codebot May 19 '20 at 10:34
  • If you have array of association(such as hasMany), try [toJSON](https://sequelize.org/master/class/lib/model.js~Model.html#instance-method-toJSON), it actually returns object instead of string. – hsinewu May 18 '21 at 07:57
  • 1
    Note that `raw: true` will also ignore any `virtual` columns – Eli Zatlawy Mar 22 '23 at 10:03
149

For those coming across this question more recently, .values is deprecated as of Sequelize 3.0.0. Use .get() instead to get the plain javascript object. So the above code would change to:

var nodedata = node.get({ plain: true });

Sequelize docs here

CharlesA
  • 4,260
  • 2
  • 25
  • 31
  • 9
    Correct me if I'm wrong - but I don't think this will work with an array of rows? For example with .findAndcount you'd have to .map the result rows and return .get on each to get what you need. – backdesk Jan 06 '16 at 10:50
  • Probably easier to just run JSON.stringify or res.json (if you're working within express). – backdesk Jan 06 '16 at 11:01
  • @backdesk - haven't tried it on an array - but from the [docs](http://docs.sequelizejs.com/en/latest/docs/instances/#values-of-an-instance) it looks like it should return the same thing. – CharlesA Jan 06 '16 at 12:40
  • 6
    Using `.get()` won't convert included associations, use `.get({ plain: true })` instead. Thanks for mentioning the suggestion from the docs. – Wumms Mar 17 '16 at 16:25
  • 1
    Also useful if you already performed the query and thus can't issue `raw: true` – Matt Molnar Jun 28 '18 at 16:27
  • Best answer so far. – diogo Jul 22 '20 at 14:21
59

Best and the simple way of doing is :

Just use the default way from Sequelize

db.Sensors.findAll({
    where: {
        nodeid: node.nodeid
    },
    raw : true // <----------- Magic is here
}).success(function (sensors) {
        console.log(sensors);
});

Note : [options.raw] : Return raw result. See sequelize.query for more information.


For the nested result/if we have include model , In latest version of sequlize ,

db.Sensors.findAll({
    where: {
        nodeid: node.nodeid
    },
    include : [
        { model : someModel }
    ]
    raw : true , // <----------- Magic is here
    nest : true // <----------- Magic is here
}).success(function (sensors) {
        console.log(sensors);
});
Vivek Doshi
  • 56,649
  • 12
  • 110
  • 122
  • This answer very useful, use native functionality provided by sequleize, in my case when i send more than one row in socket, stack overflow error happened. – Ashish Kadam Mar 06 '19 at 06:06
  • 10
    If you have an array of child data, you will get wrong results. So, if posted query returns only one `sensors` with an array of child `someModel`, you will get an `array` of `sensors` with one `someModel` in each. – Rahmat Ali Dec 23 '19 at 12:39
  • 2
    `raw` result also omits all `VIRTUAL` property values. – defraggled Apr 05 '21 at 14:20
47

If I get you right, you want to add the sensors collection to the node. If you have a mapping between both models you can either use the include functionality explained here or the values getter defined on every instance. You can find the docs for that here.

The latter can be used like this:

db.Sensors.findAll({
  where: {
    nodeid: node.nodeid
  }
}).success(function (sensors) {
  var nodedata = node.values;

  nodedata.sensors = sensors.map(function(sensor){ return sensor.values });
  // or
  nodedata.sensors = sensors.map(function(sensor){ return sensor.toJSON() });

  nodesensors.push(nodedata);
  response.json(nodesensors);
});

There is chance that nodedata.sensors = sensors could work as well.

sdepold
  • 6,171
  • 2
  • 41
  • 47
33

As CharlesA notes in his answer, .values() is technically deprecated, though this fact isn't explicitly noted in the docs. If you don't want to use { raw: true } in the query, the preferred approach is to call .get() on the results.

.get(), however, is a method of an instance, not of an array. As noted in the linked issue above, Sequelize returns native arrays of instance objects (and the maintainers don't plan on changing that), so you have to iterate through the array yourself:

db.Sensors.findAll({
    where: {
        nodeid: node.nodeid
    }
}).success((sensors) => {
    const nodeData = sensors.map((node) => node.get({ plain: true }));
});
Shane Hughes
  • 652
  • 6
  • 13
  • This worked for me! Though I don't know if 'get' is a JavaScript or a Sequelize function. Please enlighten me. Thanks – antew Apr 06 '18 at 11:32
  • 1
    @antew It's a method on an object returned from the database - it's part of the sequelize library, not a native javascript function. – Shane Hughes Apr 06 '18 at 20:10
  • Took me ages to get to this working! Thank you. Was so difficult because I have a lot of nested includes, which is also a bug. So I have to do multiple queries, and I couldn't because of this!!! – Karl Taylor Feb 10 '19 at 14:27
23

you can use map function. this is worked for me.

db.Sensors
    .findAll({
        where: { nodeid: node.nodeid }
     })
    .map(el => el.get({ plain: true }))
    .then((rows)=>{
        response.json( rows )
     });
nur zazin
  • 1,018
  • 13
  • 13
12

Here's what I'm using to get plain response object with non-stringified values and all nested associations from sequelize v4 query.

With plain JavaScript (ES2015+):

const toPlain = response => {
  const flattenDataValues = ({ dataValues }) => {
    const flattenedObject = {};

    Object.keys(dataValues).forEach(key => {
      const dataValue = dataValues[key];

      if (
        Array.isArray(dataValue) &&
        dataValue[0] &&
        dataValue[0].dataValues &&
        typeof dataValue[0].dataValues === 'object'
      ) {
        flattenedObject[key] = dataValues[key].map(flattenDataValues);
      } else if (dataValue && dataValue.dataValues && typeof dataValue.dataValues === 'object') {
        flattenedObject[key] = flattenDataValues(dataValues[key]);
      } else {
        flattenedObject[key] = dataValues[key];
      }
    });

    return flattenedObject;
  };

  return Array.isArray(response) ? response.map(flattenDataValues) : flattenDataValues(response);
};

With lodash (a bit more concise):

const toPlain = response => {
  const flattenDataValues = ({ dataValues }) =>
    _.mapValues(dataValues, value => (
      _.isArray(value) && _.isObject(value[0]) && _.isObject(value[0].dataValues)
        ? _.map(value, flattenDataValues)
        : _.isObject(value) && _.isObject(value.dataValues)
          ? flattenDataValues(value)
          : value
    ));

  return _.isArray(response) ? _.map(response, flattenDataValues) : flattenDataValues(response);
};

Usage:

const res = await User.findAll({
  include: [{
    model: Company,
    as: 'companies',
    include: [{
      model: Member,
      as: 'member',
    }],
  }],
});

const plain = toPlain(res);

// 'plain' now contains simple db object without any getters/setters with following structure:
// [{
//   id: 123,
//   name: 'John',
//   companies: [{
//     id: 234,
//     name: 'Google',
//     members: [{
//       id: 345,
//       name: 'Paul',
//     }]
//   }]
// }]
evenfrost
  • 414
  • 5
  • 14
  • 1
    Thanks! The toPlain works as expected and solved my issue as well. – Erhnam Dec 28 '18 at 10:42
  • This is neat, but it's no good if you override any of your model's toJSON methods. I do this quite a lot to either remove or convert the createdAt/updatedAt values. So far the only thing that has worked properly for me is JSON.parse(JSON.stringify(response)). – hamham Apr 11 '20 at 17:36
  • 1
    Your answer gave me a nudge to look deeper into [Model](https://sequelize.org/master/class/lib/model.js~Model.html)'s doc and [toJSON](https://github.com/sequelize/sequelize/blob/65549922fbe3a823cd11b3e8eea64122fe6142a5/src/model.js#L4651). After fixing a custom getter in my model definition, `toJSON` worked well for me, so had no need for a custom implementation like yours. – Michal Čizmazia Jan 11 '21 at 09:41
  • 1
    I believe this is the only working solution. ```get({plain:true})``` adds all non requested fields with undefined. ```{raw:true, nest:true}``` does not deal with 1:n nested associations well. – Zoltan Magyar Oct 06 '21 at 11:21
10

I have found a solution that works fine for nested model and array using native JavaScript functions.

var results = [{},{},...]; //your result data returned from sequelize query
var jsonString = JSON.stringify(results); //convert to string to remove the sequelize specific meta data

var obj = JSON.parse(jsonString); //to make plain json
// do whatever you want to do with obj as plain json
Suben Saha
  • 1,778
  • 16
  • 21
  • 1
    So simple, yet powerfully works and keeps the model logic. I don't know how efficient it is, but it was good enough for me. You can short it to something like: let res = JSON.parse(JSON.stringify(result)); – Artipixel Aug 20 '18 at 13:24
6

For plain objects

db.model.findAll({
  raw : true ,
  nest : true
})
Amr Ibrahim
  • 2,066
  • 2
  • 23
  • 47
2

You can also try this if you want to occur for all the queries:

var sequelize = new Sequelize('database', 'username', 'password', {query:{raw:true}})

Visit Setting all queries to raw = true sequelize

2

It's a little late to answer, but in case someone runs across this question and needs a solution...

(Assuming this is in async/await function)

const sensors = JSON.parse(JSON.stringify(await 
    db.Sensors.findAll({
    where: {
        nodeid: node.nodeid
    }
})))

You can query and return it is a plain object, array of objects, etc...

Hatem Jaber
  • 2,341
  • 2
  • 22
  • 38
1

I use my own model mixin for handy responses

const camelcaseKeys = require('camelcase-keys');

const initMixins = Sequelize => {
    // Convert model to plain object and camelcase keys for response
    Sequelize.Model.prototype.toPlainObject = function({ camelcase = false } = {}) {
        const plain = this.get({ plain: true });
        if (camelcase) {
            return camelcaseKeys(plain, { deep: true });
        } else {
            return plain;
        }
    };
};

module.exports = {
    initMixins,
};
// usage
const userModel = await UserModel.findOne();
console.log(userModel.toPlainObject());