MongoDB map-reduce(通过nodejs):如何在scopeObj中包含复杂模块(带有依赖项)?

时间:2018-03-12 15:31:49

标签: javascript node.js mongodb npm mapreduce

我正在为mongodb数据库开展复杂的map-reduce流程。我将一些更复杂的代码拆分为模块,然后通过将其包含在scopeObj中,将其提供给我的map / reduce / finalize函数,如下所示:

  const scopeObj = {
    userCalculations: require('../lib/userCalculations')
  }

  function myMapFn() {
    let userScore = userCalculations.overallScoreForUser(this)
    emit({
      'Key': this.userGroup
    }, {
      'UserCount': 1,
      'Score': userScore
    })
  }

  function myReduceFn(key, objArr) { /*...*/ }

  db.collection('userdocs').mapReduce(
    myMapFn,
    myReduceFn,
    {
      scope: scopeObj,
      query: {},
      out: {
        merge: 'userstats'
      }
    },
    function (err, stats) {
      return cb(err, stats);
    }
  )

......一切正常。直到最近我才认为将模块代码包含在map-reduce scopeObj中是不可能的,但事实证明这只是因为我试图包含的模块都依赖于其他模块。完全独立的模块似乎工作正常。

这让我(最后)回答了我的问题。我怎么能 - 或者,就此而言,我应该 - 将更复杂的模块(包括我从npm中提取的内容)合并到我的map-reduce代码中?有人认为我曾经使用Browserify或类似的方法将我的所有依赖关系整合到一个文件中,然后以某种方式包含它......但我不确定这样做的正确方法是什么。而且我也不确定我在多大程度上冒着严重膨胀我的地图缩减代码的风险,这显然必须高效。

有没有人有经验做这样的事情?它是如何运作的,如果有的话?我在这里走错路吗?

更新 :澄清我试图克服的问题: 在上面的代码中,require('../lib/userCalculations')由Node执行 - 它读入文件../lib/userCalculations.js并将该文件的module.exports对象的内容分配给scopeObj.userCalculations。但是,我们可以在require(...)内的某个地方拨打userCalculations.js。那个电话还没有实际执行。因此,当我尝试在Map函数中调用userCalculations.overallScoreForUser()时,MongoDB会尝试执行require函数。而且require未在mongo上定义。

例如,Browserify通过将所有必需模块中的所有代码编译成没有require调用的单个javascript文件来处理此问题,因此可以在浏览器中运行。但这并不能完全适用于此,因为我需要将结果代码本身作为一个模块,我可以使用,就像我在代码示例中使用userCalculations一样。也许有一种奇怪的方式来运行我不知道的浏览器?或者其他一些只是“扁平化”的工具。将整个模块层次结构化为单个模块?

希望澄清一点。

1 个答案:

答案 0 :(得分:3)

作为一般回答,你的问题的答案是:我怎么能 - 或者,就此而言,我应该把更复杂的模块,包括我从npm中提取的东西,纳入我的地图 - 减少代码? - 否,您无法安全地在计划发送到MongoDB以获取mapReduce作业的节点代码中包含复杂模块

您自己提到了问题 - 嵌套require语句。现在,require是同步,但是如果你有嵌套的函数,那么这些需要的调用在调用时才会被执行,而且MongoDB VM会在此时抛出。

请考虑以下三个文件的示例:data.jsondep.jsmain.js

// data.json - just something we require "lazily"
false

// dep.js -- equivalent of your userCalculations
module.exports = {
  isValueTrue() {
    // The problem: nested require
    return require('./data.json');
  }
}


// main.js - from here you send your mapReduce to MongoDB.
// require dependency instantly
const calc = require('./dep.js');
// require is synchronous, the effectis the same if you do:
//   const calc = (function () {return require('./dep.js')})();

console.log('Calc is loaded.');
// Let's mess with unwary devs
require('fs').writeFileSync('./data.json', 'false');

// Is calc.isValueTrue() true or false here?
console.log(calc.isValueTrue());

作为一般解决方案,这是不可行的。虽然绝大多数模块可能没有嵌套的require语句,HTTP调用,甚至内部,服务调用,全局变量等,但有些人都这样做。你不能保证这会起作用。

现在,作为您的本地实施:例如您需要使用此技术测试的NPM模块的确切特定版本,并且您知道它可以使用,或者您自己发布它们,这有点可行。

然而,即使在这种情况下,如果这是团队的努力,那么必然会有一个开发人员不知道你的依赖关系在哪里使用或者如何使用全局(不是故意的,而是通过省略,例如他们错误地计算this)或者根本不知道他们在做什么的含义。如果你有强大的集成测试套件,你可以防范这一点,但问题是,这是不可预测的。我个人认为,当你可以在不可预测和可预测之间做出选择时,几乎总是你应该使用可预测的。

现在,如果您明确声明要在MongoDB mapReduce中使用某个库,则可以使用。你必须要很好地防范ommisions和问题,并且在下面进行强有力的测试,但是在感到足够安全之前我会确定目的是明确的。但是当然,如​​果你使用的东西太复杂而你需要几个npm包,也许你可以直接在MongoDB服务器上安装这些函数,也许你可以用更适合的目的来做mapReducing,或类似的

总结:作为一个具有明确任务声明的目的构建的库,它将与节点和MongoDB mapReduce一起使用,我将确保我的测试涵盖我的所有关键任务和重要功能,然后导入这样的npm包。否则我不会使用也不会推荐这种方法。