Sequelize, converte entità in oggetto semplice


95

Non ho molta familiarità con javascript e stupefacente, perché non posso aggiungere nuove proprietà, all'oggetto, che sono state recuperate dal database utilizzando i nomi ORM Sequelize.js.

Per evitare ciò, utilizzo questo trucco:

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

Quindi, qual è il modo normale per aggiungere nuove proprietà all'oggetto.

Se può aiutare, uso sequelize-postgres versione 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] } } }

Penso che poi quello che pensi sarà: "Ok, è facile, aggiungi la tua proprietà a dataValues".

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

Aggiungo queste righe e questo non funziona


nodenon deve essere un oggetto tradizionale. Forse è un Buffer? Cosa dice console.log(node);prima della tua linea di trucco?
Kevin Reilly

Vedi aggiornamento post.
Ruslan

Risposte:


43

Se hai capito bene, vuoi aggiungere la sensorsraccolta al file node. Se hai una mappatura tra entrambi i modelli puoi usare la includefunzionalità spiegata qui o il valuesgetter definito su ogni istanza. Puoi trovare i documenti per questo qui .

Quest'ultimo può essere usato in questo modo:

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

C'è anche una possibilità che nodedata.sensors = sensorspotrebbe funzionare.


2
Grazie, tutto ciò di cui ho bisogno: node.values ​​:)
Ruslan

147

è possibile utilizzare le opzioni di query {raw: true}per restituire il risultato grezzo. La tua query dovrebbe essere la seguente:

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

anche se hai associazioni con includequesto viene appiattito. Quindi, possiamo usare un altro parametronest:true

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

32
"raw: true" in qualche modo appiattirà l'array iniettato dai modelli associati. L'unico modo per aggirare il problema che ho trovato finora è configs = JSON.parse (JSON.stringify (configs));
Soichi Hayashi

1
Questo è un approccio semplice e migliore della risposta accettata. grazie
pirisma

1
@SoichiHayashi In realtà, è il contrario se ci pensi ...;) Senza raw: vero, Sequelize in qualche modo appiattisce il risultato in oggetti. I risultati della query sql sono già appiattiti. L'ho trovato utile alcune volte per ottenere un risultato piatto ... +1 per questa risposta.
PRS

4
@SoichiHayashi per ottenere risultati grezzi ma nidificati, puoi usare nest: trueinsieme a raw: true.
1valdis

qualche idea su come passare raw:truecome impostazione globale, quindi non è necessario passarlo a ogni query? Grazie
codebot

109

Per coloro che si imbattono in questa domanda più di recente, .valuesè deprecata a partire da Sequelize 3.0.0. Utilizzare .get()invece per ottenere il semplice oggetto javascript. Quindi il codice precedente cambierebbe in:

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

Sequelizza i documenti qui


6
Correggimi se sbaglio, ma non credo che funzionerà con una matrice di righe? Ad esempio con .findAndcount dovresti. Mappare le righe dei risultati e restituire .get su ciascuna per ottenere ciò di cui hai bisogno.
backdesk

Probabilmente è più semplice eseguire JSON.stringify o res.json (se lavori in express).
backdesk

@backdesk - non l'ho provato su un array - ma dai documenti sembra che dovrebbe restituire la stessa cosa.
CharlesA

3
L'uso .get()non converte le associazioni incluse, usa .get({ plain: true })invece. Grazie per aver menzionato il suggerimento dei documenti.
Wumms

Utile anche se hai già eseguito la query e quindi non puoi emettereraw: true
Matt Molnar

49

Il modo migliore e più semplice per farlo è:

Usa semplicemente il modo predefinito da Sequelize

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

Nota: [options.raw]: restituisce il risultato grezzo. Vedere sequelize.query per ulteriori informazioni.


Per il risultato annidato / se abbiamo incluso il modello, nell'ultima versione di 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);
});

Questa risposta è molto utile, usa la funzionalità nativa fornita da sequleize, nel mio caso quando invio più di una riga nel socket, si è verificato un errore di overflow dello stack.
Ashish Kadam

2
Se disponi di un array di dati figlio, otterrai risultati errati. Quindi, se la query pubblicata ne restituisce solo una sensorscon un array di figli someModel, otterrai un arraydi sensorscon uno someModelin ciascuna.
Rahmat Ali

31

Come osserva CharlesA nella sua risposta, .values()è tecnicamente deprecato , sebbene questo fatto non sia esplicitamente indicato nei documenti . Se non si desidera utilizzare { raw: true }nella query, l'approccio preferito è richiamare .get()i risultati.

.get(), tuttavia, è un metodo di un'istanza, non di un array. Come notato nel problema collegato sopra, Sequelize restituisce array nativi di oggetti istanza (ei manutentori non hanno intenzione di cambiarlo), quindi devi iterare l'array da solo:

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

Questo ha funzionato per me! Anche se non so se "get" è una funzione JavaScript o Sequelize. Per favore, illuminami. Grazie
antew

1
@antew È un metodo su un oggetto restituito dal database - fa parte della libreria sequelize, non una funzione javascript nativa.
Shane Hughes

Mi ci sono voluti anni per arrivare a questo lavoro! Grazie. È stato così difficile perché ho molti include nidificati, che è anche un bug. Quindi devo fare più query e non ho potuto per questo !!!
Karl Taylor

17

puoi usare la funzione mappa. questo ha funzionato per me.

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

Grazie, questa è stata la soluzione migliore per il mio uso cast, soloresults = results.map(el => el.get({ plain: true }))
Joshua Ohana il

9

Ho trovato una soluzione che funziona bene per il modello e l'array annidati utilizzando le funzioni JavaScript native.

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
Così semplice, ma potente e che mantiene la logica del modello. Non so quanto sia efficiente, ma è stato abbastanza buono per me. Puoi abbreviarlo in qualcosa del tipo: let res = JSON.parse (JSON.stringify (risultato));
Artipixel

9

Ecco cosa sto usando per ottenere un oggetto di risposta semplice con valori non stringati e tutte le associazioni nidificate dalla sequelizequery v4.

Con JavaScript semplice (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 po 'più 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);
};

Utilizzo :

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
Grazie! Il toPlain funziona come previsto e ha risolto anche il mio problema.
Erhnam

Questo è pulito, ma non va bene se sovrascrivi uno dei metodi toJSON del tuo modello. Lo faccio spesso per rimuovere o convertire i valori createdAt / updatedAt. Finora l'unica cosa che ha funzionato correttamente per me è JSON.parse (JSON.stringify (risposta)).
hamham

2

Per testo normale JSON nidificato

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

1
non funziona correttamente per un'associazione uno a molti è meglio usare .map (obj => obj.get ({plain: true}))
Manav Kothari

in realtà sto parlando di db.model.findOne e dell'associazione one to many.
Manav Kothari
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.