Feathers: Buat/perbarui banyak ke banyak dokumentasi hubungan yang diperlukan

Dibuat pada 6 Apr 2018  ·  20Komentar  ·  Sumber: feathersjs/feathers

Halo,

Saat ini saya sedang mengevaluasi Feathers untuk tim kami dan menyukainya. Saya mencoba membahas semua kasus penggunaan kami dan bingung bagaimana menangani pembuatan atau pembaruan asosiasi antara entitas dengan banyak ke banyak hubungan.

Saya ingin meminta untuk menyimpan dan memperbarui contoh bulu hubungan m ke n.

Skenario:
Ada dua layanan /players dan /teams (ini adalah sumber daya arbitrer yang saya pikirkan). Pemain mungkin milik banyak tim dan Tim memiliki banyak pemain. Saya tidak yakin bagaimana menangani pemain yang berhubungan dengan tim dengan cara yang elegan.

Saya ingin memahami bagaimana melakukan hal berikut:

  • Tambahkan pemain ke tim
  • Keluarkan pemain dari tim

Saya dapat mengatur asosiasi dengan baik:
team.model.js

...
  team.associate = function (models) {
    team.belongsToMany(models.player, {through: 'PlayerTeam'});
  };
...

player.model.js

...
  player.associate = function (models) {
    player.belongsToMany(models.team, {through: 'PlayerTeam'});
  };
...
Database Documentation

Komentar yang paling membantu

Hubungan n:m sejauh ini merupakan hubungan yang paling sulit untuk didamaikan di seluruh operasi CRUD. Saya selalu menggunakan "blogposts" dan "tag" contoh klasik n:m . Ada 3 skenario berbeda yang harus Anda pertimbangkan:

  1. Buat/perbarui posting blog hanya dengan tag baru
  2. Buat/perbarui posting blog hanya dengan tag yang ada
  3. Buat/perbarui posting blog dengan kombinasi tag baru dan yang sudah ada

