ExpressJS和Mongoose使用for函数创建多次

时间:2014-07-02 01:54:51

标签: javascript node.js mongodb express mongoose

在我创建的网站上,用户可以输入不同的标签并用逗号分隔。然后ExpressJS应该搜索它们是否存在。如果它们不存在,那么它应该为每个它们创建一个对象。我有一个数组,并使用for函数迭代它,但是,由于回调,只创建了一个对象...是否有任何可能的方法根据数组的长度一次创建多个对象?< / p>

for (i=0;i<postTopics.length;i++) {
    var postTopic = postTopics[i],
        postTopicUrl = postTopic.toString().toLowerCase().replace(' ', '-');

    Topic.findOne({ "title": postTopics[i] }, function (err, topic) {

        if (err) throw err;

        if (!topic) {

            Topic.create({
                title: postTopic,
                url: postTopicUrl

            }, function (err, topic) {

                if (err) throw err;

                res.redirect('/');
            });
        }
    });
}

2 个答案:

答案 0 :(得分:1)

试用async.parallel

  

$ npm install async

// Get the async module so we can do our parallel asynchronous queries much easier.
var async = require('async');

// Create a hash to store your query functions on.
var topicQueries = {};

// Loop through your postTopics once to create a query function for each one.
postTopics.forEach(function (postTopic) {
    // Use postTopic as the key for the query function so we can grab it later.
    topicQueries[postTopic] = function (cb) {
        // cb is the callback function passed in by async.parallel. It accepts err as the first argument and the result as the second.
        Topic.findOne({ title: postTopic }, cb);
    };
});

// Call async.parallel and pass in our topicQueries object.
// If any of the queries passed an error to cb then the rest of the queries will be aborted and this result function will be called with an err argument.
async.parallel(topicQueries, function (err, results) {
    if (err) throw err;

    // Create an array to store our Topic.create query functions. We don't need a hash because we don't need to tie the results back to anything else like we had to do with postTopics in order to check if a topic existed or not.
    var createQueries = [];

    // All our parallel queries have completed.
    // Loop through postTopics again, using postTopic to retrieve the resulting document from the results object, which has postTopic as the key.
    postTopics.forEach(function (postTopic) {
        // If there is no document at results[postTopic] then none was returned from the DB.
        if (results[postTopic]) return;

        // I changed .replace to use a regular expression. Passing a string only replaces the first space in the string whereas my regex searches the whole string.
        var postTopicUrl = postTopic.toString().toLowerCase().replace(\ \g, '-');

        // Since this code is executing, we know there is no topic in the DB with the title you searched for, so create a new query to create a new topic and add it to the createQueries array.
        createQueries.push(function (cb) {
            Topic.create({
                title: postTopic,
                url: postTopicUrl
            }, cb);
        });
    });

    // Pass our createQueries array to async.parallel so it can run them all simultaneously (so to speak).
    async.parallel(createQueries, function (err, results) {
        // If any one of the parallel create queries passes an error to the callback, this function will be immediately invoked with that err argument.
        if (err) throw err;

        // If we made it this far, no errors were made during topic creation, so redirect.
        res.redirect('/');
    });
});

首先,我们创建一个名为topicQueries的对象,然后为postTopic数组中的每个postTopics标题附加一个查询函数。然后我们将完成的topicQueries对象传递给async.parallel,它将运行每个查询并将结果收集到results对象中。

results对象最终是一个简单的对象哈希,每个postTopic标题作为键,值是DB的结果。如果if (results[postTopic]) return;results密钥下没有文档,postTopic行会返回。意思是,只有在没有从具有该标题的数据库返回的主题时,才会运行它下面的代码。如果没有匹配的主题,那么我们向createQueries数组添加查询函数。

我们不希望您的网页在其中一个新主题完成保存后重定向。我们要等到你的所有创建查询都已完成,所以我们再次使用async.parallel,但这次我们使用数组而不是对象哈希,因为我们不需要将结果绑定到任何东西。当您将数组传递给async.parallel时,results参数也将是一个包含每个查询结果的数组,尽管我们并不关心此示例中的结果,只是没有抛出任何错误。如果parallel函数完成且没有err参数,那么所有主题都已成功完成,我们最终可以将用户重定向到新页面。

PS - 如果您遇到过类似的情况,除了每个后续查询都要求查询之前的数据,然后结帐async.waterfall:)

答案 1 :(得分:0)

如果你真的想知道事物是否已经存在并避免在重复项上出错,那么.create()方法已经接受了一个列表。您似乎并不关心如何在响应中创建文档,因此只需检查那里的文档并发送新文档。

