Sequelize, 엔티티를 일반 객체로 변환


97

저는 자바 스크립트에 익숙하지 않고 놀랍습니다. 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);당신의 트릭 라인이 말하기 전에 무엇을 말합니까?
Kevin Reilly

업데이트 후를 참조하십시오.
Ruslan

답변:


43

내가 맞다면 sensors컬렉션을 node. 두 모델간에 매핑이있는 경우 여기에 설명include기능을 사용 하거나 모든 인스턴스에 정의 된 getter를 사용할 수 있습니다 . 여기 에서 문서 찾을 수 있습니다 .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 ​​:)
Ruslan

150

쿼리 옵션 {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,
})

33
"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.
1valdis 2010 년

raw:true전역 설정 으로 전달하는 방법을 알고 있으므로 모든 쿼리에 전달할 필요가 없습니까? 감사합니다
codebot

111

이 질문을 더 최근 .values에 접한 사람들을 위해 Sequelize 3.0.0에서 더 이상 사용되지 않습니다. .get()대신 사용 하여 일반 자바 스크립트 개체를 가져옵니다. 따라서 위의 코드는 다음과 같이 변경됩니다.

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

여기에서 문서 속편


6
내가 틀렸다면 정정하십시오-그러나 이것이 행 배열에서 작동하지 않을 것이라고 생각합니까? 예를 들어 .findAndcount를 사용하면 결과 행을 .map하고 필요한 것을 얻기 위해 각각에 대해 .get을 반환해야합니다.
backdesk

아마도 JSON.stringify 또는 res.json을 실행하는 것이 더 쉬울 것입니다 (익스프레스 내에서 작업하는 경우).
backdesk

@backdesk-배열에서 시도하지 않았지만 문서 에서 동일한 것을 반환 해야하는 것처럼 보입니다.
CharlesA

3
를 사용 .get()하면 포함 된 연결이 변환되지 .get({ plain: true })않고 대신 사용 됩니다. 문서의 제안을 언급 해 주셔서 감사합니다.
Wumms

또한 당신은 이미 쿼리를 유용하게 수행 한 경우 따라서 발행 할 수 없습니다raw: true
매트 몰 나르

50

가장 좋고 간단한 방법은 다음과 같습니다.

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에서 제공하는 기본 기능을 사용합니다. 제 경우에는 소켓에서 두 개 이상의 행을 보낼 때 스택 오버플로 오류가 발생했습니다.
Ashish Kadam

2
하위 데이터 배열이있는 경우 잘못된 결과가 표시됩니다. 따라서 게시 된 쿼리가 sensorschild 배열이있는 하나만 반환 someModel하면 각각에 하나씩 arrayof sensorssomeModel표시됩니다.
Rahmat Ali

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 2018

1
@antew 데이터베이스에서 반환 된 객체에 대한 메서드입니다. 네이티브 자바 스크립트 함수가 아닌 sequelize 라이브러리의 일부입니다.
Shane Hughes

이 작업을 수행하는 데 몇 년이 걸렸습니다! 감사합니다. 중첩 된 포함이 많기 때문에 너무 어려웠고 버그이기도합니다. 그래서 여러 쿼리를해야하는데 이것 때문에 할 수 없었습니다 !!!
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 }))
Joshua Ohana

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

다음은 sequelizev4 쿼리 에서 문자열 화되지 않은 값과 모든 중첩 된 연결이있는 일반 응답 개체를 가져 오는 데 사용하는 것 입니다.

일반 자바 스크립트 (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 dec

이것은 깔끔하지만 모델의 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}))을 사용하는 것이 더 좋습니다.
Manav Kothari

실제로 나는 db.model.findOne과 일대 다 연관에 대해 이야기하고 있습니다.
Manav Kothari
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.