Sequelize, ubah entitas menjadi objek biasa

95

Saya tidak terlalu familiar dengan javascript, dan menakjubkan, karena saya tidak bisa menambahkan properti baru, ke objek, yang diambil dari database menggunakan nama ORM Sequelize.js.

Untuk menghindari ini, saya menggunakan peretasan ini:

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

Jadi, cara apa biasanya untuk menambahkan properti baru ke objek.

Jika bisa membantu, saya menggunakan sequelize-postgres versi 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] } } }

Saya pikir selanjutnya, apa yang Anda pikirkan: "Oke, itu mudah, tambahkan saja properti Anda ke dataValues."

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

Saya menambahkan baris ini, dan ini tidak berhasil

Ruslan
sumber
nodetidak boleh menjadi objek tradisional. Mungkin itu Buffer? Apa yang dikatakan console.log(node);sebelum garis trik Anda?
Kevin Reilly
Silakan lihat pembaruan posting.
Ruslan

Jawaban:

43

Jika saya mengerti, Anda ingin menambahkan sensorskoleksi ke node. Jika Anda memiliki pemetaan di antara kedua model, Anda dapat menggunakan includefungsionalitas yang dijelaskan di sini atau valuespengambil yang ditentukan di setiap instance. Anda dapat menemukan dokumennya di sini .

Yang terakhir dapat digunakan seperti ini:

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

Ada kemungkinan yang nodedata.sensors = sensorsbisa berhasil juga.

sdepold
sumber
2
Terima kasih, semua yang saya butuhkan: node.values ​​:)
Ruslan
147

Anda bisa menggunakan opsi kueri {raw: true}untuk mengembalikan hasil mentah. Kueri Anda harus seperti berikut:

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

juga jika Anda memiliki asosiasi dengan includeyang diratakan. Jadi, kita bisa menggunakan parameter lainnest:true

db.Sensors.findAll({
  where: {
    nodeid: node.nodeid
  },
  raw: true,
  nest: true,
})
lutaoact.dll
sumber
32
"raw: true" entah bagaimana akan meratakan array yang diinjeksi oleh model terkait. Satu-satunya solusi yang saya temukan sejauh ini adalah dengan configs = JSON.parse (JSON.stringify (configs));
Soichi Hayashi
1
Itu pendekatan yang sederhana dan lebih baik daripada jawaban yang diterima. terima kasih
pyprism
1
@SoichiHayashi Sebenarnya, kebalikannya ketika Anda memikirkannya ...;) Tanpa mentah: benar, Sequelize entah bagaimana meratakan hasilnya menjadi objek. Hasil dari query sql sudah diratakan. Saya merasa berguna beberapa kali untuk mendapatkan hasil datar ... +1 untuk jawaban ini.
PRS
4
@SoichiHayashi untuk mendapatkan hasil mentah tetapi bersarang, Anda dapat menggunakannya nest: truebersama raw: true.
1valdis
ada ide bagaimana mengirimkannya raw:truesebagai pengaturan global, jadi tidak perlu meneruskannya ke setiap kueri? Terima kasih
codebot
110

Bagi mereka yang menemukan pertanyaan ini baru-baru ini, .valuestidak berlaku lagi pada Sequelize 3.0.0. Gunakan .get()sebagai gantinya untuk mendapatkan objek javascript biasa. Jadi kode di atas akan berubah menjadi:

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

Sequelize dokumen di sini

CharlesA
sumber
6
Koreksi saya jika saya salah - tetapi saya tidak berpikir ini akan berfungsi dengan array baris? Misalnya dengan .findAndcount Anda harus. Memetakan baris hasil dan mengembalikan .get pada masing-masing untuk mendapatkan yang Anda butuhkan.
meja belakang
Mungkin lebih mudah untuk menjalankan JSON.stringify atau res.json (jika Anda bekerja dalam express).
meja belakang
@ Backdesk - belum mencobanya pada array - tetapi dari dokumen sepertinya itu akan mengembalikan hal yang sama.
CharlesA
3
Menggunakan .get()tidak akan mengonversi asosiasi yang disertakan, gunakan .get({ plain: true })sebagai gantinya. Terima kasih telah menyebutkan saran dari dokumen.
Wumms
Juga berguna jika Anda telah melakukan kueri dan karenanya tidak dapat menerbitkanraw: true
Matt Molnar
49

Cara terbaik dan sederhana untuk melakukannya adalah:

Cukup gunakan cara default dari Sequelize

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

