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


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.


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

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


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

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

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

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',
//     }]
//   }]
// }]

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

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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.