所以用&#34;先找到&#34;,然后连续运行任务。 async.waterfall只是为了缩进缩进:

// Just a placeholder for your input
var topics = ["A Topic","B Topic","C Topic","D Topic"];

async.waterfall(
  [
    function(callback) {
      Topic.find(
        { "title": { "$in": topics } },
        function(err,found) {

          // assume ["Topic B", "Topic D"] are found
          found = found.map(function(x) {
            return x.title;
          });

          var newList = topics.filter(function(x) {
            return found.indexOf(x) == -1;
          });

          callback(err,newList);

        }
      );

    },
    function(newList,callback) {

      Topic.create(
        newList.map(function(x) {
          return {
            "title": x,
            "url": x.toString().toLowerCase().replace(' ','-')
          };
        }),
        function(err) {
          if (err) throw err;
          console.log("done");
          callback();
        }
      );
    }
  ]

);

你可以移动&#34; url&#34;生成一个&#34; pre&#34;保存架构钩子。但是,如果您真的不需要验证规则,那么请选择&#34;批量API&#34;操作提供了你的目标MongoDB和mongoose版本足以支持这个,这实际上意味着获得底层驱动程序的句柄:

// Just a placeholder for your input
var topics = ["A Topic","B Topic","C Topic","D Topic"];

async.waterfall(
  [
    function(callback) {
      Topic.find(
        { "title": { "$in": topics } },
        function(err,found) {

          // assume ["Topic B", "Topic D"] are found
          found = found.map(function(x) {
            return x.title;
          });

          var newList = topics.filter(function(x) {
            return found.indexOf(x) == -1;
          });

          callback(err,newList);

        }
      );

    },
    function(newList,callback) {

      var bulk = Topic.collection.initializeOrderedBulkOp();
      newList.forEach(function(x) {
        bullk.insert({
          "title": x,
          "url": x.toString().toLowerCase().replace(' ','-')
        });
      });
      bulk.execute(function(err,results) {
        console.log("done");
        callback();
      });

    }
  ]
);

这是对服务器的单个写操作,但当然所有插入实际上都按顺序完成并检查错误。

否则只需挂起重复的错误并插入&#34;无序操作&#34;,检查&#34;非重复&#34;如果你想要的错误:

// Just a placeholder for your input
var topics = ["A Topic","B Topic","C Topic","D Topic"];

var bulk = Topic.collection.initializeUnorderedBulkOp();
topics.forEach(function(x) {
  bullk.insert({
    "title": x,
    "url": x.toString().toLowerCase().replace(' ','-')
  });
});
bulk.execute(function(err,results) {
  if (err) throw err;
  console.log(JSON.stringify(results,undefined,4));
});

结果中的输出类似于以下内容,表示&#34;重复&#34;错误,但没有&#34;扔&#34;在这种情况下没有设置错误:

{
    "ok": 1,
    "writeErrors": [
        {
            "code": 11000,
            "index": 1,
            "errmsg": "insertDocument :: caused by :: 11000 E11000 duplicate key error index: test.topic.$title_1  dup key: { : \"B Topic\" }",
            "op": {
                "title": "B Topic",
                "url": "b-topic",
                "_id": "53b396d70fd421057200e610"
            }
        },
        {
            "code": 11000,
            "index": 3,
            "errmsg": "insertDocument :: caused by :: 11000 E11000 duplicate key error index: test.topic.$title_1  dup key: { : \"D Topic\" }",
            "op": {
                "title": "D Topic",
                "url": "d-topic",
                "_id": "53b396d70fd421057200e612"
            }
        }
    ],
    "writeConcernErrors": [],
    "nInserted": 2,
    "nUpserted": 0,
    "nMatched": 0,
    "nModified": 0,
    "nRemoved": 0,
    "upserted": []
}

请注意,使用本机收集方法时,需要注意已建立连接。猫鼬方法将排队&#34;排队&#34;直到建立连接,但这些不会。更多的测试问题,除非有可能这是第一个执行的代码。

希望这些批量操作的版本很快就会在mongoose API中公开,但是一般的后端功能确实依赖于在服务器上安装MongoDB 2.6或更高版本。一般来说,这将是最好的处理方式。

当然,除了最后一个不需要这个的样本之外,你可以绝对去&#34; async nuts&#34;通过调用&#34;过滤器&#34;,&#34; map&#34;的版本和&#34; forEach&#34;存在于该库下的。可能不是一个真正的问题,除非你提供非常长的输入列表。

节点本机驱动程序手册中介绍了.initializeOrderedBulkOP().initializeUnorderedBulkOP()方法。另请参阅主要手册,了解Bulk operations的一般说明。