如何在sails-mongo上进行非常大的查询?

时间:2015-11-22 20:33:40

标签: json node.js sails.js sails-mongo

我正在使用风帆0.11.2。使用最新的sails-mongo适配器。 我有一个非常大的数据库(千兆字节的数据),主要是时间戳和值。我使用蓝图api对它进行查询。

如果我使用localhost:1337 / datatable?limit = 100000000000进行查询,则nodejs在0.12上挂起,占用大量CPU,并在v4上崩溃。它在toJSON函数上崩溃。

我发现我需要对我的API进行多次查询。但我不知道怎么做。

如何进行多次查询“不爆炸”我的服务器?

更新

在带有最新水线和sails-mongo的更新版本0.12.3上,查询更加顺畅。云上的崩溃是因为我没有足够的RAM来处理同一个T2.micro实例上的sailsjs和mongodb。

我已将mongodb服务器移动到M3.Medium实例。现在服务器不再崩溃,但它冻结了。我正在使用跳过限制,它适用于sails.js,但对于mongodb来说是一个很大的资源浪费!

Mongodb使用limit = skip + limit进行内部查询。然后将光标移动到所需的数据并返回。当你在分页中做很多事情时,你会使用大量的内部查询。随着查询大小的增加。

2 个答案:

答案 0 :(得分:1)

正如this article所解释的那样,避免使用MongoDB中浪费资源的方法是避免使用skip并巧妙地使用_id作为查询的一部分。

我没有使用sails mongo,但我确实通过在nodejs中使用mongo驱动程序来实现上述想法:

/**
 * Motivation:
 * Wanted to put together some code that used:
 *  - BlueBird (promises)
 *  - MongoDB NodeJS Driver
 *  - and paging that did not rely on skip()
 *
 * References:
 * Based on articles such as:
 * https://scalegrid.io/blog/fast-paging-with-mongodb/
 * and GitHub puclic code searches such as:
 * https://github.com/search?utf8=%E2%9C%93&q=bluebird+MongoClient+_id+find+limit+gt+language%3Ajavascript+&type=Code&ref=searchresults
 * which yielded smaple code hits such as:
 * https://github.com/HabitRPG/habitrpg/blob/28f2e9c356d7053884107d90d04e28dde75fa81b/migrations/api_v3/coupons.js#L71
 */

  var Promise = require('bluebird'); // jshint ignore:line
  var _ = require('lodash');
  var MongoClient = require('mongodb').MongoClient;
  var dbHandleForShutDowns;

  // option a: great for debugging
  var logger = require('tracer').console();
  // option b: general purpose use
  //var logger = console;

  //...

    var getPage = function getPage(db, collectionName, query, projection, pageSize, processPage) {
      //console.log('DEBUG', 'filter:', JSON.stringify(query,null,2));
      projection = (projection) ? projection['_id']=true : {'_id':true};
      return db
        .collection(collectionName)
        .find(query)
        .project(projection)
        .sort({'_id':1}).limit(pageSize)
        .toArray() // cursor methods return promises: http://mongodb.github.io/node-mongodb-native/2.1/api/Cursor.html#toArray
        .then(function processPagedResults(documents) {
          if (!documents || documents.length < 1) {
            // stop - no data left to traverse
            return Promise.resolve();
          }
          else {
            if (documents.length < pageSize) {
              // stop - last page
              return processPage(documents);
            }
            else {
              return processPage(documents) // process the results of the current page
                .then(function getNextPage(){ // then go get the next page
                  var last_id = documents[documents.length-1]['_id'];
                  query['_id'] = {'$gt' : last_id};
                  return getPage(db, collectionName, query, projection, pageSize, processPage);
                });
            }
          }
        });
    };

    //...

    return MongoClient
      .connect(params.dbUrl, {
        promiseLibrary: Promise
      })
      .then(function(db) {
        dbHandleForShutDowns = db;
        return getPage(db, collectionName, {}, {}, 5, function processPage(pagedDocs){console.log('do something with', pagedDocs);})
          .finally(db.close.bind(db));
      })
      .catch(function(err) {
        console.error("ERROR", err);
        dbHandleForShutDowns.close();
      });

以下两节介绍了代码如何操作_id并使其成为查询的一部分:

 .sort({'_id':1}).limit(pageSize)
 // [...]
var last_id = documents[documents.length-1]['_id'];
query['_id'] = {'$gt' : last_id};

整体代码流程:

  1. getPage()处理工作,您可以根据自己的喜好设置pageSizequery

    return getPage(db, collectionName, {}, {}, 5, function processPage(pagedDocs){console.log('do something with', pagedDocs);})
    
  2. 方法签名:

    var getPage = function getPage(db, collectionName, query, projection, pageSize, processPage) {
    
  3. 一旦可用,请处理pagedResults

    return processPage(documents) // process the results of the current page
    
  4. 转到下一页:

    return getPage(db, collectionName, query, projection, pageSize, processPage);
    
  5. 当没有剩余数据时,代码将停止:

    // stop - no data left to traverse
    return Promise.resolve();
    
  6. 或者在处理最后一页数据时它会停止:

    // stop - last page
    return processPage(documents);
    
  7. 我希望这会提供一些灵感,即使它不是满足您需求的精确解决方案。

答案 1 :(得分:1)

1。运行汇总

const SailsMongoQuery = require('sails-mongo/lib/query/index.js')
const SailsMongoMatchMongoId = require('sails-mongo/lib/utils.js').matchMongoId
const fn = model.find(query).paginate(paginate)
const criteria = fn._criteria
const queryLib = new SailsMongoQuery(criteria, {})
const queryOptions = _.omit(queryLib.criteria, 'where')
const where = queryLib.criteria.where || {}
const queryWhere = Object.keys(where).reduce((acc, key) => {
  const val = where[key]
  acc[key] = SailsMongoMatchMongoId(val) ? new ObjectID(val) : val
  return acc
}, {})

const aggregate = [
  { $match: queryWhere }
].concat(Object.keys(queryOptions).map(key => ({ [`$${key}`]: queryOptions[key] })))

// console.log('roge aggregate --->', JSON.stringify(aggregate, null, 2))

model.native((err, collection) => {
  if (err) return callback(err)
  collection.aggregate(aggregate, { allowDiskUse: true }).toArray(function (err, docs) {
    if (err) return callback(err)
    const pk = primaryKey === 'id' ? '_id' : primaryKey
    ids = docs.reduce((acc, doc) => [...acc, doc[pk]], [])
    callback()
  })
})

2。按ID查找航行

query = Object.assign({}, query, { [primaryKey]: ids }) // check primary key in sails model
fn = model.find(query) // .populate or another method
fn.exec((err, results) => { console.log('result ->>>>', err, results) })