简体   繁体   English

如何在Node.js中高效/快速地执行数组连接,类似于MongoDB $ lookup?

[英]How to perform array join in Node.js efficiently/fast similar to MongoDB $lookup?

I want to perform a $lookup in Node.js similar to $lookup aggreation from MongoDB. 我想在Node.js中执行$ lookup,类似于MongoDB的$ lookup aggreation。

I have a solution but I'm not sure how fast it performs with more objects in each of the two arrays or with bigger objects. 我有一个解决方案,但我不确定它在两个数组中的每一个或更大的对象中使用更多对象的速度有多快。

let users = [
    {userId: 1, name: 'Mike'}, 
    {userId: 2, name: 'John'}
    ]
let comments = [
    {userId: 1, text: 'Hello'}, 
    {userId: 1, text: 'Hi'}, 
    {userId: 2, text: 'Hello'}
    ]

let commentsUsers = [
    {userId: 1, text: 'Hello', user: {userId: 1, name: 'Mike'}}, 
    {userId: 1, text: 'Hi', user: {userId: 1, name: 'Mike'}}, 
    {userId: 2, text: 'Hello', user: {userId: 2, name: 'John'}}
    ] //Desired result

I know this can be done easily with ECMA6 arrays. 我知道这可以通过ECMA6阵列轻松完成。 For example: 例如:

let commentsUsers = comments.map(comment => {comment, users.find(user => user.userId === comment.userId)} )

I that an effective way to do this for a large number of users eg. 对于大量用户来说,这是一种有效的方法,例如。 1M users. 1M用户。 How does lodash compare to this or any other more specialized library? lodash与这个或任何其他更专业的库相比如何? Are there better ways to do this with vanilla JS eg. 是否有更好的方法与香草JS这样做,例如。 with Array.prototype.reduce()? 使用Array.prototype.reduce()? Can indexing be used in any way to improve the performance of the join? 可以以任何方式使用索引来提高连接的性能吗?

Edit: 编辑:

My ideal solution 我的理想解决方案

 let users = [{userId:1,name:'Mike'},{userId:2,name:'John'}] let comments = [{userId:1,text:'Hello'},{userId:1,text:'Hi'},{userId:2,text:'Hello'}]; let usersMap = new Map(users.map(user => [user.userId, user])) let commentsUsers = comments.map(comment => ({...comment, user: usersMap.get(comment.userId)})) console.log(commentsUsers) 

Thanks for the feedback! 感谢您的反馈!

Your desired result is not a proper data structure. 您想要的结果不是正确的数据结构。 You are missing a key to your object of eg {userId: 1, name: 'Mike'} . 您缺少对象的关键字,例如{userId: 1, name: 'Mike'} I added user as the key value for a indexing solution. 我添加了user作为索引解决方案的键值。

First I create a Map where the userId will be our loop-up value. 首先,我创建一个Map ,其中userId将成为我们的循环值。 Afterwards I just iterate over the comments with map , transforming each object to a new one that contains all the comment information plus a new kv pair of user. 之后,我只是使用map迭代comments ,将每个对象转换为包含所有comment信息和新的kv用户对象的新对象。 For that pair we don't need to use find anymore instead we have a simple HashMap get call. 对于那对,我们不再需要使用find ,而是我们有一个简单的HashMap get调用。

Time-complexity-wise this changes the code from O(n^2) to O(n) . 时间复杂度方面,这将代码从O(n ^ 2)更改为O(n)

 let users = [{userId:1,name:'Mike'},{userId:2,name:'John'}], comments = [{userId:1,text:'Hello'},{userId:1,text:'Hi'},{userId:2,text:'Hello'}]; function mergeCommentUser(users, comments) { let map = new Map(users.map(v => [v.userId, v])); return comments.map(o => ({...o, user: map.get(o.userId)})); } console.log(JSON.stringify(mergeCommentUser(users,comments))) 

Depending on what you want (and to save on redundancy), you could also change the following line: 根据您的需要(以及节省冗余),您还可以更改以下行:

let map = new Map(users.map(v => [v.userId, v]));

to the following instead: 改为:

let map = new Map(users.map(v => [v.userId, v.name]));

By that your result would look like: 通过这样你的结果看起来像:

[
    {"userId":1,"text":"Hello","user":"Mike"},
    {"userId":1,"text":"Hi","user":"Mike"},
    {"userId":2,"text":"Hello","user":"Paul"}
]

Otherwise, you could omit the comment.userId and instead add the full user to the object for another way to avoid redundancy. 否则,您可以省略comment.userId ,而是将完整用户添加到对象,以获得另一种避免冗余的方法。

Currently, the code example you provide is O(n * m), or, O(n 2 ). 目前,您提供的代码示例是O(n * m),或O(n 2 )。 You could create a map of each of the userId's and their respective indexes in the users array, and then rather than find the user, you can directly access it by index. 您可以在users数组中创建每个userId及其各自索引的映射,然后您可以通过索引直接访问它而不是查找用户。 This will reduce the time to O(n + m), that is, O(n). 这将减少到O(n + m)的时间,即O(n)。

The code would look something like this: 代码看起来像这样:

 const users = [{ userId: 1, name: "Mike" }, { userId: 2, name: "John" }]; const comments = [ { userId: 1, text: "Hello" }, { userId: 1, text: "Hi" }, { userId: 2, text: "Hello" } ]; const map = new Map(users.map((o, i) => [o.userId, i])); console.log( comments.map(o => { const index = map.get(o.userId); return index !== undefined ? { comment: o.text, user: users[index] } : o; }) ); 

Obviously, you can modify the end result, but this approach would be much more efficient than the one you proposed. 显然,您可以修改最终结果,但这种方法比您提出的方法更有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM