Sequelize, konvertiere Entität in einfaches Objekt


93

Ich bin nicht sehr vertraut mit Javascript und atemberaubend, weil ich dem Objekt, das mit den ORM-Namen Sequelize.js aus der Datenbank abgerufen wurde, keine neue Eigenschaft hinzufügen kann.

Um dies zu vermeiden, benutze ich diesen 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);
});

Wie kann man dem Objekt normalerweise neue Eigenschaften hinzufügen?

Wenn es helfen kann, verwende ich sequelize-postgres Version 2.0.x.

upd. console.log (Knoten):

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

Ich denke als nächstes, was Sie denken, wird sein: "Ok, das ist einfach, fügen Sie einfach Ihre Eigenschaft zu dataValues ​​hinzu."

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

Ich füge diese Zeilen hinzu und das funktioniert nicht


nodedarf kein traditionelles Objekt sein. Vielleicht ist es ein Puffer? Was sagt console.log(node);vor Ihrer Tricklinie?
Kevin Reilly

Bitte siehe Post-Update.
Ruslan

Antworten:


42

Wenn ich Sie richtig verstehe, möchten Sie die sensorsSammlung zur hinzufügen node. Wenn Sie eine Zuordnung zwischen beiden Modellen haben, können Sie entweder die hier erläuterteinclude Funktionalität oder den für jede Instanz definierten Getter verwenden. Die Dokumentation dazu finden Sie hier .values

Letzteres kann folgendermaßen verwendet werden:

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

Es besteht die Möglichkeit, dass nodedata.sensors = sensorsdies auch funktioniert.


2
Vielen Dank, alles was ich brauche: node.values ​​:)
Ruslan

135

Sie können die Abfrageoptionen verwenden {raw: true}, um das Rohergebnis zurückzugeben. Ihre Anfrage sollte wie folgt aussehen:

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

auch wenn Sie Assoziationen damit haben include, wird abgeflacht. Wir können also einen anderen Parameter verwendennest:true

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

31
"raw: true" wird das von den zugehörigen Modellen injizierte Array irgendwie abflachen. Die einzige Lösung, die ich bisher gefunden habe, ist configs = JSON.parse (JSON.stringify (configs));
Soichi Hayashi

1
Das ist ein einfacher und besserer Ansatz als eine akzeptierte Antwort. danke
pyprism

1
@SoichiHayashi Eigentlich ist es umgekehrt, wenn man darüber nachdenkt ...;) Ohne raw: true, Sequelize macht das Ergebnis irgendwie zu Objekten. Die Ergebnisse der SQL-Abfrage sind bereits abgeflacht. Ich fand es ein paar Mal nützlich, ein flaches Ergebnis zu erhalten ... +1 für diese Antwort.
PRS

4
@SoichiHayashi, um ein rohes, aber verschachteltes Ergebnis zu erhalten, das Sie nest: truezusammen mit verwenden können raw: true.
1valdis

Haben Sie eine Idee, wie Sie die raw:trueEinstellung als globale Einstellung übergeben können, sodass sie nicht an jede Abfrage übergeben werden muss? Danke
Codebot

109

Für diejenigen, die in jüngerer Zeit auf diese Frage .valuesstoßen , ist sie ab Sequelize 3.0.0 veraltet. Verwenden Sie .get()stattdessen, um das einfache Javascript-Objekt abzurufen. Der obige Code würde sich also ändern zu:

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

Dokumentieren Sie die Dokumente hier


6
Korrigieren Sie mich, wenn ich falsch liege - aber ich glaube nicht, dass dies mit einer Reihe von Zeilen funktioniert? Zum Beispiel müssten Sie mit .findAndcount die Ergebniszeilen zuordnen und jeweils .get zurückgeben, um das zu erhalten, was Sie benötigen.
Backdesk

Wahrscheinlich einfacher, einfach JSON.stringify oder res.json auszuführen (wenn Sie in Express arbeiten).
Backdesk

@backdesk - habe es noch nicht auf einem Array versucht - aber aus den Dokumenten geht hervor, dass es dasselbe zurückgeben sollte.
CharlesA

3
Mit .get()wird keine eingeschlossenen Assoziationen konvertieren, .get({ plain: true })stattdessen verwenden. Vielen Dank, dass Sie den Vorschlag aus den Dokumenten erwähnt haben.
Wumms

