Converting large number of database results into array of objects in javascript

时间:2019-04-17 02:11:57

标签: javascript node.js

I am pulling a database query that has the following info:

id, name, roleId, roleTitle

In the query, I am pulling for users and their roles. Each user can have 0 to N number of roles. I want to in the end have an object like this:

{
    id
    name
    roles: [{
       id
       title
    }]
}

What would the most efficient way of doing this be? Currently I am doing something like this:

const data = [];
arr.forEach((u) => {
   const index = data.findIndex(x => x.id === u.id);
    if (index >= 0) {
      data[index].roles.push({ id: u.roleId, title: u.roleTitle });
    } else {
      data.push({
        id: u.id,
        name: u.name,
        roles: u.roleId ? [{
          id: u.roleId,
          title: u.roleTitle,
        }] : [],
      });
    }
}

This solution works correctly but wasn't sure if this was the fastest way to get this done if we scale the user numbers to 10k with an average role per user of 3 or 50k and 5 roles per user

3 个答案:

答案 0 :(得分:2)

您最好的选择实际上是在SQL中完成所有操作,因为您将PostgreSQL用于数据库(如注释中所述)。我不知道您的表和列的确切名称,因此您可能需要对其进行调整,但这将为您提供所需的信息:

SELECT json_agg(t)
FROM (
  SELECT
    u.id,
    u.name,
    ro.roles
  FROM "user" u
  LEFT JOIN (
    SELECT
      ur.user_id,
      json_agg(
        json_build_object(
          'id', r.id,
          'title', r.title
        )
      ) AS roles
    FROM user_role ur
    LEFT JOIN "role" r ON r.id = ur.role_id
    GROUP BY ur.user_id
  ) ro ON ro.user_id = u.id
) t;

SQL小提琴:http://www.sqlfiddle.com/#!17/5f6ca/11

说明

json_build_object将使用指定的名称/值对创建一个对象,因此:

json_build_object(
  'id', r.id,
  'title', r.title
)

将角色idtitle组合成这样的JSON对象:

{id: 1, title: "Title 1"}

json_agg将多个行聚合到一个JSON数组中,因此它将上面的角色对象转换为一个列,该列是每个用户的角色对象的数组(由于内部子查询的GROUP BY u.id部分)。内部子查询为我们提供了这样的结果集(每个用户一行)

| user_id |                       roles                          |
|---------|------------------------------------------------------|
|    1    | [{id: 1, title: "Role 1"}, {id: 2, title: "Role 2"}] |

然后将子查询联接到用户表,所有这些都包装在另一个子查询中,因此json_agg可以用于整个结果,并返回单个json对象,该对象是具有角色的用户数组。

答案 1 :(得分:1)

This almost certainly isn't the most efficient possible version but is faster than what you're doing now:

const data = Object.values(arr.reduce((obj, {id, name, roleId, roleTitle}) => {
  if (!(id in obj)) {
    obj[id] = {
      id,
      name,
      roles: {},
    };
  }
  if (!obj[id].roles[roleId]) {
    obj[id].roles[roleId] = {
      id: roleId,
      title: roleTitle,
    };
  }
  return obj;
}, {}));

By using objects (hashes) instead of arrays, determining if the user is already there or if the user already has a role is a constant-time O(1) operation (the cost of the hashing function). But searching an array, depending on the search method used, is linear in the worst case O(n) and even the best case is O(log n).

You could go down the rabbit hole of micro-optimizations that will change with the wind, but choosing the correct data structures and algorithms will usually get you the most bang for your optimization buck.

I've used Object.values to convert back to an array at the end, if you omit this and just stick with objects it could be even faster.

答案 2 :(得分:0)

希望这会有所帮助。

var modified_array = function(xs, key) {
  return xs.reduce(function(rv, x) {
    obj = (rv[x[key]] = rv[x[key]] || {});
    obj.id = x.id;
    obj.name = x.name;
    obj.roles = obj.roles || []
    obj.roles.push({ id: x.roleId, title: x.roleTitle})
    return rv;
  }, {});
};

arr = [{id:1,name:"abd",roleId: 10,roleTitle: "hello"},
       {id:1, name: "abd", roleId: 15,roleTitle: "fello"}]    
console.log( Object.values(modified_array(arr, 'id')));