├── .gitignore ├── .npmignore ├── .travis.yml ├── Makefile ├── README.md ├── index.js ├── lib ├── cache.js ├── finder.js └── index.js ├── package.json └── test └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | npm_debug.log 4 | .data -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | npm_debug.log 4 | .data -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.11" 4 | - "0.10" 5 | - "0.8" 6 | services: 7 | - mongodb 8 | before_install: 9 | - npm install -g npm -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | REPORTER = spec 2 | 3 | test: 4 | @./node_modules/.bin/mocha \ 5 | --reporter $(REPORTER) \ 6 | --bail 7 | 8 | .PHONY: test 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mongoose cachebox 2 | 3 | [![Build Status](https://travis-ci.org/cayasso/mongoose-cachebox.png?branch=master)](https://travis-ci.org/cayasso/mongoose-cachebox) 4 | [![NPM version](https://badge.fury.io/js/mongoose-cachebox.png)](http://badge.fury.io/js/mongoose-cachebox) 5 | 6 | Caching [mongoose](http://http://mongoosejs.com/) queries easier with [cacheman](https://github.com/cayasso/cacheman) that supports in-memory, and Redis engines. 7 | 8 | ## Installation 9 | 10 | ``` bash 11 | $ npm install mongoose-cachebox 12 | ``` 13 | 14 | ## Usage 15 | 16 | ``` javascript 17 | var mongoose = require('mongoose'); 18 | 19 | var options = { 20 | cache: true, // start caching 21 | ttl: 30 // 30 seconds 22 | }; 23 | 24 | // adding mongoose cachebox 25 | mongooseCachebox(mongoose, options); 26 | ``` 27 | 28 | Then later any `find` query will be cached for 30 seconds. 29 | 30 | You can also enable caching programatically by using the `cache` method directly from the query instance: 31 | 32 | ``` javascript 33 | var Person = mongoose.model('Person'); 34 | 35 | Person 36 | .find({ active: true }) 37 | .cache('50s') // cache for 50 seconds 38 | .exec(function (err, docs) { 39 | if (err) throw error; 40 | 41 | console.log(docs.ttl); // time left for expiration in ms 42 | console.log(docs.stored); // timestamp this query was cached 43 | console.log(docs); 44 | }); 45 | 46 | ``` 47 | 48 | ## API 49 | 50 | This plugin will add two more methods to a mongoose query instance `cache` and `ttl`. 51 | 52 | ### query.cache([cached], [ttl]) 53 | 54 | Both parameters `cache` and `ttl` are optional, the first one is for enable caching the second is for specifying the cache expiration (time to live). 55 | 56 | For start caching just call the `cache` method: 57 | 58 | ``` javascript 59 | Person 60 | .find({ active: true }) 61 | .cache() // will enable caching with 60 seconds ttl 62 | .exec(function (err, docs) { 63 | /* .... */ 64 | }); 65 | ``` 66 | 67 | The above is equivalent to this: 68 | 69 | ``` javascript 70 | Person 71 | .find({ active: true }) 72 | .cache(true) // start caching with 60 seconds ttl 73 | .exec(function (err, docs) { 74 | /* .... */ 75 | }); 76 | ``` 77 | 78 | You can specify the `ttl` (time to live) value directly: 79 | 80 | ``` javascript 81 | Person 82 | .find({ active: true }) 83 | .cache(10) // cache for 10 seconds 84 | .exec(function (err, docs) { 85 | /* .... */ 86 | }); 87 | ``` 88 | 89 | The above is equivalent to this: 90 | 91 | ``` javascript 92 | Person 93 | .find({ active: true }) 94 | .cache(true, 10) // enable caching with 10 seconds ttl 95 | .exec(function (err, docs) { 96 | /* .... */ 97 | }); 98 | ``` 99 | 100 | And to disable caching for specific query just pass `false`: 101 | 102 | ``` javascript 103 | Person 104 | .find({ active: true }) 105 | .cache(false) // stop caching this query 106 | .exec(function (err, docs) { 107 | /* .... */ 108 | }); 109 | ``` 110 | 111 | ### query.ttl(ttl) 112 | 113 | By default the ttl value is `60000` (60 seconds) but you can use the `ttl` method to specify a different value: 114 | 115 | ``` javascript 116 | Person 117 | .find({ active: true }) 118 | .cache() // cache query 119 | .ttl(10) // caching for 10 seconds 120 | .exec(function (err, docs) { 121 | /* .... */ 122 | }); 123 | ``` 124 | 125 | ## Redis 126 | 127 | By default `mongoose-cachebox` will use the memory engine to cache queries but it can cache queries using `Redis` by specifying redis engine when initializing the plugin: 128 | 129 | ``` javascript 130 | var mongoose = require('mongoose'); 131 | 132 | var options = { 133 | engine: 'redis', 134 | host: '127.0.0.1', 135 | port: '6379', 136 | password: 'secret' 137 | }; 138 | 139 | // adding mongoose cachebox 140 | mongooseCachebox(mongoose, options); 141 | ``` 142 | 143 | This module use [cacheman](https://github.com/cayasso/cacheman) for the caching magic, so check out the project for more details and options. 144 | 145 | ## Run tests 146 | 147 | ``` bash 148 | $ make test 149 | ``` 150 | 151 | ## License 152 | 153 | (The MIT License) 154 | 155 | Copyright (c) 2013 Jonathan Brumley <cayasso@gmail.com> 156 | 157 | Permission is hereby granted, free of charge, to any person obtaining 158 | a copy of this software and associated documentation files (the 159 | 'Software'), to deal in the Software without restriction, including 160 | without limitation the rights to use, copy, modify, merge, publish, 161 | distribute, sublicense, and/or sell copies of the Software, and to 162 | permit persons to whom the Software is furnished to do so, subject to 163 | the following conditions: 164 | 165 | The above copyright notice and this permission notice shall be 166 | included in all copies or substantial portions of the Software. 167 | 168 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 169 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 170 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 171 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 172 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 173 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 174 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 175 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/index'); -------------------------------------------------------------------------------- /lib/cache.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var Cacheman = require('cacheman') 6 | , debug = require('debug')('mongoose-cachebox:cache'); 7 | 8 | /** 9 | * Expose `Cache`. 10 | */ 11 | 12 | module.exports = Cache; 13 | 14 | /** 15 | * Cache instances. 16 | * 17 | * @type {Object} 18 | * @api public 19 | */ 20 | 21 | Cache.instances = {}; 22 | 23 | /** 24 | * Bucket instances. 25 | * 26 | * @type {Object} 27 | * @api public 28 | */ 29 | 30 | Cache.buckets = {}; 31 | 32 | /** 33 | * Cache constructor. 34 | * 35 | * @param {Query} query 36 | * @param {Object} options 37 | * @api public 38 | */ 39 | 40 | function Cache(query, options, middleware) { 41 | 42 | var id = query.key; 43 | 44 | if (!(this instanceof Cache)) { 45 | return (id in Cache.instances) ? 46 | Cache.instances[id] : 47 | new Cache(query, options, middleware); 48 | } 49 | 50 | this.id = id; 51 | this._name = query.model.modelName; 52 | this.ttl(60); 53 | this.cache(false); 54 | this.cached(false); 55 | this.client = (this._name in Cache.buckets) ? 56 | Cache.buckets[this._name] : 57 | new Cacheman(this._name, options); 58 | Cache.instances[id] = this; 59 | Cache.buckets[this._name] = this.client; 60 | if (middleware) this.client._fns[0] = middleware.bind(this); 61 | } 62 | 63 | /** 64 | * Add a middleware for intercepting cache values. 65 | * 66 | * @param {Function} fn 67 | * @return {Cache} this 68 | * @api public 69 | */ 70 | 71 | Cache.prototype.use = function use(fn) { 72 | this.client._fns[1] = null; 73 | this.client._fns[1] = fn; 74 | return this; 75 | }; 76 | 77 | /** 78 | * Enable or disable caching. 79 | * 80 | * @param {Boolean} cached 81 | * @return {Cache|Boolean} self or cached 82 | * @api public 83 | */ 84 | 85 | Cache.prototype.cache = function cache(val) { 86 | if (!arguments.length) return this._cache; 87 | this._cache = val; 88 | return this; 89 | }; 90 | 91 | /** 92 | * Set or get cached value. 93 | * 94 | * @param {Boolean} cached 95 | * @return {Cache|Boolean} self or cached 96 | * @api public 97 | */ 98 | 99 | Cache.prototype.cached = function cached(val) { 100 | if (!arguments.length) return this._cached; 101 | this._cached = val; 102 | return this; 103 | }; 104 | 105 | /** 106 | * Time to live setter and getter. 107 | * 108 | * @param {Number} ttl 109 | * @return {Cache|Number} this 110 | * @api public 111 | */ 112 | 113 | Cache.prototype.ttl = function ttl(val) { 114 | if (!arguments.length) return this._ttl; 115 | this._ttl = val; 116 | return this; 117 | }; 118 | 119 | /** 120 | * Autocache docs 121 | * 122 | * @param {Function} fn 123 | * @return {Cache} this 124 | * @api private 125 | */ 126 | 127 | Cache.prototype.autocache = function autocache(fn) { 128 | var cache = this; 129 | cache.client.cache(cache.id, undefined, cache._ttl, function autocache(err, res) { 130 | if (err) return fn(err); 131 | cache.cached(true); 132 | fn(null, res); 133 | }); 134 | return this; 135 | }; 136 | 137 | /** 138 | * Cache query. 139 | * 140 | * @param {Object} val 141 | * @param {Function} fn 142 | * @return {Cache} this 143 | * @api public 144 | */ 145 | 146 | Cache.prototype.set = function set(val, fn) { 147 | var cache = this; 148 | cache.client.set(cache.id, val, cache._ttl, function set(err, res) { 149 | if (err) return fn(err); 150 | cache.cached(true); 151 | fn(null, res); 152 | }); 153 | return this; 154 | }; 155 | 156 | /** 157 | * Get cached query. 158 | * 159 | * @param {Function} fn 160 | * @return {Cache} this 161 | * @api public 162 | */ 163 | 164 | Cache.prototype.get = function get(fn) { 165 | var cache = this; 166 | cache.client.get(cache.id, function get(err, res) { 167 | if (err) return fn(err); 168 | cache.cached(!!res); 169 | fn(null, res); 170 | }); 171 | return this; 172 | }; 173 | 174 | /** 175 | * Delete cached query. 176 | * 177 | * @param {Function} fn 178 | * @return {Cache} this 179 | * @api public 180 | */ 181 | 182 | Cache.prototype.del = function del(fn) { 183 | var cache = this; 184 | cache.client.del(cache.id, function del(err, res) { 185 | if (err) return fn(err); 186 | cache.cached(false); 187 | fn(null, res); 188 | }); 189 | return this; 190 | }; -------------------------------------------------------------------------------- /lib/finder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Expose `finder` middleware. 3 | */ 4 | 5 | module.exports = function finder(query, execFind) { 6 | 7 | /** 8 | * Middleware method. 9 | * 10 | * @param {String} key 11 | * @param {Mixed} data 12 | * @param {Number} ttl 13 | * @param {Function} next 14 | * @api public 15 | */ 16 | 17 | return function middleware(key, data, ttl, next) { 18 | 19 | // dont do anything if the value is still in cache 20 | // just call the next handler 21 | if ('undefined' !== typeof data) { 22 | return next(); 23 | } 24 | 25 | // here we execute the execFind method with 26 | // the query context 27 | execFind.call(query, function finding(err, docs) { 28 | 29 | // we raise an error if any 30 | if (err) return next(err); 31 | 32 | // if no error we call the next handler 33 | next(null, docs, ttl); 34 | 35 | }); 36 | 37 | }; 38 | }; -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var Cache = require('./cache') 6 | , finder = require('./finder') 7 | , debug = require('debug')('mongoose-cachebox'); 8 | 9 | /** 10 | * Expose module. 11 | */ 12 | 13 | module.exports = function mongooseCacheBox(mongoose, options, cb) { 14 | 15 | // make sure options is declared 16 | options = options || {}; 17 | 18 | // get our adapter `memory` is assumed by default 19 | options.engine = options.engine || 'memory'; 20 | 21 | // default time to live in ms 22 | var TTL = options.ttl || 60 23 | , CACHED = 'cache' in options ? options.cache : false 24 | , Query = mongoose.Query 25 | , bind = Query.prototype.bind 26 | , exec = Query.prototype.exec 27 | , execFind = Query.prototype.execFind; 28 | 29 | /** 30 | * Bind query options. 31 | * 32 | * @api private 33 | */ 34 | 35 | Query.prototype.bind = function (model, op, updateArg) { 36 | 37 | bind.call(this, model, op, updateArg); 38 | 39 | var obj = {}; 40 | 41 | this._recache = false; 42 | this._expiredCb = null; 43 | this._hasMiddleware = false; 44 | 45 | // generate cache key 46 | for (var k in this) obj[k] = this[k]; 47 | 48 | obj.model = this.model.modelName; 49 | 50 | this.key = JSON.stringify(obj); 51 | 52 | this.initCache(model.schema.options); 53 | 54 | return this; 55 | }; 56 | 57 | /** 58 | * Initialize cache and set instance. 59 | * 60 | * @param {Object} opt 61 | * @return {Query} this 62 | * @api private 63 | */ 64 | 65 | Query.prototype.initCache = function (opt) { 66 | 67 | // setting cache instance 68 | this._cache = Cache(this, options, finder(this, execFind)); 69 | 70 | if ('cache' in opt) { 71 | this._cache.cache(opt.cache); 72 | } 73 | 74 | if ('ttl' in opt && 'number' === typeof opt.ttl) { 75 | this._cache.ttl(opt.ttl); 76 | } 77 | 78 | return this; 79 | 80 | }; 81 | 82 | /** 83 | * Time to live setter and getter. 84 | * 85 | * @param {Number} ttl 86 | * @return {Query|Number} this 87 | * @api public 88 | */ 89 | 90 | Query.prototype.ttl = function (ttl) { 91 | var cache = this._cache; 92 | var _ttl = cache.ttl(); 93 | if (!arguments.length) return _ttl; 94 | if (_ttl !== ttl) { 95 | this._recache = true; 96 | cache.ttl(ttl); 97 | } 98 | return this; 99 | }; 100 | 101 | /** 102 | * Time to live setter and getter. 103 | * 104 | * @param {Boolean} cached 105 | * @param {Number} ttl 106 | * @return {Query} this 107 | * @api public 108 | */ 109 | 110 | Query.prototype.cache = function (cached, ttl) { 111 | 112 | if (!arguments.length) { 113 | cached = true; 114 | } else { 115 | 116 | if (!this._hasMiddleware) { 117 | if ('function' === typeof cached) { 118 | this._cache.use(cached); 119 | this._hasMiddleware = true; 120 | } 121 | 122 | if ('function' === typeof ttl) { 123 | this._cache.use(ttl); 124 | this._hasMiddleware = true; 125 | } 126 | } 127 | 128 | if (/string|number/.test(typeof cached)) { 129 | ttl = cached; 130 | cached = true; 131 | } 132 | } 133 | 134 | if ('undefined' !== typeof ttl) { 135 | this.ttl(ttl); 136 | } 137 | 138 | this._cache.cache(!!cached); 139 | 140 | return this; 141 | }; 142 | 143 | /** 144 | * Execute find. 145 | * 146 | * @param {Function} fn 147 | * @return {Query} this 148 | * @api private 149 | */ 150 | 151 | Query.prototype.execFind = function (fn) { 152 | if (!this._cache || !this._cache.cache()) return execFind.call(this, fn); 153 | this._cache.autocache(fn); 154 | return this; 155 | }; 156 | 157 | /** 158 | * Execute query. 159 | * 160 | * @param {String|Function} op 161 | * @param {Function} fn 162 | * @return {Query} this 163 | * @api private 164 | */ 165 | 166 | Query.prototype.exec = function (op, fn) { 167 | if (!this.cached) return exec.call(this, op, fn); 168 | if ('function' === typeof op) { 169 | fn = op; 170 | op = null; 171 | } 172 | exec.call(this, op, fn); 173 | return this; 174 | }; 175 | 176 | /** 177 | * Check if results from this query is from cache. 178 | * 179 | * @type {Boolean} 180 | * @api public 181 | */ 182 | 183 | Object.defineProperty(Query.prototype, 'isFromCache', { 184 | get: function () { 185 | return !!(this._cache && this._cache.cached()); 186 | } 187 | }); 188 | 189 | /** 190 | * Check if this query has caching enabled. 191 | * 192 | * @type {Boolean} 193 | * @api public 194 | */ 195 | 196 | Object.defineProperty(Query.prototype, 'isCacheEnabled', { 197 | get: function () { 198 | return !!(this._cache && this._cache.cache()); 199 | } 200 | }); 201 | 202 | return mongoose; 203 | 204 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mongoose-cachebox", 3 | "version": "1.2.1", 4 | "description": "Cache mongoose queries in memory, redis or use your own driver.", 5 | "author": "Jonathan Brumley ", 6 | "homepage": "https://github.com/cayasso/mongoose-cachebox", 7 | "main": "index.js", 8 | "scripts": { 9 | "test": "make test" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/cayasso/mongoose-cachebox.git" 14 | }, 15 | "keywords": [ 16 | "plugin", 17 | "mongoose", 18 | "query", 19 | "cache", 20 | "data", 21 | "mongo", 22 | "mongodb", 23 | "redis", 24 | "store", 25 | "memory", 26 | "catbox", 27 | "ttl" 28 | ], 29 | "license": "MIT", 30 | "dependencies": { 31 | "debug": "0.7.x", 32 | "cacheman": "~1.x.x", 33 | "cacheman-redis": "~0.1.0" 34 | }, 35 | "devDependencies": { 36 | "expect.js": "*", 37 | "mocha": "*", 38 | "mongoose": "~3.6.x", 39 | "pre-commit": "0.0.9", 40 | "redis": "0.8.x" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var mongooseCachebox = require('../') 2 | , mongoose = require('mongoose') 3 | , expect = require('expect.js') 4 | , Schema = mongoose.Schema 5 | , PeopleSchema 6 | , People 7 | , db 8 | , names = ["Jacob", "Sophia", "Mason", "Isabella", "William", "Emma", "Jayden", "Olivia", "Noah", "Ava", "Michael", "Emily", "Ethan", "Abigail", "Alexander", "Madison", "Aiden", "Mia", "Daniel", "Chloe"]; 9 | 10 | describe('mongoose-cachebox', function () { 11 | 12 | before(function (done) { 13 | // connecting to mongoose 14 | mongoose.connect('mongodb://127.0.0.1/mongoose-cachebox-testing'); 15 | 16 | db = mongoose.connection; 17 | 18 | db.on('error', function (err) { 19 | done(err); 20 | }); 21 | 22 | db.once('open', done); 23 | 24 | // adding mongoose cachebox 25 | mongooseCachebox(mongoose, { engine: 'memory' }); 26 | 27 | PeopleSchema = new Schema({ 28 | name: String, 29 | date: { 30 | type: String, 31 | "default": Date.now() 32 | }, 33 | num: Number, 34 | test: Boolean 35 | }); 36 | 37 | People = mongoose.model('People', PeopleSchema); 38 | }); 39 | 40 | function generate (amount, fn) { 41 | var crowd = []; 42 | var count = 0; 43 | while (count < amount) { 44 | crowd.push({ 45 | name: names[Math.floor(Math.random() * names.length)], 46 | num: Math.random() * 10000 47 | }); 48 | count++; 49 | } 50 | People.create(crowd, fn); 51 | } 52 | 53 | beforeEach(function(done){ 54 | generate(10, done); 55 | }); 56 | 57 | afterEach(function(done){ 58 | People.remove(done); 59 | }); 60 | 61 | it('should have `cache` method', function () { 62 | expect(People.find({}).cache).to.be.a('function'); 63 | }); 64 | 65 | it('should not cache query if `cache` method is not called', function (done) { 66 | var query = People.find({}); 67 | query.exec(function (err, docs) { 68 | if (err) return done(err); 69 | People.find({}).exec(function (err, docs) { 70 | if (err) return done(err); 71 | if (docs) { 72 | expect(query.isFromCache).to.be(false); 73 | done(); 74 | } 75 | }); 76 | }); 77 | }); 78 | 79 | it('should cache query if the `cache` method is called', function (done) { 80 | this.timeout(0); 81 | var query = People.find({}); 82 | query.cache(1).exec(function (err, docs) { 83 | if (err) return done(err); 84 | People.find({}).exec(function (err, docs) { 85 | if (err) return done(err); 86 | People.find({}).exec(function (err, docs) { 87 | if (err) return done(err); 88 | People.find({}).exec(function (err, docs) { 89 | if (err) return done(err); 90 | People.find({}).exec(function (err, docs) { 91 | if (err) return done(err); 92 | People.find({}).exec(function (err, docs) { 93 | if (err) return done(err); 94 | if (docs) { 95 | expect(docs).to.be.ok(); 96 | expect(query.isFromCache).to.be(true); 97 | done(); 98 | } 99 | }); 100 | }); 101 | }); 102 | }); 103 | }); 104 | }); 105 | }); 106 | 107 | it('should work with lean enabled', function (done) { 108 | var query = People.find({}); 109 | query.lean().cache().exec(function (err, docs) { 110 | if (err) return done(err); 111 | People.find({}).exec(function (err, docs) { 112 | if (err) return done(err); 113 | if (docs) { 114 | expect(query.isFromCache).to.be(true); 115 | done(); 116 | } 117 | }); 118 | }); 119 | }); 120 | 121 | it('should cache query with specific ttl if passed to `cache` method', function (done) { 122 | var query = People.find({}); 123 | query.cache(30).exec(function (err, docs) { 124 | if (err) return done(err); 125 | People.find({}).exec(function (err, docs) { 126 | if (err) return done(err); 127 | if (docs) { 128 | expect(query.isFromCache).to.be(true); 129 | done(); 130 | } 131 | }); 132 | }); 133 | }); 134 | 135 | it('should cache query with specific ttl is passed to `ttl` method', function (done) { 136 | var query = People.find({}); 137 | query.cache().ttl(50).exec(function (err, docs) { 138 | if (err) return done(err); 139 | People.find({}).exec(function (err, docs) { 140 | if (err) return done(err); 141 | if (docs) { 142 | expect(query.ttl()).to.be(50); 143 | expect(query.isFromCache).to.be(true); 144 | done(); 145 | } 146 | }); 147 | }); 148 | }); 149 | 150 | it('should cache query with specific human readable ttl is passed to `ttl` method', function (done) { 151 | var query = People.find({}); 152 | query.cache().ttl('5s').exec(function (err, docs) { 153 | if (err) return done(err); 154 | People.find({}).exec(function (err, docs) { 155 | if (err) return done(err); 156 | if (docs) { 157 | expect(query.ttl()).to.be('5s'); 158 | expect(query.isFromCache).to.be(true); 159 | done(); 160 | } 161 | }); 162 | }); 163 | }); 164 | 165 | it('should stop caching', function (done) { 166 | var query = People.find({}); 167 | query.cache().exec(function (err, docs) { 168 | if (err) return done(err); 169 | People.find({}).cache(false).exec(function (err, docs) { 170 | if (err) return done(err); 171 | if (docs) { 172 | expect(query.isCacheEnabled).to.be(false); 173 | done(); 174 | } 175 | }); 176 | }); 177 | }); 178 | 179 | it('should cache all queries by setting cache property to true on the schema', function (done) { 180 | var ModelSchema = new Schema({ field: String }); 181 | var Model = mongoose.model('A', ModelSchema); 182 | ModelSchema.set('cache', true); 183 | Model.create([{ field: 'a' }, { field: 'b' }, { field: 'c' }], function () { 184 | var query = Model.find({}); 185 | query.exec(function (err, docs) { 186 | if (err) return done(err); 187 | Model.find({}).exec(function (err, docs) { 188 | if (err) return done(err); 189 | if (docs) { 190 | expect(query.isFromCache).to.be(true); 191 | Model.remove(); 192 | done(); 193 | } 194 | }); 195 | }); 196 | }); 197 | }); 198 | 199 | it('should allow setting default ttl from schema', function (done) { 200 | var ModelSchema = new Schema({ a: String, b: Number }); 201 | var Model = mongoose.model('B', ModelSchema); 202 | 203 | ModelSchema.set('cache', true); 204 | ModelSchema.set('ttl', 90); 205 | 206 | Model.create([{ a: 'a', b: 1 }, { a: 'b', b: 1 }, { a: 'c', b: 1 }], function () { 207 | var query = Model.find({ a: 'a'}, function (err, docs) { 208 | if (err) return done(err); 209 | Model.find({ a: 'a'}, function (err, docs) { 210 | if (err) return done(err); 211 | if (docs) { 212 | expect(query.ttl()).to.be(90); 213 | Model.remove(); 214 | done(); 215 | } 216 | }); 217 | }); 218 | }); 219 | }); 220 | 221 | it('should allow passing middleware for overwriting data', function (done) { 222 | var query = People.find({}); 223 | var overwrite = ['overwrited']; 224 | query.cache(function (key, data, ttl, next) { 225 | if (data) return next(null, overwrite, ttl); 226 | next(); 227 | }).exec(function (err, docs) { 228 | if (err) return done(err); 229 | People.find({}).exec(function (err, docs) { 230 | if (err) return done(err); 231 | if (docs) { 232 | expect(docs).to.be(overwrite); 233 | done(); 234 | } 235 | }); 236 | }); 237 | }); 238 | 239 | after(function(done){ 240 | mongoose.disconnect(); 241 | done(); 242 | }); 243 | 244 | }); 245 | 246 | --------------------------------------------------------------------------------