1

I am working on a keystonejs project here and am running into a problem with relationships between two models.

I have the below two models:

User model:

User.add({
    name: { type: Types.Name, required: true, index: true },
    email: { type: Types.Email, initial: true, required: true, index: true },
  number: { type: Types.Number, index: true },
    password: { type: Types.Password, initial: true, required: true }
}, 'Permissions', {
    isAdmin: { type: Boolean, label: 'Can access Keystone', index: true },
}, 'Groups', {
    groups: {type: Types.Relationship, ref: 'Group', many: true }
});

And I have a Group model

Group.add({
    name: { type: String, required: true, index: true },
    description: { type: String, initial: true, required: true, index: true}

});

I have a aggregate function that basically pulls all of the groups that have users in it

    User.model.aggregate({$group:{'_id':'$groups'}}).exec(function(err,data){
                console.log(data);
});

My problem is that the above aggregate only shows me my groups by their _id values and not their names.

How could I populate and show the names in the front end? Obviously the id's are necessary on the back end to reference but to the front end users that won't work.

Thanks

Dani Cela
  • 45
  • 5
  • 1
    That's not possible because you're trying to do an aggregate on Users collection.Users collection doesn't have the name field of Group collection, and [MongoDb doesn't allow joins](https://docs.mongodb.org/manual/reference/database-references/#dbref-explanation). To do that you can include the name field on Users Collection, or try using [DBRefs](http://stackoverflow.com/questions/26067955/resolving-mongodb-dbref-array-using-mongo-native-query-and-working-on-the-resolv) – hecnabae Nov 18 '15 at 09:23
  • So based on what I understand, it would just be easier to just combine these models together. My issue is i still want to be able to create groups from within the interface. I have read a bit about what you mentioned along with other methods of making this but so far have not been able to comprehend how to complete this. Do you have any other recommendations on how i could combine these two while still maintaining the ability to add groups via the admin interface? – Dani Cela Nov 18 '15 at 21:02
  • I think that it will be easier on application level. Anyway, if you haven't the possibility, another approach you can use is the [map-and-reduce](https://docs.mongodb.org/manual/core/map-reduce/). Some examples: [Multiple collections into one collection](http://stackoverflow.com/questions/5681851/mongodb-combine-data-from-multiple-collections-into-one-how) -- [Merging 2 collections](http://stackoverflow.com/questions/5681851/mongodb-combine-data-from-multiple-collections-into-one-how) – hecnabae Nov 19 '15 at 21:19

1 Answers1

2

You can create what you want pretty easily so don't be discouraged. You just need a heads up on the '.populate()' function in mongoose. See example below.

User Model (slightly tidied - I removed the (strange?) nesting)

User.add({
    name: { type: Types.Name, required: true, index: true },
    email: { type: Types.Email, initial: true, required: true, index: true},
    number: { type: Types.Number, index: true },
    password: { type: Types.Password, initial: true, required: true },
    isAdmin: { type: Boolean, label: 'Can access Keystone', index: true },
    groups: {type: Types.Relationship, ref: 'Group', many: true }
});

Group Model -- note the Group.relationship({}) option I've added near the bottom for admin convenience (shows users which reference that group on the backend)

Group.add({
    name: { type: String, required: true, index: true },
    description: { type: String, initial: true, required: true, index: true}    
});

Group.relationship({ ref: 'User', path: 'users', refPath:'Groups'});

Controller getting a list of users with all their corresponding group information

 view.on('init', function(next) {
    keystone.list('User').model.find()
    .populate('groups')
    .exec(function(err, users) {
      if(err) {
        console.log(err);
        return next(err);
      } else {
        locals.data.users = users;
        next(err);
      }
    });
  });

Controller getting Users within a specific group to display on the frontend (you need the group ID first)

  view.on('init', function(next) {
    keystone.list('User').model.find()
    .where('Groups').in([array, of, group, ids])
    .populate('groups')
    .exec(function(err, users) {
      if(err) {
        console.log(err);
        return next(err);
      } else {
        locals.data.users = users;
        next(err);
      }
    });
  });

locals.data.users would return like this in each case:

[
  {
    _id: '',
    name: '',
    ...
    groups: [
      {
        _id: '',
        name: ''
        ...
      }
    ]
  }
]