Auch nützlich, wenn Sie die Abfrage bereits durchgeführt haben und daher keine Ausgabe durchführen könnenraw: true
Matt Molnar

48

Das Beste und Einfachste ist:

Verwenden Sie einfach die Standardmethode von Sequelize

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

Hinweis: [options.raw]: Gibt das Rohergebnis zurück. Weitere Informationen finden Sie unter sequelize.query.


Für das verschachtelte Ergebnis / wenn wir ein Modell enthalten haben, In der neuesten Version von 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);
});

Diese Antwort ist sehr nützlich. Verwenden Sie die von sequleize bereitgestellte native Funktionalität. In meinem Fall ist ein Stapelüberlauffehler aufgetreten, wenn ich mehr als eine Zeile im Socket sende.
Ashish Kadam

1
Wenn Sie ein Array von untergeordneten Daten haben, erhalten Sie falsche Ergebnisse. Also, wenn Abfrage zurück geschrieben nur ein sensorsmit einer Reihe von Kind someModel, werden Sie eine bekommen arrayvon sensorseinem someModelin jedem.
Rahmat Ali

30

Wie CharlesA in seiner Antwort feststellt, .values()ist dies technisch veraltet , obwohl diese Tatsache in den Dokumenten nicht ausdrücklich erwähnt wird . Wenn Sie nicht { raw: true }in der Abfrage verwenden möchten , ist der bevorzugte Ansatz, .get()die Ergebnisse aufzurufen .

.get()ist jedoch eine Methode einer Instanz, nicht eines Arrays. Wie im obigen verknüpften Problem erwähnt, gibt Sequelize native Arrays von Instanzobjekten zurück (und die Betreuer planen nicht, dies zu ändern), sodass Sie das Array selbst durchlaufen müssen:

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

Das hat bei mir funktioniert! Obwohl ich nicht weiß, ob 'get' ein JavaScript oder eine Sequelize-Funktion ist. Bitte erleuchte mich. Vielen Dank
am

1
@antew Dies ist eine Methode für ein Objekt, das von der Datenbank zurückgegeben wird. Sie ist Teil der Sequelize-Bibliothek und keine native Javascript-Funktion.
Shane Hughes

Ich habe ewig gebraucht, um an diese Arbeit zu kommen! Danke dir. War so schwierig, weil ich viele verschachtelte Includes habe, was auch ein Fehler ist. Ich muss also mehrere Abfragen machen und konnte das aus diesem Grund nicht !!!
Karl Taylor

15

Sie können die Kartenfunktion verwenden. das ist für mich gearbeitet.

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

Vielen Dank, dies war die beste Lösung für meine Verwendung Besetzung, nurresults = results.map(el => el.get({ plain: true }))
Joshua Ohana

9

Folgendes verwende ich, um ein einfaches Antwortobjekt mit nicht stringifizierten Werten und allen verschachtelten Zuordnungen aus der sequelizev4-Abfrage abzurufen .

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

Mit lodash (etwas prägnanter):

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

Verwendung :

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
Vielen Dank! Der toPlain funktioniert wie erwartet und hat auch mein Problem gelöst.
Erhnam

Das ist ordentlich, aber es ist nicht gut, wenn Sie eine der toJSON-Methoden Ihres Modells überschreiben. Ich mache das ziemlich oft, um die Werte "createdAt / updatedAt" zu entfernen oder zu konvertieren. Bisher hat für mich nur JSON.parse (JSON.stringify (Antwort)) richtig funktioniert.
Hamham

8

Ich habe eine Lösung gefunden, die für verschachtelte Modelle und Arrays mit nativen JavaScript-Funktionen gut funktioniert.

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
So einfach und doch kraftvoll funktioniert und die Modelllogik beibehält. Ich weiß nicht, wie effizient es ist, aber es war gut genug für mich. Sie können es kurzschließen mit: let res = JSON.parse (JSON.stringify (Ergebnis));
Artipixel

2

Für verschachtelten JSON-Klartext

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

1
Es funktioniert nicht richtig für eine bis viele Assoziationen. Es ist besser, .map (obj => obj.get ({plain: true})) zu verwenden.
Manav Kothari

Es funktioniert gut für mich auch für verschachtelte
Amr Ibrahim

Eigentlich spreche ich über db.model.findOne und eine zu viele Assoziationen.
Manav Kothari
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.