Sequelize, перетворити сутність на звичайний об'єкт


95

Я не дуже добре знайомий з javascript і приголомшливий, тому що я не можу додати нове властивість до об'єкта, яке отримується з бази даних за допомогою імен ORM Sequelize.js.

Щоб цього уникнути, я використовую такий хак:

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

Отже, який звичайний спосіб додати нові властивості до об’єкта.

Якщо це може допомогти, я використовую sequelize-postgres версії 2.0.x.

оновлено. console.log (вузол):

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

Я думаю, далі, що ви думаєте, буде: "Гаразд, це просто, просто додайте своє властивість до dataValues."

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

Я додаю ці рядки, і це не працює


nodeне повинен бути традиційним об’єктом. Можливо, це буфер? Що говорить console.log(node);перед вашим трюком?
Kevin Reilly

Будь ласка, дивіться оновлення допису.
Руслан

Відповіді:


43

Якщо я правильно вас зрозумів, ви хочете додати sensorsколекцію до node. Якщо у вас є зіставлення між обома моделями, ви можете використовувати includeфункціональність, описану тут, або valuesгеттер, визначений у кожному екземплярі. Документи щодо цього ви можете знайти тут .

Останні можна використовувати так:

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

Є шанс, який також nodedata.sensors = sensorsміг би спрацювати.


2
Дякую, все, що мені потрібно: node.values ​​:)
Руслан

147

Ви можете використовувати параметри запиту {raw: true}для повернення вихідного результату. Ваш запит повинен виглядати наступним чином:

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

також якщо у вас є асоціації з тим, includeщо згладжується Отже, ми можемо використовувати інший параметрnest:true

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

32
"raw: true" якимось чином вирівняє масив, введений відповідними моделями. Єдиною роботою, яку я знайшов дотепер, є configs = JSON.parse (JSON.stringify (configs));
Soichi Hayashi

1
Це простий і кращий підхід, ніж прийнята відповідь. спасибі
pyprism

1
@SoichiHayashi Насправді все навпаки, коли ти думаєш про це ...;) Без raw: true, Sequelize якимось чином розгалужує результат на об'єкти. Результати запиту sql вже вирівняні. Кілька разів мені було корисно отримати чіткий результат ... +1 для цієї відповіді.
PRS

4
@SoichiHayashi, щоб отримати необроблений, але вкладений результат, ви можете використовувати nest: trueразом із raw: true.
валдіс

будь-яка ідея, як передавати raw:trueяк глобальний параметр, тому немає необхідності передавати його кожному запиту? Дякую
codebot

109

Для тих, хто стикався з цим запитанням нещодавно, .valuesзастарілим станом на Sequelize 3.0.0. Використовуйте .get()замість цього, щоб отримати простий об’єкт javascript. Отже, наведений вище код змінився б на:

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

Закріпіть тут документи


6
Виправте мене, якщо я помиляюся - але я не думаю, що це буде працювати з масивом рядків? Наприклад, за допомогою .findAndcount вам потрібно було б .написати рядки результатів і повернути .get на кожному, щоб отримати те, що вам потрібно.
backdesk

Можливо, простіше просто запустити JSON.stringify або res.json (якщо ви працюєте в express).
backdesk

@backdesk - не пробував це на масиві - але з документів, схоже, він повинен повертати те саме.
CharlesA

3
Використання .get()не перетворює включені асоціації, .get({ plain: true })замість цього використовуйте . Дякуємо, що згадали пропозицію з документації.
Вуммс

Також корисно, якщо ви вже виконували запит і, отже, не можете його видатиraw: true
Метт Молнар,

49

Найкращий і простий спосіб зробити це:

Просто скористайтеся способом за замовчуванням від Sequelize

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

Примітка: [options.raw]: Повернути вихідний результат. Для отримання додаткової інформації див. Sequelize.query.


Для вкладеного результату / якщо ми маємо включити модель, в останній версії

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

Ця відповідь дуже корисна, використовуйте рідну функціональність, яку надає секвелайз, у моєму випадку, коли я надсилаю більше одного рядка в сокет, сталася помилка переповнення стека.
Ashish

2
Якщо у вас є масив дочірніх даних, ви отримаєте неправильні результати. Отже, якщо опублікований запит повертає лише один sensorsіз масивом дочірніх елементів someModel, ви отримаєте значення arrayз sensorsпо одному someModelв кожному.
Рахмат Алі

31

Як CharlesA зазначає у своїй відповіді, .values()є технічно застарілими , хоча цей факт явно не зазначено в документації . Якщо ви не хочете використовувати { raw: true }в запиті, найкращим підходом є виклик .get()результатів.

.get()однак це метод екземпляра, а не масиву. Як зазначалося у зв’язаному випуску вище, Sequelize повертає власні масиви об’єктів екземпляра (а супровідники цього не планують змінювати), тому вам доведеться самостійно перебирати масив:

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

Це спрацювало для мене! Хоча я не знаю, чи є "get" JavaScript чи функцією Sequelize. Будь ласка, просвіти мене. Дякую
антевуйте

1
@antew Це метод на об'єкті, що повертається з бази даних - це частина бібліотеки секвелізацій, а не власна функція javascript.
Шейн Хьюз,

Мені потрібні віки, щоб дістатися до цієї роботи! Дякую. Це було так складно, тому що у мене є багато вкладених включень, що також є помилкою. Тому мені доводиться робити кілька запитів, і я не міг через це !!!
Karl Taylor

17

Ви можете використовувати функцію карти. це спрацювало для мене.

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

Дякую, це було найкраще рішення для мого використання, простоresults = results.map(el => el.get({ plain: true }))
Джошуа Охана

9

Я знайшов рішення, яке чудово працює для вкладеної моделі та масиву з використанням власних функцій 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
Настільки простий, але потужно працює і зберігає логіку моделі. Не знаю, наскільки це ефективно, але для мене це було досить добре. Ви можете скоротити його до приблизно такого: let res = JSON.parse (JSON.stringify (result));
Artipixel

9

Ось те, що я використовую для отримання простого об’єкта відповіді з нестрифікованими значеннями та всіх вкладених асоціацій із sequelizeзапиту v4.

З простим 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);
};

З лодашем (трохи коротше ):

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

Використання :

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
Дякую! ToPlain працює належним чином і вирішив і мою проблему.
Ернам

Це акуратно, але не годиться, якщо ви заміните будь-який із методів вашої моделі toJSON. Я роблю це досить багато, щоб або видалити, або перетворити значення createdAt / updatedAt. Поки єдине, що у мене справно працювало, це JSON.parse (JSON.stringify (відповідь)).
Хамхем,

2

Для вкладеного простого тексту JSON

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

1
це не спрацьовує для асоціації для багатьох, краще використовувати .map (obj => obj.get ({plain: true}))
Manav Kothari

насправді я говорю про db.model.findOne та асоціацію один до багатьох.
Манав Котарі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.