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.

UPD. 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);написано перед вашим трюком?
Кевин Рейли,
См. Сообщение об обновлении.
Руслан

Ответы:

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это тоже сработает.

sdepold
источник
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,
})
lutaoact
источник
32
"raw: true" каким-то образом сгладит массив, введенный ассоциированными моделями. Единственная работа, которую я нашел до сих пор, - это configs = JSON.parse (JSON.stringify (configs));
Соичи Хаяси
1
Это простой и лучший подход, чем принятый ответ. спасибо
pyprism
1
@SoichiHayashi На самом деле, когда вы думаете об этом, все наоборот ...;) Без raw: true Sequelize каким-то образом превращает результат в объекты. Результаты sql-запроса уже сглажены. Несколько раз мне было полезно получить плоский результат ... +1 за этот ответ.
PRS
4
@SoichiHayashi, чтобы получить необработанный, но вложенный результат, вы можете использовать nest: trueвместе с raw: true.
1valdis 08
есть идеи, как передать raw:trueглобальную настройку, чтобы нет необходимости передавать ее каждому запросу? Спасибо
codebot
109

Для тех, кто столкнулся с этим вопросом в последнее время, .valuesсчитается устаревшим с Sequelize 3.0.0. Используйте .get()вместо этого, чтобы получить простой объект javascript. Таким образом, приведенный выше код изменится на:

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

Продолжить документы здесь

CharlesA
источник
6
Поправьте меня, если я ошибаюсь - но я не думаю, что это сработает с массивом строк? Например, с .findAndcount вам придется сопоставить строки результатов и вернуть .get для каждой, чтобы получить то, что вам нужно.
backdesk 06
Вероятно, проще просто запустить JSON.stringify или res.json (если вы работаете в экспрессе).
backdesk 06
@backdesk - не пробовал в массиве - но из документации похоже, что он должен вернуть то же самое.
CharlesA
3
Использование .get()не будет преобразовывать включенные ассоциации, используйте .get({ plain: true })вместо этого. Спасибо, что упомянули предложение из документов.
Wumms
Также полезно, если вы уже выполнили запрос и, следовательно, не можете его выполнить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 для получения дополнительной информации.


Для вложенного результата / если у нас есть модель включения, в последней версии 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);
});
Вивек Доши
источник
Этот ответ очень полезен, используйте собственные функции, предоставляемые sequleize, в моем случае, когда я отправляю более одной строки в сокет, произошла ошибка переполнения стека.
Ашиш Кадам
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. Пожалуйста, просветите меня. Спасибо
antew
1
@antew Это метод объекта, возвращенного из базы данных - это часть библиотеки sequelize, а не встроенная функция javascript.
Шейн Хьюз
Мне потребовались годы, чтобы добраться до этой работы! Спасибо. Было так сложно, потому что у меня много вложенных включений, что тоже является ошибкой. Так что мне пришлось сделать несколько запросов, и я не мог из-за этого !!!
Карл Тейлор
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);
};

С lodash (немного более кратко):

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 работает так, как ожидалось, и также решил мою проблему.
Erhnam
Это здорово, но не годится, если вы переопределите любой из методов toJSON своей модели. Я делаю это довольно часто, чтобы удалить или преобразовать значения createdAt / updatedAt. Пока что единственное, что у меня сработало, это JSON.parse (JSON.stringify (response)).
Hamham
2

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

db.model.findAll({
  raw : true ,
  nest : true
})
Амр Ибрагим
источник
1
он не работает должным образом для связи от одного ко многим, лучше использовать .map (obj => obj.get ({plain: true}))
Манав Котари
на самом деле я говорю о db.model.findOne и ассоциации "один ко многим".
Манав Котари,