Catatan: [options.raw]: Mengembalikan hasil mentah. Lihat sequelize.query untuk informasi lebih lanjut.


Untuk hasil bersarang / jika kita sudah memasukkan model, Dalam versi terbaru dari 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
sumber
Jawaban ini sangat berguna, gunakan fungsionalitas asli yang disediakan oleh sequleize, dalam kasus saya ketika saya mengirim lebih dari satu baris di soket, terjadi kesalahan stack overflow.
Ashish Kadam
2
Jika Anda memiliki larik data anak, Anda akan mendapatkan hasil yang salah. Jadi, jika dipasang kembali permintaan hanya satu sensorsdengan array anak someModel, Anda akan mendapatkan arraydari sensorsdengan satu someModeldi masing-masing.
Rahmat Ali
31

Seperti yang dicatat CharlesA dalam jawabannya, .values()secara teknis tidak digunakan lagi , meskipun fakta ini tidak secara eksplisit disebutkan dalam dokumen . Jika Anda tidak ingin menggunakan { raw: true }dalam kueri, pendekatan yang disukai adalah memanggil .get()hasil.

.get(), bagaimanapun, adalah metode sebuah instance, bukan dari sebuah array. Seperti disebutkan dalam masalah terkait di atas, Sequelize mengembalikan array asli objek instance (dan pengelola tidak berencana mengubahnya), jadi Anda harus mengulang sendiri melalui array:

db.Sensors.findAll({
    where: {
        nodeid: node.nodeid
    }
}).success((sensors) => {
    const nodeData = sensors.map((node) => node.get({ plain: true }));
});
Shane Hughes
sumber
Ini berhasil untuk saya! Meskipun saya tidak tahu apakah 'get' adalah JavaScript atau fungsi Sequelize. Tolong beri saya pencerahan. Terima kasih
antew
1
@antew Ini adalah metode pada objek yang dikembalikan dari database - itu bagian dari perpustakaan sekuel, bukan fungsi javascript asli.
Shane Hughes
Butuh waktu lama bagiku untuk bekerja! Terima kasih. Sangat sulit karena saya memiliki banyak include bersarang, yang juga merupakan bug. Jadi saya harus melakukan banyak pertanyaan, dan saya tidak bisa karena ini !!!
Karl Taylor
17

Anda dapat menggunakan fungsi peta. ini berhasil untuk saya.

db.Sensors
    .findAll({
        where: { nodeid: node.nodeid }
     })
    .map(el => el.get({ plain: true }))
    .then((rows)=>{
        response.json( rows )
     });
nur zazin
sumber
Terima kasih, ini adalah solusi terbaik untuk pemeran yang saya gunakan, hanyaresults = results.map(el => el.get({ plain: true }))
Joshua Ohana
9

Saya telah menemukan solusi yang berfungsi dengan baik untuk model dan array bersarang menggunakan fungsi JavaScript asli.

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
sumber
1
Sangat sederhana, namun bekerja dengan kuat dan mempertahankan logika model. Saya tidak tahu seberapa efisiennya, tapi cukup bagus untuk saya. Anda bisa menyingkatnya menjadi sesuatu seperti: let res = JSON.parse (JSON.stringify (result));
Artipixel
9

Inilah yang saya gunakan untuk mendapatkan objek respons biasa dengan nilai non-string dan semua asosiasi bersarang dari sequelizekueri v4.

Dengan JavaScript biasa (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);
};

Dengan lodash (sedikit lebih ringkas):

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

Penggunaan :

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',
//     }]
//   }]
// }]
bahkan beku
sumber
1
Terima kasih! ToPlain berfungsi seperti yang diharapkan dan menyelesaikan masalah saya juga.
Erhnam
Ini rapi, tetapi tidak ada gunanya jika Anda mengganti metode toJSON model Anda. Saya melakukan ini cukup banyak untuk menghapus atau mengonversi nilai createAt / updatedAt. Sejauh ini, satu-satunya hal yang berfungsi dengan baik untuk saya adalah JSON.parse (JSON.stringify (response)).
hamham
2

Untuk teks biasa JSON bertingkat

db.model.findAll({
  raw : true ,
  nest : true
})
Amr Ibrahim
sumber
1
itu tidak bekerja dengan baik untuk satu ke banyak asosiasi lebih baik menggunakan .map (obj => obj.get ({plain: true}))
Manav Kothari
sebenarnya saya berbicara tentang db.model.findOne dan satu untuk banyak asosiasi.
Manav Kothari