When it's about many-to-many relations in DB

I’m mainly Ruby on Rails user and need to make a SPA and I’m going to deal with many-to-many relations database.

My understanding MongoDB is good when it’s about one-to-many relations only and when it comes to many-to-many relations, I need to use RDBS, otherwise I would give a try meteor.

Please am I correct with this or not?

Mongo, especially Meteor with Mongo, does not provide any out of box solution to either type of relations.

Be it one to many, or many to many, you will need to mimic the necessary schema structure on your collections and then handle the actual resolving of the relations within your application logic.

Trivializing the problem of an n:n relationship to a 1:n relationship perspective would be to look at it as if it were a 1:n relationship that maps to a cursor/array of 1:n relationships.

Therefore, if you’ve already grasped how you’d do 1:n, you could just as easily extend that to an n:n

In order to achieve the necessary persistence structure, the easiest way would be to create a join collection.

2 Likes

Yes, I know MongoDB doesn’t support relations because it’s noSQL. :wink:

I was wondering about solution on this regard which is explained it can be done by creating a join collection.

My understanding (and I’m confident), mongodb supports 1:n relations which is nested (embedded) document.

Thanks for reply. Going to dig further. :smile:

mongodb supports 1:n relations which is nested (embedded) document.

Unfortunately, that’s one area where Meteor limits you. Meteor’s livedata package diffs collection changes on the topmost fields of a document, therefore if you embed another document, the changes to those documents will not be equipped with fine grained reactivity. Furthermore, any change to any embedded document will invalidate the parent collection’s computations.

Therefore, in Meteor world, you do 1:n relationships with (app based) joins.

For that, you can make use of packages such as dburles:collection-helpers and reywood:publish-composite

A very basic example would be:

Common.js

// Let's create the collection on both the server and the client
Posts = new Mongo.Collection('posts');
Comments = new Mongo.Collection('comments');

// Now let's define some helpers (transforms) that fetch the related documents
Posts.helpers({
  // Join the comments cursor to the post
  comments: function() {
    return Comments.find({postId: this._id})
  },
  // Join the user profile on the poster's user id
  authorName: function() {
    var userProfile = Meteor.users.findOne({_id: this.posterUserId}).profile;
    return userProfile.name + ' ' + userProfile.lastName;
  }
});

Server.js

// Let's publish all the posts as well as their comments
Meteor.publishComposite('postsWithComments', function() {
  return {
    find: function() {
      return Posts.find();
    },
    children: [
      {
        find: function(post) {
          return post.comments();
        }
      }
    ]
  };
});

// Let's insert a dummy post and a dummy comment, both from the same user
Meteor.startup(function() {
  postId = Posts.insert({title: 'My first post', content: 'Some awesome post content', posterUserId: Meteor.userId()});
  Comments.insert({postId: postId, body: 'Congrats to myself on my first post.', commenterUserId: Meteor.userId()})
});

Client.js

// Let's subscribe to the publication
Meteor.subscribe('postsWithComments');

Having declared these, we should be getting

Browser console

Console.log(Posts.findOne().title) //=> My first post
Console.log(Posts.findOne().comments().fetch()[0]) //=> Congrats to...
Console.log(Posts.findOne().authorName) //=> First and last name of the user

We could also have published user data, joined the comment author name and published that as well etc. But I’ll leave that to you as exercise :wink:

1 Like

Truly speaking, I’m a bit shocked :slight_smile: I didn’t know about that…

But I see (fortunately) this can be solved you explained.

I believe, in practice, most (if not all) users need realtime changes on the browsers for embedded documents as well, correct?

Well, as much as it sounds to be a shortcoming, I can understand why MDG has decided to leave out livedata diffing on subdocument level. It is a performance issue and would quickly get very complicated and unmanageable if implemented.

This has been like that since the beginning of Meteor and nobody had a hard time adapting their schema designs to that paradigm :smile:

In fact, due to a storage subsystem constraint, mongodb itself (up until v3) was advising against using subdocuments where the number of subdocuments would grow indefinitely. So practically, meteor’s implementation choices had not made too much of a negative difference.

So, in short, it’s possible to show realtime changes for the post.comments with the method you showed previously, am I correct?

Yes, the example I gave above is reactive.

1 Like