如何在内存中缓存mongoose查询?

时间:2015-10-29 14:44:19

标签: node.js mongodb caching mongoose

我有以下查询,这些查询以启动GetById方法开始,一旦启动并从另一个文档中提取数据,它就会保存到竞赛文档中。

我希望能够在保存十分钟后缓存数据。我看了一下cacheman库,不确定它是不是适合这项工作的工具。什么是最好的方法来解决这个问题?

 getById: function(opts,callback) {
        var id = opts.action;
        var raceData = { };
        var self = this;

        this.getService().findById(id,function(err,resp) {
                    if(err)
                        callback(null);
                    else {

                            raceData = resp;                            

                            self.getService().getPositions(id, function(err,positions) {    
                                    self.savePositions(positions,raceData,callback);                                    
                            });
                    }
        });
  },

  savePositions: function(positions,raceData,callback) {
        var race = [];
    _.each(positions,function(item) {                           
          _.each(item.position,function(el) {
            race.push(el);
        });   
    });

        raceData.positions = race;  

        this.getService().modelClass.update({'_id' : raceData._id },{ 'positions' : raceData.positions },callback(raceData));

  }

2 个答案:

答案 0 :(得分:1)

我最近编写并发布了一个名为Monc的模块。您可以在here上找到源代码。您可以找到几种有用的方法来存储,删除和检索存储在内存中的数据。

您可以使用它来使用简单嵌套来缓存Mongoose查询

test.find({}).lean().cache().exec(function(err, docs) {
  //docs are fetched into the cache.
});

否则,您可能需要查看Mongoose的核心并覆盖原型,以便像原始建议一样使用cacheman

创建一个节点模块并强制它将Mongoose扩展为:

monc.hellocache(mongoose, {});

在您的模块中,您应该扩展Mongoose.Query.prototype

exports.hellocache = module.exports.hellocache = function(mongoose, options, Aggregate) {

  //require cacheman 

  var CachemanMemory = require('cacheman-memory');
  var cache = new CachemanMemory();

  var m = mongoose;
  m.execAlter = function(caller, args) {
    //do your stuff here
  }

  m.Query.prototype.exec = function(arg1, arg2) {
        return m.execAlter.call(this, 'exec', arguments);
    };
})

看看Monc's source code,因为它可能是一个很好的参考,可以扩展和链接Mongoose方法

答案 1 :(得分:0)

我将使用npm redis软件包进行解释,该软件包将键/值对存储在缓存服务器中。键是查询,redis仅存储字符串。

我们必须确保密钥是唯一且一致的。因此,键值应存储查询以及正在应用查询的模型的名称。

查询时,在猫鼬库中有

function Query(conditions, options, model, collection) {} //constructor function

负责查询。在此构造函数中,

Query.prototype.exec = function exec(op, callback) {} 

此函数负责执行查询。因此我们必须操纵该功能并使其执行那些任务:

首先检查我们是否有与查询相关的缓存数据 如果是,请立即回复请求并返回 如果没有,我们需要响应请求并更新缓存,然后响应

const redis = require("client");
const redisUrl = "redis://127.0.0.1:6379";
const client = redis.createClient(redisUrl);
const util = require("util");
//client.get does not return promise
client.get = util.promisify(client.get);

    const exec = mongoose.Query.prototype.exec;
   //mongoose code is written using classical prototype inheritance for setting up objects and classes inside the library.

mongoose.Query.prototype.exec = async function() {
  //crate a unique and consistent key
  const key = JSON.stringify(
    Object.assign({}, this.getQuery(), {
      collection: this.mongooseCollection.name
    })
  );
  //see if we have value for key in redis
  const cachedValue = await redis.get(key);
  //if we do return that as a mongoose model.
  //the exec function expects us to return mongoose documents
  if (cachedValue) {
    const doc = JSON.parse(cacheValue);
    return Array.isArray(doc)
      ? doc.map(d => new this.model(d))
      : new this.model(doc);
  }
  const result = await exec.apply(this, arguments); //now exec function's original task.
  client.set(key, JSON.stringify(result),"EX",6000);//it is saved to cache server make sure capital letters EX and time as seconds
};

如果我们将值存储为对象数组,则需要确保将每个对象都单独转换为猫鼬文档。 this.model是Query构造函数中的一种方法,可将对象转换为猫鼬文档。

请注意,如果要存储嵌套值而不是client.get和client.set,请使用client.hset和client.hget

现在我们猴子修补了 Query.prototype.exec

因此您不需要导出此功能。只要您在代码中进行查询操作,猫鼬都会执行以上代码