ethanresnick / json-api

Turn your node app into a JSON API server (http://jsonapi.org/)
GNU Lesser General Public License v3.0
268 stars 41 forks source link

Support inverse relationships? #150

Open nicosefer opened 6 years ago

nicosefer commented 6 years ago

Hi, currently we have 2 models (merchant and contact) which are related in a 1 to n relationship. We are creating contacts with a merchant related, but when we try to GET /merchants, there are not contacts related. Any insight on how to get all the contacts associated with a merchant ?

// merchant.js
var schema = mongoose.Schema({
  contacts: [{ref:'Contact', type: ObjectId}]
// contact.js
var schema = mongoose.Schema({
  merchant: {ref:'Merchant', type: ObjectId}
ethanresnick commented 6 years ago

So, when you create the contact, Mongoose does not automatically update the merchant document to have the contact id in contacts. If you want the merchant document to have a list of contact ids, you'll need to use mongoose middleware to update one document when the other changes. In particular, when a resource is created, the json-api library runs save middleware (so you'll want to put some code there) but, when a resource is updated, the library runs the findOneAndUpdate query middleware, so you'll want to add the synchronization logic there as well.

If you do the above synchronization, you'll have faster reads and the library will take care of GET /merchants?include=contacts for you. If you don't want to do that synchronization, though (as it can be tricky to do reliably), there are other approaches too.

The simplest would be to use mongoose's "populate virtuals" feature, but that unfortunately isn't supported yet by the json-api lib. I'll leave this issue open as a reminder to myself to implement that soon. (Or, a PR would be welcome.)

In the mean time, you can get the same functionality using a query factory to customize the way the library formats the results of the query it generates for GET /merchants request. In your custom function, you can fetch the contacts and include them in the response. This gets a bit verbose, but it's doable (and extractable into a utility if you have a lot of relationships like this).

To show how the query factory version would work, I've added an example here. It adds a principalOf relationship to each person object (analogous to your merchant.contacts relationship), and fills in its contents with many school ids, using the School.principal field from the other side of the relationship, when the user does?include=principalOf.

EmirGluhbegovic commented 6 years ago

Any update regarding this issue and using the mongoose populate functionality? Is there a way to 'catch' the mongo query and attach the populate manually atm? http://mongoosejs.com/docs/populate.html

Alright solved it by using: http://mongoosejs.com/docs/populate.html#populate-virtuals With https://github.com/mongodb-js/mongoose-autopopulate

Makes the document appear as embedded when internally you are handling it as a reference.