Sequelizar, convertir entidad en objeto simple


95

No estoy muy familiarizado con javascript, y es sorprendente, porque no puedo agregar una nueva propiedad, al objeto, que se obtuvo de la base de datos usando los nombres ORM Sequelize.js.

Para evitar esto, uso este truco:

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);
});

Entonces, ¿qué forma normalmente de agregar nuevas propiedades al objeto?

Si puede ayudar, uso sequelize-postgres versión 2.0.x.

upd. console.log (nodo):

{ 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] } } }

A continuación, creo que lo que piensa será: "Ok, eso es fácil, simplemente agregue su propiedad a dataValues".

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

Agrego estas líneas, y esto no funciona


nodeno debe ser un objeto tradicional. ¿Quizás es un búfer? ¿Qué dice console.log(node);antes de tu línea de truco?
Kevin Reilly

Consulte la actualización de la publicación.
Ruslan

Respuestas:


43

Si lo entiendo bien, desea agregar la sensorscolección al archivo node. Si tiene un mapeo entre ambos modelos, puede usar la includefuncionalidad explicada aquí o el valuescaptador definido en cada instancia. Puedes encontrar los documentos para eso aquí .

Este último se puede utilizar así:

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);
});

Existe la posibilidad de que eso también nodedata.sensors = sensorsfuncione.


2
Gracias, todo lo que necesito: node.values ​​:)
Ruslan

147

puede utilizar las opciones de consulta {raw: true}para devolver el resultado sin procesar. Su consulta debería ser la siguiente:

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

también si tiene asociaciones con includeeso se aplana. Entonces, podemos usar otro parámetronest:true

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

32
"raw: true" de alguna manera aplana la matriz inyectada por modelos asociados. La única solución que encontré hasta ahora es configs = JSON.parse (JSON.stringify (configs));
Soichi Hayashi

1
Ese es un enfoque simple y mejor que la respuesta aceptada. gracias
pyprism

1
@SoichiHayashi En realidad, es al revés cuando lo piensas ...;) Sin raw: true, Sequelize de alguna manera reduce el resultado a objetos. Los resultados de la consulta SQL ya están compactados. Lo he encontrado útil algunas veces para obtener un resultado plano ... +1 para esta respuesta.
PRS

4
@SoichiHayashi para obtener un resultado sin procesar pero anidado, puede usar nest: truejunto con raw: true.
1valdis

¿Alguna idea de cómo pasar el raw:truecomo una configuración global, por lo que no es necesario pasarlo a cada consulta? Gracias
codebot

109

Para aquellos que se encuentren con esta pregunta más recientemente, .valuesestá obsoleto a partir de Sequelize 3.0.0. Úselo en su .get()lugar para obtener el objeto javascript simple. Entonces el código anterior cambiaría a:

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

Secuela de documentos aquí


6
Corrígeme si me equivoco, pero no creo que esto funcione con una matriz de filas. Por ejemplo, con .findAndcount, tendría que .mapear las filas de resultados y devolver .get en cada una para obtener lo que necesita.
backdesk

Probablemente sea más fácil ejecutar JSON.stringify o res.json (si está trabajando dentro de Express).
backdesk

@backdesk: no lo he probado en una matriz, pero según los documentos parece que debería devolver lo mismo.
CharlesA

3
El uso .get()no convertirá las asociaciones incluidas, utilice .get({ plain: true })en su lugar. Gracias por mencionar la sugerencia de los documentos.
Wumms

También es útil si ya realizó la consulta y, por lo tanto, no puede emitirraw: true
Matt Molnar

49

La mejor y más sencilla forma de hacerlo es:

Simplemente use la forma predeterminada de Sequelize

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

Nota: [options.raw]: Devuelve el resultado sin procesar. Consulte sequelize.query para obtener más información.


Para el resultado anidado / si hemos incluido el modelo, en la última versión de 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);
});

Esta respuesta es muy útil, use la funcionalidad nativa proporcionada por sequleize, en mi caso, cuando envío más de una fila en el socket, ocurrió un error de desbordamiento de pila.
Ashish Kadam

2
Si tiene una matriz de datos secundarios, obtendrá resultados incorrectos. Por lo tanto, si la consulta publicada devuelve solo una sensorscon una matriz de elementos secundarios someModel, obtendrá una arrayde sensorscon una someModelen cada una.
Rahmat Ali

31

Como señala CharlesA en su respuesta, .values()está técnicamente en desuso , aunque este hecho no se menciona explícitamente en los documentos . Si no desea utilizarlo { raw: true }en la consulta, el enfoque preferido es consultar .get()los resultados.

.get(), sin embargo, es un método de una instancia, no de una matriz. Como se señaló en el problema vinculado anterior, Sequelize devuelve matrices nativas de objetos de instancia (y los mantenedores no planean cambiar eso), por lo que debe recorrer la matriz usted mismo:

db.Sensors.findAll({
    where: {
        nodeid: node.nodeid
    }
}).success((sensors) => {
    const nodeData = sensors.map((node) => node.get({ plain: true }));
});

¡Esto funcionó para mí! Aunque no sé si 'get' es una función JavaScript o Sequelize. Por favor iluminame. Gracias
antew

1
@antew Es un método sobre un objeto devuelto desde la base de datos; es parte de la biblioteca de secuenciación, no una función nativa de JavaScript.
Shane Hughes

¡Me tomó mucho tiempo conseguir que esto funcionara! Gracias. Fue muy difícil porque tengo muchas inclusiones anidadas, lo cual también es un error. ¡Así que tengo que hacer varias consultas y no pude por esto!
Karl Taylor

17

puede utilizar la función de mapa. esto funcionó para mí.

db.Sensors
    .findAll({
        where: { nodeid: node.nodeid }
     })
    .map(el => el.get({ plain: true }))
    .then((rows)=>{
        response.json( rows )
     });

Gracias, esta fue la mejor solución para mi elenco de uso, soloresults = results.map(el => el.get({ plain: true }))
Joshua Ohana

9

Encontré una solución que funciona bien para modelos y matrices anidados que utilizan funciones nativas de JavaScript.

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

1
Tan simple, pero poderosamente funciona y mantiene la lógica del modelo. No sé qué tan eficiente es, pero fue lo suficientemente bueno para mí. Puede acortarlo a algo como: let res = JSON.parse (JSON.stringify (result));
Artipixel

9

Esto es lo que estoy usando para obtener un objeto de respuesta simple con valores sin cadenas y todas las asociaciones anidadas de la sequelizeconsulta v4.

Con JavaScript simple (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);
};

Con lodash (un poco más conciso):

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);
};

Uso :

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',
//     }]
//   }]
// }]

1
¡Gracias! El toPlain funciona como se esperaba y también resolvió mi problema.
Erhnam

Esto es genial, pero no es bueno si anula cualquiera de los métodos toJSON de su modelo. Hago esto bastante para eliminar o convertir los valores createdAt / updatedAt. Hasta ahora, lo único que me ha funcionado correctamente es JSON.parse (JSON.stringify (respuesta)).
Hamham

2

Para texto sin formato JSON anidado

db.model.findAll({
  raw : true ,
  nest : true
})

1
no funciona correctamente para una asociación de uno a muchos, es mejor usar .map (obj => obj.get ({plain: true}))
Manav Kothari

en realidad, estoy hablando de db.model.findOne y una asociación de uno a muchos.
Manav Kothari
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.