0

I have these tables:

  1. Users
  2. Skills
  3. Has_skills (user_id, skill_id)

I'm passing an array of skill IDs to the function which should fetch users who have at least one of those skills. The query should be efficient in a way so it fetches a number of users (limit, range or in other words pagination functionality) but not doing the range starting from user with ID 0 and then going upwards, but the range starting from the user with most matched skills to the least.

So how can the query sort the records by the number of matched skills from most matched skills to least so I can add pagination based on those results? I assume I should additionaly tweak the modifyEager on has_skills and count it, and then implement the range for pagination but I am not entirely sure how to do that. So ultimately, this is what I need to add:

  1. The query should first do the ordering / sorting of the records by the number of matched skills
  2. The above condition should be limited by a number of users or range for pagination purpose and better performance

This is my function:

async function getUsersWithPassedSkillIds({ skillIds }) {
    const users = await User.query()
        .select('users.id', 'users.name')
        .joinEager('has_skills')
        .modifyEager('has_skills', builder => builder.select('id', 'name'))
        .whereIn('has_skills.id', skillIds)

    return users
}
krcky
  • 81
  • 2
  • 11

1 Answers1

1

The author of Objection.js helped me make this work!

// Fetch the users who have at least one matching skill
const hasSkillsSubquery = User.relatedQuery("skills").whereIn(
  "has_skills.skill_id",
  skillIds
);

const users = await User.query()
  .select("users.id", "users.name")
  // Use .eager instead of .joinEager as pagination doesn't work with it due to joins.
  .eager("skills")
  // Optional: Populating the matched skills
  .modifyEager("skills", builder =>
    builder.select("skills.id", "skills.name").whereIn("skills.id", skillIds)
  )
  // Only taking into account users who have at least 1 matched skill
  .whereExists(hasSkillsSubquery.clone())
  // Sorting users by matched skills
  .orderByRaw("(?) DESC", hasSkillsSubquery.clone().count())
  // This would return the user with the most matched skills.
  // If you want to fetch 10 users ordered by number of matching skills: .range(0, 9)
  .range(0, 0);
krcky
  • 81
  • 2
  • 11