Sequelizar, converter entidade em objeto simples


100

Não estou muito familiarizado com javascript, e surpreendente, porque não consigo adicionar uma nova propriedade, ao objeto, que buscou do banco de dados usando nomes ORM Sequelize.js.

Para evitar isso, uso este 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);
});

Então, qual é a maneira normal de adicionar novas propriedades ao objeto.

Se puder ajudar, eu uso sequelize-postgres versão 2.0.x.

atualiz. console.log (nó):

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

Acho que a seguir, o que você acha que será: "Ok, isso é fácil, basta adicionar sua propriedade a dataValues."

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

Eu adiciono essas linhas, e isso não funciona


nodenão deve ser um objeto tradicional. Talvez seja um Buffer? O que console.log(node);antes de sua linha de truque diz?
Kevin Reilly

Por favor, veja a atualização do post.
Ruslan

Respostas:


43

Se entendi bem, você deseja adicionar a sensorscoleção ao node. Se você tiver um mapeamento entre os dois modelos, poderá usar a includefuncionalidade explicada aqui ou o valuesgetter definido em cada instância. Você pode encontrar os documentos para isso aqui .

O último pode ser usado assim:

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

Há uma chance de que nodedata.sensors = sensorstambém funcione.


2
Obrigado, tudo que eu preciso: node.values ​​:)
Ruslan

158

você pode usar as opções de consulta {raw: true}para retornar o resultado bruto. Sua consulta deve ser a seguinte:

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

também se você tiver associações com includeisso, fica achatado. Então, podemos usar outro parâmetronest:true

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

35
"raw: true" irá de alguma forma nivelar a matriz injetada por modelos associados. A única solução que encontrei até agora é configs = JSON.parse (JSON.stringify (configs));
Soichi Hayashi,

1
Essa é uma abordagem simples e melhor do que uma resposta aceita. obrigado
pyprism de

1
@SoichiHayashi Na verdade, é o contrário quando você pensa sobre isso ...;) Sem raw: true, Sequelize de alguma forma atenua o resultado em objetos. Os resultados da consulta sql já estão nivelados. Algumas vezes achei útil obter um resultado simples ... +1 para esta resposta.
PRS

4
@SoichiHayashi para obter um resultado bruto, mas aninhado, você pode usar nest: truejunto com raw: true.
1valdis

alguma ideia de como passar o raw:truecomo uma configuração global, então não há necessidade de passar para todas as consultas? Obrigado
codebot

112

Para aqueles que se .valuesdepararam com esta questão mais recentemente, está obsoleto a partir do Sequelize 3.0.0. Use .get()para obter o objeto javascript simples. Portanto, o código acima mudaria para:

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

Sequelize os documentos aqui


6
Corrija-me se eu estiver errado - mas não acho que isso funcionará com uma matriz de linhas. Por exemplo, com .findAndcount você teria que mapear as linhas do resultado e retornar .get em cada uma para obter o que você precisa.
backdesk de

Provavelmente mais fácil apenas executar JSON.stringify ou res.json (se você estiver trabalhando no express).
backdesk de

@backdesk - não tentei em um array - mas pelos documentos parece que deve retornar a mesma coisa.
CharlesA de

3
Usar .get()não converterá associações incluídas, use em seu .get({ plain: true })lugar. Obrigado por mencionar a sugestão dos documentos.
Wumms,

Também é útil se você já realizou a consulta e, portanto, não consegue emitirraw: true
Matt Molnar

51

A melhor e mais simples maneira de fazer é:

Basta usar a forma padrão do Sequelize

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

Nota: [options.raw]: Retorna o resultado bruto. Veja sequelize.query para mais informações.


Para o resultado aninhado / se tivermos modelo de inclusão, na versão mais recente do 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);
});

Esta resposta é muito útil, uso a funcionalidade nativa fornecida pelo sequleize, no meu caso quando envio mais de uma linha no socket, ocorre um erro de estouro de pilha.
Ashish Kadam

3
Se você tiver uma matriz de dados filho, obterá resultados errados. Portanto, se a consulta postada retornar apenas um sensorscom uma matriz de filhosomeModel , você obterá um arrayde sensorscom um someModelem cada.
Rahmat Ali

31

Como CharlesA observa em sua resposta, .values()está tecnicamente obsoleto , embora esse fato não seja explicitamente mencionado nos documentos . Se você não quiser usar { raw: true }na consulta, a abordagem preferencial é chamar .get()os resultados.

.get(), entretanto, é um método de uma instância, não de uma matriz. Conforme observado no problema vinculado acima, Sequelize retorna arrays nativos de objetos de instância (e os mantenedores não planejam mudar isso), então você tem que iterar por meio do array você mesmo:

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

Isso funcionou para mim! Embora eu não saiba se 'get' é uma função JavaScript ou Sequelize. Por favor me esclareça. Obrigado
antes de

1
@antew É um método em um objeto retornado do banco de dados - é parte da biblioteca sequelize, não uma função javascript nativa.
Shane Hughes

Levei séculos para começar a trabalhar! Obrigado. Foi tão difícil porque eu tenho muitas inclusões aninhadas, o que também é um bug. Então eu tenho que fazer várias consultas e não pude por causa disso !!!
Karl Taylor de

17

você pode usar a função de mapa. isso funcionou para mim.

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

Obrigado, esta foi a melhor solução para o meu elenco de uso, apenasresults = results.map(el => el.get({ plain: true }))
Joshua Ohana

9

Eu encontrei uma solução que funciona bem para o modelo aninhado e array usando funções nativas de 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
Tão simples, mas funciona poderosamente e mantém a lógica do modelo. Não sei o quão eficiente é, mas foi bom o suficiente para mim. Você pode abreviá-lo para algo como: let res = JSON.parse (JSON.stringify (result));
Artipixel de

9

Aqui está o que estou usando para obter o objeto de resposta simples com valores não stringificados e todas as associações aninhadas da sequelizeconsulta v4.

Com JavaScript simples (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);
};

Com lodash (um pouco mais 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);
};

Uso :

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
Obrigado! O toPlain funcionou conforme o esperado e também resolveu meu problema.
Erhnam

Isso é legal, mas não é bom se você substituir qualquer um dos métodos toJSON do seu modelo. Eu faço muito isso para remover ou converter os valores createdAt / updatedAt. Até agora, a única coisa que funcionou corretamente para mim é JSON.parse (JSON.stringify (resposta)).
hamham,

2

Para texto simples JSON aninhado

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

2
não funciona corretamente para uma associação de muitos, é melhor usar .map (obj => obj.get ({plain: true}))
Manav Kothari

na verdade, estou falando sobre db.model.findOne e um para muitos associação.
Manav Kothari
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.