使用 Meteor 方法从 MongoDB 下载大量文档

Posted

技术标签:

【中文标题】使用 Meteor 方法从 MongoDB 下载大量文档【英文标题】:Download large sets of documents from MongoDB using Meteor methods 【发布时间】:2021-12-09 12:35:18 【问题描述】:

我正在尝试使用 Meteor 方法从集合(大约 12 MB)中导出所有文档,但它几乎总是使应用程序崩溃或从不返回结果。

我正在考虑将文档上传到 S3,然后向客户端发送下载链接,但是似乎有不必要的网络连接,并且会使过程更长。

有没有更好的方法从服务器到客户端获取大量数据?

这是该代码的示例,非常简单。

'downloadUserActions': () => 
 if (Roles.userIsInRole(Meteor.userId(), ['admin'])) 
   const userData = userActions.find().fetch();
   return userData
 

谢谢。

【问题讨论】:

您是否有一个最小的可重现示例来解决问题?我不确定您的方法从根本上是否存在缺陷。可能是您的应用程序导致问题的原因。如果失败了,那么您总是可以公开一个旧的服务器路由(想想 REST API)来下载数据。 我同意@ChristianFritz。从理论上讲,通过 DDP 方法下载没有什么问题,因此它可能与您的代码有关,如果您无法使其与 DDP 一起使用,您可以随时求助于 REST。 @ChristianFritz 感谢您的输入,我也在想同样的事情,但是服务器保持沉默并且从不返回结果,我还会检查服务器是否存在内存问题但从未找到。我还将尝试使用文件系统库来流式传输数据,我会不断更新这个问题。 12MB 不够大,可能会导致您的应用崩溃。你还有autopublish 包吗? 【参考方案1】:

您可以使用一种方法,将请求拆分为多个:

获取文档计数 直到文档计数被完全提取 获取已获取文档的当前计数 获取下一组文档并跳过已经获取的文档

为此,您需要在 mongo 查询中使用 skip 选项以跳过已获取的文档。

代码示例

const limit = 250

Meteor.methods(
  // get the max amount of docs
  getCount () 
    return userActions.find().count()
  ,
  // get the next block of docs
  // from: skip to: skip + limit
  // example: skip = 1000, limit = 500 is
  // from: 1000 to: 1500
  downloadUserActions (skip) 
    this.unblock()
    return userActions.find(,  skip, limit ).fetch()
  
)

客户:

// wrap the Meteor.call into a promise
const asyncCall = (name, args) => new Promise((resolve, reject) => 
  Meteor.call(name, args, (err, res) => 
    if (err) 
      return reject(err)
    
    return resolve(res)
  )
)

const asyncTimeout = ms => new Promise(resolve => setTimeout(() => resolve(), ms)


const fetchAllDocs = async (destination) => 
  const maxDocs = await asyncCall('getCount')
  let loadedDocs = 0

  while (loadedDocs < maxDocs) 
    const docs = await asyncCall('downloadUserActions', loadedDocs)
    docs.forEach(doc => 
      // think about using upsert to fix multiple docs issues
      destination.insert(doc) 
    )

    // increase counter (skip value)
    loadedDocs = destination.find().count()

    // wait 10ms for next request, increase if server needs
    // more time
    await asyncTimeout(10)
  
 
  return destination

将它与客户端上的本地 Mongo 集合一起使用:

await fetchAllDocs(new Mongo.Collection(null))

在函数之后,所有文档现在都存储在这个本地集合中。

使用limit 和超时(毫秒)值来找到用户体验和服务器性能之间的最佳平衡点。

其他改进

代码不会验证或验证请求。这取决于你! Aölso 您可能会考虑添加故障安全机制,以防while 循环由于某些意外错误而永远无法完成。

进一步阅读

https://docs.meteor.com/api/methods.html#DDPCommon-MethodInvocation-unblock https://docs.meteor.com/api/collections.html#Mongo-Collection https://docs.meteor.com/api/collections.html#Mongo-Collection-find

【讨论】:

以上是关于使用 Meteor 方法从 MongoDB 下载大量文档的主要内容,如果未能解决你的问题,请参考以下文章

在 Meteor 运行时,如何从另一个客户端访问 Meteor 的 MongoDB?

在 Meteor.js 中使用多个 Mongodb 数据库

Meteor - 如何在 MongoDB 集合中查找/获取对象并使用方法将其推送到另一个集合中?

从 MongoDB 获取大数据的最佳方法

Meteor -- 如何连接到 mongodb? [复制]

日期查询之间的 Meteor.js / MongoDB 不返回数据