Sequelize, konwertuj jednostkę na zwykły obiekt

100

Nie jestem zbyt zaznajomiony z JavaScriptem i oszałamiającym, ponieważ nie mogę dodać nowej właściwości do obiektu, który został pobrany z bazy danych przy użyciu nazw ORM Sequelize.js.

Aby tego uniknąć, używam tego hacka:

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

Więc jaki jest normalny sposób dodawania nowych właściwości do obiektu.

Jeśli to pomoże, używam sequelize-postgres w wersji 2.0.x.

upd. console.log (węzeł):

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

Myślę, że teraz myślisz, że będzie to: „Ok, to proste, po prostu dodaj swoją właściwość do dataValues”.

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

Dodam te linie i to nie działa

Rusłan
źródło
nodenie może być przedmiotem tradycyjnym. Może to jest bufor? Co mówi console.log(node);przed twoją linią tricków?
Kevin Reilly
Proszę zobaczyć po aktualizacji.
Ruslan,

Odpowiedzi:

43

Jeśli dobrze rozumiem, chcesz dodać sensorskolekcję do pliku node. Jeśli masz odwzorowanie między oboma modelami, możesz użyć includefunkcji wyjaśnionej tutaj lub metody valuespobierającej zdefiniowanej w każdej instancji. Możesz znaleźć odpowiednie dokumenty tutaj .

Ten ostatni może być używany w następujący sposób:

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

Jest szansa, że nodedata.sensors = sensorsto też się uda.

sdepold
źródło
2
Dziękuję, wszystko, czego potrzebuję: node.values ​​:)
Ruslan
158

możesz użyć opcji zapytania, {raw: true}aby zwrócić nieprzetworzony wynik. Twoje zapytanie powinno wyglądać następująco:

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

również jeśli masz z includetym skojarzenia , zostanie spłaszczony. Możemy więc użyć innego parametrunest:true

db.Sensors.findAll({
  where: {
    nodeid: node.nodeid
  },
  raw: true,
  nest: true,
})
lutaoact
źródło
35
"raw: true" w jakiś sposób spłaszcza tablicę wstrzykniętą przez powiązane modele. Jedyne obejście, jakie do tej pory znalazłem, to configs = JSON.parse (JSON.stringify (configs));
Soichi Hayashi,
1
To proste i lepsze podejście niż zaakceptowana odpowiedź. dzięki
pyprism
1
@SoichiHayashi Właściwie to odwrotnie, kiedy o tym myślisz ...;) Bez surowego: prawda, Sequelize w jakiś sposób rozkłada wynik na obiekty. Wyniki zapytania sql są już spłaszczone. Kilka razy uznałem, że przydatne jest uzyskanie płaskiego wyniku ... +1 dla tej odpowiedzi.
PRS
4
@SoichiHayashi, aby uzyskać surowy, ale zagnieżdżony wynik, możesz użyć nest: truerazem z raw: true.
1valdis
raw:truemasz pomysł, jak przekazać to ustawienie globalne, więc nie ma potrzeby przekazywania go do każdego zapytania? Dzięki
codebot
112

Dla tych, którzy ostatnio .valuesnapotkali to pytanie, jest on przestarzały od wersji Sequelize 3.0.0. Użyj .get()zamiast tego, aby uzyskać zwykły obiekt javascript. Więc powyższy kod zmieniłby się na:

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

Sekwencjonuj dokumenty tutaj

Karol A.
źródło
6
Popraw mnie, jeśli się mylę - ale nie sądzę, że to zadziała z tablicą wierszy? Na przykład z .findAndcount musiałbyś .mapować wiersze wyników i zwracać .get na każdym, aby uzyskać to, czego potrzebujesz.
backdesk
Prawdopodobnie łatwiej jest po prostu uruchomić JSON.stringify lub res.json (jeśli pracujesz w express).
backdesk
@backdesk - nie próbowałem tego na tablicy - ale z dokumentacji wygląda na to, że powinien zwrócić to samo.
Charles A,
3
Użycie .get()nie spowoduje konwersji dołączonych skojarzeń, .get({ plain: true })zamiast tego użyj . Dzięki za wspomnienie sugestii od doktorów.
Wumms,
Przydatne również, jeśli już wykonałeś zapytanie i nie możesz go wydaćraw: true
Matt Molnar
51

Najlepszym i najprostszym sposobem jest:

Po prostu użyj domyślnego sposobu z Sequelize

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

Uwaga: [options.raw]: zwraca nieprzetworzony wynik. Aby uzyskać więcej informacji, zobacz sequelize.query.


Dla zagnieżdżonego wyniku / jeśli mamy uwzględniony model, W najnowszej wersji 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
źródło
Ta odpowiedź jest bardzo przydatna, użyj natywnej funkcjonalności dostarczonej przez sequleize, w moim przypadku, gdy wysyłam więcej niż jeden wiersz w gnieździe, wystąpił błąd przepełnienia stosu.
Ashish Kadam
3
Jeśli masz tablicę danych dziecka, otrzymasz nieprawidłowe wyniki. Tak więc, jeśli wysłane zapytanie zwróci tylko jedno sensorsz tablicą podrzędną someModel, otrzymasz arrayof sensorsz jednym someModelw każdym.
Rahmat Ali
31

Jak zauważa CharlesA w swojej odpowiedzi, .values()jest technicznie przestarzała , chociaż fakt ten nie jest wyraźnie odnotowany w dokumentacji . Jeśli nie chcesz używać { raw: true }w zapytaniu, preferowanym podejściem jest wywołanie .get()wyników.

.get()jednakże jest metodą instancji, a nie tablicy. Jak wspomniano w powiązanym problemie powyżej, Sequelize zwraca natywne tablice obiektów instancji (a opiekunowie nie planują tego zmieniać), więc musisz samodzielnie iterować po tablicy:

db.Sensors.findAll({
    where: {
        nodeid: node.nodeid
    }
}).success((sensors) => {
    const nodeData = sensors.map((node) => node.get({ plain: true }));
});
Shane Hughes
źródło
To zadziałało dla mnie! Chociaż nie wiem, czy „get” to JavaScript czy funkcja Sequelize. Proszę, oświeć mnie. Dzięki
antew
1
@antew Jest to metoda na obiekcie zwróconym z bazy danych - jest częścią biblioteki sequelize, a nie natywną funkcją javascript.
Shane Hughes
Zajęło mi to wieki, zanim przystąpiłem do tej pracy! Dziękuję Ci. Było to trudne, ponieważ mam dużo zagnieżdżonych dołączeń, co jest również błędem. Więc muszę zrobić wiele zapytań i nie mogłem z tego powodu !!!
Karl Taylor,
17

Możesz użyć funkcji mapy. to działa dla mnie.

db.Sensors
    .findAll({
        where: { nodeid: node.nodeid }
     })
    .map(el => el.get({ plain: true }))
    .then((rows)=>{
        response.json( rows )
     });
nur zazin
źródło
Dzięki temu było to najlepsze rozwiązanie dla mojej obsady, po prosturesults = results.map(el => el.get({ plain: true }))
Joshua Ohana,
10

Oto, czego używam, aby uzyskać zwykły obiekt odpowiedzi z wartościami niezredagowanymi i wszystkimi zagnieżdżonymi skojarzeniami z sequelizezapytania v4.

Ze zwykłym JavaScriptem (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);
};

Z lodaszkiem (trochę bardziej zwięźle):

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

Użycie :

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',
//     }]
//   }]
// }]
nawet mróz
źródło
1
Dzięki! ToPlain działa zgodnie z oczekiwaniami i rozwiązał również mój problem.
Erhnam
To fajne, ale nie jest dobrze, jeśli zastąpisz którąkolwiek z metod toJSON modelu. Robię to dość często, aby usunąć lub przekonwertować wartości createdAt / updatedAt. Jak dotąd jedyną rzeczą, która działała poprawnie, jest JSON.parse (JSON.stringify (odpowiedź)).
Hamham
Twoja odpowiedź skłoniła mnie do głębszego przyjrzenia się dokumentowi Modelki i JSON - owi . Po naprawieniu niestandardowego gettera w mojej definicji modelu, toJSONdziałało dobrze, więc nie potrzebowałem niestandardowej implementacji, takiej jak Twoja.
Michał Čizmazia
9

Znalazłem rozwiązanie, które działa dobrze w przypadku modelu zagnieżdżonego i tablicy przy użyciu natywnych funkcji 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
Suben Saha
źródło
1
Tak prosty, ale potężny działa i zachowuje logikę modelu. Nie wiem, na ile jest skuteczny, ale dla mnie wystarczył. Możesz go skrócić do czegoś takiego jak: let res = JSON.parse (JSON.stringify (wynik));
Artipixel
2

Dla zagnieżdżonego zwykłego tekstu JSON

db.model.findAll({
  raw : true ,
  nest : true
})
Amr Ibrahim
źródło
2
nie działa poprawnie dla jednego do wielu skojarzeń, lepiej jest użyć .map (obj => obj.get ({zwykły: prawda}))
Manav Kothari
właściwie mówię o db.model.findOne i jeden do wielu skojarzeń.
Manav Kothari