Skip to content
New issue

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

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

Already on GitHub? # to your account

Creating a batchloader for belongsToMany relationships #6

Closed
MrSnoozles opened this issue Nov 22, 2018 · 6 comments
Closed

Creating a batchloader for belongsToMany relationships #6

MrSnoozles opened this issue Nov 22, 2018 · 6 comments

Comments

@MrSnoozles
Copy link

Hello,

this is not a bug, but maybe the documentation could be expanded upon as I could find anything. In my app I have tasks and users. Each tasks is assigned to several users and a user can have several tasks. So we have a belongsToMany (n:m) relationship on both sides.

Does batchloading work for this scenario? I'm a bit clueless how to get it working in that case. is there an example somewhere I could get inspiration from?

Regards

@eddyystop
Copy link
Collaborator

feathers-plus/cli-generator-example has several many-to-many entities. You can look at that.

If your many-to-many file contains { a, b } and a base record contains 'a' then you would join all the 'a' records to in { a, b } to the base record, and for such { a, b } you would join whatever record 'b' referes to.

@eddyystop
Copy link
Collaborator

Reopen this if you have more questions.

@MrSnoozles
Copy link
Author

MrSnoozles commented Nov 23, 2018

Thank you for the reply. I found the examples quite confusing though, mainly because I'm new to Feathers, don't understand all the concepts yet and the code in those examples is so different from whats written in the BatchLoader guide. E.g. in the examples a feathersBatchLoader function is used and I have no idea where it's coming from.

However after spending several hours more today, I managed to get something working which is probably terrible. :) Instead of using the loaderFactory I created a custom BatchLoader which looks like follows:

const BatchLoader = require('@feathers-plus/batch-loader');
const { loaderFactory, getUniqueKeys } = BatchLoader;
const { omit } = require('lodash')

context._loaders.assignees.task_id = new BatchLoader(async (keys, context) => {
    // batch loader function for belongsToMany task <-> assignee relationship
    const taskModel = context.service.Model;
    const users = context.app.service('users');

    const assignees = await users.Model.findAll({
        include: [{
            model: taskModel,
            as: 'tasks',
            where: {
                id: {
                    $in: getUniqueKeys(keys)
                }
            },
        }],
        raw: false
    });

    // prepare an array of same length as keys
    var result = new Array(keys.length).fill(null).map(() => []);

    assignees.forEach((user) => {
        user.tasks.forEach((task) => {
            let idx = keys.indexOf(task.id);

            // check if user already exists
            if(result[idx].map(user => user.id).indexOf(user.id) === -1) {
                
                // create new user object and delete tasks
                // so in the final output all user tasks are not displayed
                // again (avoid circular structure)
                var newUser = JSON.parse(JSON.stringify(user));
                newUser = omit(newUser, ['tasks']);

                // add to result
                result[idx].push(newUser);
            }
        });
    });

    return result;

}, {context});

@eddyystop
Copy link
Collaborator

loaderFactory is just a convenience method handling the most common case. It's also useful in making examples easier to understand.

No need to use it if your needs differ.

@eddyystop
Copy link
Collaborator

You implementation is a good example that batchloaders can be as complicated as necessary.

@MrSnoozles
Copy link
Author

MrSnoozles commented Nov 23, 2018

Do you see anything obvious that could be improved upon in the example? There are a few things that feel ugly-ish to me, mainly:

  • using the Sequelize model directly instead of the Feathers service for querying
  • everything after // prepare an array of same length as keys. I wanted to use the getResultsByKey helper but couldn't get it to work

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

No branches or pull requests

2 participants