#3 sejauh ini adalah yang paling kompleks; namun, dengan menyelesaikan skenario itu, Anda menyelesaikan 2 yang pertama secara gratis. Inilah cara saya mendekatinya:

  • kendala: sejauh menyangkut klien, mengaitkan tag ke posting blog akan selalu terjadi melalui layanan blogpost (hanya untuk mempermudah). Mari kita asumsikan kita hanya memperbarui satu blogpost dengan payload berikut:
    js { id: 123, title: 'My first blog post', body: '...', tags: [ { id: 111, text: 'tag-1' }, { id: 222, text: 'tag-2' }, { text: 'new-tag' } // new tags will not have an "id" ] }
  • pada layanan blogpost, fokuslah untuk memperbarui blogpost terlebih dahulu - kami akan menyimpan tag untuk nanti. Kita perlu "men-cache" data tag di hook sebelum agar blogpost dapat diperbarui dengan benar:
    js (context) => { if (context.data.tags) { context._tag_data = context.data.tags; delete context.data.tags; } return context; }
  • Di after hook, tangani tag. Pertama-tama kita perlu membuat tag baru:
    js async (context) => { const tags = context._tag_data; if (tags && tags.length) { // tags without an "id" are considered new const existingTags = tags.filter(t => t.hasOwnProperty('id')); const newTags = tags.filter(t => !t.hasOwnProperty('id')); await tagService.create(newTags).then(createdTags => { // update the context._tag_data to contain the existing and newly created tags context._tag_data = existingTags.concat(createdTags) }) } return context; }
  • Terakhir, kaitkan semua tag dengan posting blog:
    js async (context) => { const tags = context._tag_data; if (tags && tags.length) { const blogPostId = hook.result.id; const mappings = tags.map(t => ({ tagId: t.id, blogPostId })); await postTagsService.create(mappings).then(() => { // Put the tags on the final result hook.result.tags = tags; }); } return context; }

Saya tahu itu tampaknya tidak _mudah_, tetapi saya telah menghabiskan banyak waktu untuk memikirkan semua kasus penggunaan dan hal di atas adalah satu-satunya cara saya melihat tentang solusi ramah-pengguna holistik. Saya siap untuk saran, tetapi Anda harus bersaing dengan konvensi kegunaan yang sudah ada (Wordpress, StackOverflow, dll) yang memungkinkan pembuatan dan pengaitan data dalam satu operasi.

Semua 20 komentar

kyle - apakah itu hanya menyelesaikan seperti ini? (contoh)

Terima kasih,

Mark Edwards

Pada Kam, 5 Apr 2018 jam 15:44, Kyle Copeland [email protected]
menulis:

Halo,

Saat ini saya sedang mengevaluasi Feathers untuk tim kami dan menyukainya. saya mencoba untuk
mencakup semua kasus penggunaan kami dan saya bingung bagaimana menangani pembuatan atau
memperbarui asosiasi antara entitas dengan banyak ke banyak hubungan.

Saya ingin meminta untuk menyimpan dan memperbarui bulu hubungan m ke n
contoh.

Skenario:
Ada dua layanan /players dan /teams (ini adalah sumber daya arbitrer
saya pikir). Pemain mungkin termasuk dalam banyak tim dan Tim memiliki banyak
pemain. Saya tidak yakin bagaimana menangani pemain yang mengasosiasikan dengan tim dalam sebuah
busana yang elegan.

Saya ingin memahami bagaimana melakukan hal berikut:

  • Tambahkan pemain ke tim
  • Keluarkan pemain dari tim

Saya dapat mengatur asosiasi dengan baik:
team.model.js

...
team.associate = fungsi (model) {
team.belongsToMany(models.player, {melalui: 'PlayerTeam'});
};
...

player.model.js

...
player.associate = fungsi (model) {
player.belongsToMany(models.team, {melalui: 'PlayerTeam'});
};
...


Anda menerima ini karena Anda berlangganan utas ini.
Balas email ini secara langsung, lihat di GitHub
https://github.com/feathersjs/feathers/issues/852 , atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/ACyd4jo95mdpY5gNYF3GtdghdDwfwG4Uks5tlp40gaJpZM4TJRE4
.

@edwardsmarkf Hai Mark, terima kasih telah menghubungi kami. Saya pikir Anda contoh mungkin telah ditinggalkan.

hai kyle - gambar berasal dari sini:
https://en.wikipedia.org/wiki/Many-to-many_ (data_model) -- mungkin
github mail tidak mengizinkan foto?

saya harap saya mengerti Anda dengan benar.

Terima kasih,

Mark Edwards

Pada Kam, 5 Apr 2018 jam 17:51, Kyle Copeland [email protected]
menulis:

@edwardsmarkf https://github.com/edwardsmarkf Hai Mark, terima kasih untuk
menjangkau. Saya pikir Anda contoh mungkin telah ditinggalkan.


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/feathersjs/feathers/issues/852#issuecomment-379118164 ,
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/ACyd4gMY3kJiprvR3I2fRZP7LThfbvLHks5tlrwDgaJpZM4TJRE4
.

lupa menyebutkan:

https://en.wikipedia.org/wiki/Many-to-many_ (model_data):

Hubungan Penulis-Buku banyak-ke-banyak sebagai pasangan satu-ke-banyak
hubungan dengan tabel persimpangan

bisakah hubungan Anda mendapatkan "tabel persimpangan" di antara mereka? persimpangan
tabel mungkin dapat menampung komentar, tanggal, dll. hanya sebuah pemikiran.

Terima kasih,

Mark Edwards

Pada Kamis, 5 April 2018 pukul 18:04 , Mark Edwards

>

hai kyle - gambar berasal dari sini: https://en.wikipedia.org/wiki
/Banyak-ke-banyak_(data_model) -- mungkin github mail tidak mengizinkan
foto?

saya harap saya mengerti Anda dengan benar.

Terima kasih,

Mark Edwards

Pada Kam, 5 Apr 2018 jam 17:51, Kyle Copeland [email protected]
menulis:

@edwardsmarkf https://github.com/edwardsmarkf Hai Mark, terima kasih untuk
menjangkau. Saya pikir Anda contoh mungkin telah ditinggalkan.


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/feathersjs/feathers/issues/852#issuecomment-379118164 ,
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/ACyd4gMY3kJiprvR3I2fRZP7LThfbvLHks5tlrwDgaJpZM4TJRE4
.

Hei Edward, terima kasih. Menangani meja persimpangan dengan ORM/bulu adalah masalah yang dihadapi.

Inilah tebakan terbaik saya dan bagaimana menyelesaikan masalah dengan Sequelize. Beri tahu saya apa yang Anda semua pikirkan.

app.use('/team/:teamId/player/:playerId', {
    // Gives the ability to add the association
    create(data, params) {
      return app.service('team').Model.findById(params.route.teamId).then(team => {
        return app.service('player').Model.findById(params.route.playerId).then(player => {
          return team.addPlayer(player);
        });
      });
    },

    // Gives the ability to delete an association
    remove(id, params) {
      return app.service('team').Model.findById(params.route.teamId).then(team => {
        return app.service('player').Model.findById(params.route.playerId).then(player => {
          return team.removePlayer(player);
        });
      });
    }
  });

saya ingin tahu apakah ini bisa dijawab dengan lebih baik di https://sequelize.slack.com ? atau menggunakan tampilan saja?

tolong beri tahu saya apa yang Anda putuskan adalah yang terbaik. itu masalah yang sangat menarik.

Layanan yang Anda sarankan sebenarnya cukup rapi. Saya terkadang juga hanya membuat layanan terpisah untuk model tabel gabung. Saya ingin tahu apakah @DesignByOnyx memiliki wawasan di sini dari perjalanannya.

Membuat entri terkait baru harus bekerja dengan meneruskan array ke create bukan?

Terima kasih atas balasan semua orang. @daffl atas saran Anda, Anda akhirnya memiliki layanan pemain tim dan menggabungkan entri melalui kait? Bisakah Anda juga menjelaskan apa yang Anda maksud dengan membuat entri terkait baru melalui array?

@daffl di sini adalah tebakan terbaik saya di layanan vs. ORM

cara berorientasi layanan:

//AFTER HOOK: pseudo-code I haven't tested this

const {result, app} = context;

result.map(async team => {
  const players = await app.service('team-player').find({
      query: {
        teamId: team.id
      }
    }).then(results => {
       return app.service('player').find({
          query: {
            playerId: results.playerId
        });
    });

  team.players = players;
})

return context;

cara ORM:
```
// SEBELUM HOOK: ini berfungsi
context.params.sequelize = {
termasuk: [ {
model: context.app.service('player').Model,
sebagai: 'pemain',
atribut: ['nama'],
melalui: {
atribut: []
}
} ],
mentah: palsu
};

kembali konteks;

kyle - saya akan mengalami masalah yang sama dengan yang Anda hadapi, jadi tolong
beri tahu saya bagaimana ini berhasil untuk Anda.

Terima kasih,

Mark Edwards

Pada Jumat, 6 April 2018 pukul 11:22, Kyle Copeland [email protected]
menulis:

@daffl https://github.com/daffl di sini adalah tebakan terbaik saya di layanan vs. ORM

cara berorientasi layanan:

//AFTER HOOK: pseudo-code Saya belum menguji ini

const {hasil, aplikasi} = konteks;

result.map(tim async => {
const player = menunggu app.service('team-player').find({
permintaan: {
teamId: team.id
}
}).lalu(hasil => {
kembali app.service('player').find({
permintaan: {
playerId: hasil.playerId
});
});

team.players = pemain;
})

kembali konteks;

cara ORM:

// SEBELUM HOOK: ini berfungsi
context.params.sequelize = {
termasuk: [ {
model: context.app.service('player').Model,
sebagai: 'pemain',
atribut: ['nama'],
melalui: {
atribut: []
}
} ],
mentah: palsu
};

kembali konteks;


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/feathersjs/feathers/issues/852#issuecomment-379336187 ,
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/ACyd4hzQdRSLJ5tBm7I_BkOFPDYkyhU7ks5tl7JKgaJpZM4TJRE4
.

Hubungan n:m sejauh ini merupakan hubungan yang paling sulit untuk didamaikan di seluruh operasi CRUD. Saya selalu menggunakan "blogposts" dan "tag" contoh klasik n:m . Ada 3 skenario berbeda yang harus Anda pertimbangkan:

  1. Buat/perbarui posting blog hanya dengan tag baru
  2. Buat/perbarui posting blog hanya dengan tag yang ada
  3. Buat/perbarui posting blog dengan kombinasi tag baru dan yang sudah ada

#3 sejauh ini adalah yang paling kompleks; namun, dengan menyelesaikan skenario itu, Anda menyelesaikan 2 yang pertama secara gratis. Inilah cara saya mendekatinya:

  • kendala: sejauh menyangkut klien, mengaitkan tag ke posting blog akan selalu terjadi melalui layanan blogpost (hanya untuk mempermudah). Mari kita asumsikan kita hanya memperbarui satu blogpost dengan payload berikut:
    js { id: 123, title: 'My first blog post', body: '...', tags: [ { id: 111, text: 'tag-1' }, { id: 222, text: 'tag-2' }, { text: 'new-tag' } // new tags will not have an "id" ] }
  • pada layanan blogpost, fokuslah untuk memperbarui blogpost terlebih dahulu - kami akan menyimpan tag untuk nanti. Kita perlu "men-cache" data tag di hook sebelum agar blogpost dapat diperbarui dengan benar:
    js (context) => { if (context.data.tags) { context._tag_data = context.data.tags; delete context.data.tags; } return context; }
  • Di after hook, tangani tag. Pertama-tama kita perlu membuat tag baru:
    js async (context) => { const tags = context._tag_data; if (tags && tags.length) { // tags without an "id" are considered new const existingTags = tags.filter(t => t.hasOwnProperty('id')); const newTags = tags.filter(t => !t.hasOwnProperty('id')); await tagService.create(newTags).then(createdTags => { // update the context._tag_data to contain the existing and newly created tags context._tag_data = existingTags.concat(createdTags) }) } return context; }
  • Terakhir, kaitkan semua tag dengan posting blog:
    js async (context) => { const tags = context._tag_data; if (tags && tags.length) { const blogPostId = hook.result.id; const mappings = tags.map(t => ({ tagId: t.id, blogPostId })); await postTagsService.create(mappings).then(() => { // Put the tags on the final result hook.result.tags = tags; }); } return context; }

Saya tahu itu tampaknya tidak _mudah_, tetapi saya telah menghabiskan banyak waktu untuk memikirkan semua kasus penggunaan dan hal di atas adalah satu-satunya cara saya melihat tentang solusi ramah-pengguna holistik. Saya siap untuk saran, tetapi Anda harus bersaing dengan konvensi kegunaan yang sudah ada (Wordpress, StackOverflow, dll) yang memungkinkan pembuatan dan pengaitan data dalam satu operasi.

Saya juga berpikir Sequelize membuat ini sedikit lebih sulit daripada yang seharusnya. Metode model tampak intuitif sebagai pengembang tetapi dari perspektif klien API Anda hanya mengirimkan data, tidak memanggil metode apa pun.

Misalnya, tidak mungkin untuk memperbarui atau membuat asosiasi dengan id yang ada atau dengan memberikan daftar id - yang menurut saya sangat kontra-intuitif.

Saya memiliki masalah yang sama dan saya belum menemukan sesuatu yang mudah digunakan dan aman.

Masalah yang sama di sini, terima kasih @DesignByOnyx akan menggunakan pendekatan itu untuk saat ini. Adakah orang lain yang memberikan solusi intuitif untuk ini?

Saya juga mengikuti ini, saat ini ingin menerapkan ini untuk proyek baru minggu ini

@daffl terima kasih atas sarannya, yang berhasil untuk saya saat ini.

Sama seperti catatan kecil, sebenarnya _lebih_ kompleks daripada yang ditunjukkan @DesignByOnyx - Anda mungkin perlu menangani penghapusan juga. Salah satu cara untuk melakukannya adalah dengan mengirim semua tag dengan setiap permintaan pembaruan, dan kemudian menghapus semua tag yang terkait dengan posting sebelum menyimpan semua tag - dengan cara ini Anda yakin telah menyimpan kumpulan tag yang benar.

Kelemahannya adalah Anda melakukan sedikit pekerjaan yang tidak perlu pada setiap pembaruan (menghapus dan menyimpan daftar tag yang berpotensi tidak berubah).

Satu-satunya alternatif yang dapat saya pikirkan adalah membaca hubungan sebelum memperbarui, dan menghitung jumlah minimum [buat, perbarui, hapus] yang diperlukan. Mungkin sepadan jika Anda memiliki banyak tag.

Saya kira ide lain adalah membuat layanan untuk hubungan, mengidentifikasi setiap entri dengan ID dan memanggil secara independen untuk setiap pasangan [posting, tag]. Itu tampaknya tidak efisien dengan permintaan jaringan tetapi secara konseptual sedikit lebih sederhana daripada mencoba melakukan semuanya sekaligus.

class CustomService {
  constructor(options) {
    this.options = options || {};
  }

  /**
   * <strong i="5">@params</strong> {object} data - Sent in from client
   * <strong i="6">@params</strong> {INT} data.building_id - ID of the building from the Building Model
   * <strong i="7">@params</strong> {INT} data.contact_id - ID of the contact from the Contact Model
   */
  create = async (data, params) => {
    if (isNaN(data.building_id)) {
      throw new errors.BadRequest('Building ID MUST be a Number', data);
    }
    if (isNaN(data.contact_id)) {
      throw new errors.BadRequest('Contact ID MUST be a Number', data);
    }

    const building = await this.options.building.get(data.building_id);
    const contact = await this.options.contact.get(data.contact_id);
    const buildingContacts = await building.addContact(contact);

    return buildingContacts;
  };
}

export default app => {
  app.use(
    'api/v1/building_contacts',
    new CustomService({
      building: app.service('api/v1/building'),
      contact: app.service('api/v1/contact')
    })
  );

  const buildingContactsService = app.service('api/v1/building_contacts');

  buildingContactsService.hooks(hooks);
};

Masalah yang saya hadapi adalah jika kontak sudah ada di gedung, saya mendapatkan kesalahan page not found pada tukang pos.
Jika kontak tidak pernah ditambahkan ke gedung maka ini berjalan tanpa masalah.

Ada ide?

  const tags = context._tag_data;
  if (tags && tags.length) {
    const blogPostId = hook.result.id;
    const mappings = tags.map(t => ({ tagId: t.id, blogPostId }));
    await postTagsService.create(mappings).then(() => {
      // Put the tags on the final result
      hook.result.tags = tags;
    });
  }

Apakah ini berarti Anda perlu membuat layanan bulu untuk tabel di antara? Sepertinya layanan yang tidak dibutuhkan. Apakah ada cara lain untuk berinteraksi dengan database di sini?

Layanan "hubungan" diperlukan, dan setiap upaya yang saya lakukan untuk menghindari layanan ketiga tidak membuahkan hasil atau membuat kode lain menjadi sangat sulit. Ini adalah momen "ah ha" terbesar yang saya miliki ketika berurusan dengan hubungan n:m , dan percayalah itu akan membantu Anda juga. Saya sendiri tidak menemukan ide ini - orang telah menulis artikel tentang konsep bahwa "hubungan (mis. tabel gabungan) adalah entitas itu sendiri dan harus diperlakukan seperti itu".

Pikirkan tentang skenario ini - Anda memiliki posting blog dan tag yang ada dan Anda ingin menghubungkan keduanya. Anda tidak membuat atau memperbarui entri blog itu sendiri, dan Anda tidak membuat atau memperbarui tag - Anda hanya mendefinisikan hubungan di antara keduanya. Anda tidak perlu menyentuh layanan blog atau layanan tag - hanya layanan "tag-blog"... yang harus ada agar hal itu terjadi.

Opsi lainnya (lebih berbelit-belit) adalah mencoba menggunakan layanan blog atau layanan tag untuk memperbarui hubungan. Yang mana yang Anda gunakan? Bagaimana Anda menyampaikannya kepada tim Anda? Jika Anda membiarkannya terjadi dua arah, sekarang Anda memiliki dua kali kode untuk dipelihara serta model mental yang sulit. Semua masalah ini hilang jika Anda memiliki layanan ke-3 untuk hubungan itu sendiri.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

eric-burel picture eric-burel  ·  3Komentar

codeus-de picture codeus-de  ·  4Komentar

perminder-klair picture perminder-klair  ·  3Komentar

harrytang picture harrytang  ·  3Komentar

rstegg picture rstegg  ·  3Komentar