Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

$populate does not seem to work #89

Closed
garrypolley opened this issue Feb 14, 2017 · 9 comments
Closed

$populate does not seem to work #89

garrypolley opened this issue Feb 14, 2017 · 9 comments

Comments

@garrypolley
Copy link

Steps to reproduce

Create a linked relationship between two models

Expected behavior

When I query a basic endpoint I expect the $populate parameter to include my related model.

Actual behavior

The related model is not included in my results. The include never gets sent to sequelize and therefore the inner join is never made.

@daffl
Copy link
Member

daffl commented Feb 14, 2017

Does Sequelize support $populate? I thought that was a Mongoose thing.

@garrypolley
Copy link
Author

They support an include which does about the same thing. So if I have hasMany relationship, I can add a:

.findAll({ include: 'MyManyModel' })

And it'll add the nested model via an inner join. I was assuming that a $populate would drop the value into include.

@garrypolley
Copy link
Author

For further clarification --> http://docs.sequelizejs.com/en/latest/docs/models-usage/?highlight=include#including-everything

I think it'd be possible to do something like what's below, with maybe a bit more logic to use the proper model name/instance in the include.

return this.Model.findAndCount(q).then(result => {
      return {
        include: filters.$populate, // more logic may be needed to refer to the model by name. 
        total: result.count,
        limit: filters.$limit,
        skip: filters.$skip || 0,
        data: result.rows
      };
    }).catch(utils.errorHandler);

@daffl
Copy link
Member

daffl commented Feb 14, 2017

Yes, you can do that by setting params.sequelize in a hook:

app.service('sequelizeService').before({
  find(hook) {
    hook.params.sequelize = {
      include: hook.params.query.$populate
    }
  }
});

@garrypolley
Copy link
Author

Thanks! That works with me doing a hooks.app.get('models')[hooks.params.query.$populate]

It'd be nice if it "just worked" out of the box though and not requiring a hook.

I suppose I could add a "global" hook, but those tend to look a little weird/hacky in implementation if I recall.

@daffl
Copy link
Member

daffl commented Feb 14, 2017

Not anymore, you can use app.hooks.

@garrypolley
Copy link
Author

Awesome! That's exactly what I'd be happy with for now. Thanks for the help!

@L3V147H4N
Copy link

L3V147H4N commented May 15, 2017

This does not work for me...

this.UsersService.find({
      query: {
        $sort: {
          name: 1
        },
        $populate: [
          {
            model: 'Role', as: 'roles'
          }
        ]
      }
    })
`

throws self.$expandAttributes is not a function

@sarkistlt
Copy link

sarkistlt commented Feb 28, 2018

@L3V147H4N it won't work because 'Role' is string not a model, I'm using this hook:

function resolveModels(arr, models) {
  const include = [];

  arr.forEach((entity) => {
    if (typeof entity === 'string') {
      include.push(entity);
    } else if (typeof entity === 'object') {
      if (entity.model && typeof models[entity.model] === 'function') {
        entity.model = models[entity.model];
      }
      if (entity.include && Array.isArray(entity.include)) {
        entity.include = resolveModels(entity.include, models);
      }
      include.push(entity);
    }
  });

  return include;
}

export default function sequalizePopulateHook(ctx) {
  if (
    ctx.params.query &&
    ctx.params.query.$populate &&
    Array.isArray(ctx.params.query.$populate)
  ) {
    const { models } = ctx.app.get('sequelize');

    ctx.params.sequelize = ctx.params.sequelize || {};
    ctx.params.sequelize.include = resolveModels(ctx.params.query.$populate, models);
    ctx.params.sequelize.raw = false;

    delete ctx.params.query.$populate;
  }
  return ctx;
}

it will resolve model from string in:

$populate: [
  {
    model: 'Role', as: 'roles'
  }
]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants