Skip to content

Instantly share code, notes, and snippets.

@zisan34
Created October 8, 2022 22:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zisan34/64f502944920550db2347fde9d21ed44 to your computer and use it in GitHub Desktop.
Save zisan34/64f502944920550db2347fde9d21ed44 to your computer and use it in GitHub Desktop.
/**
* This file is responsible for caching mongo model instance for specific time.
* Usage example:
* Store.findOne(query, fields).cacheResults(1 * 60).exec(); // it'll cache result for specified seconds
* Store.findOne(query, fields).removeCache().exec(); // it'll remove any existing cache
*/
// @ts-nocheck
import mongoose from 'mongoose';
import redisClient from './redisClient';
mongoose.Query.prototype.cacheResults = function (time: number) {
this.cache_results = true;
this.cacheTimeSeconds = time;
return this;
};
mongoose.Query.prototype.removeCached = function () {
this.remove_cached = true;
return this;
};
mongoose.Query.prototype.getCacheKey = function () {
const key = JSON.stringify({
collectionName: this.mongooseCollection.name,
op: this.op,
options: this.getOptions(),
filter: this.getFilter(),
projection: this.projection(),
populatedPaths: this.getPopulatedPaths(),
});
return key;
};
const exec = mongoose.Query.prototype.exec;
mongoose.Query.prototype.exec = async function () {
if (this.remove_cached) {
// remove_cached is true so remove cache for the key
const key = this.getCacheKey();
redisClient.del(key);
}
if (this.cache_results) {
// cache_results is true so cache result for the key
const key = this.getCacheKey();
const cachedResults = await redisClient.get(key);
if (cachedResults) {
// if you found cached results return it;
const result = JSON.parse(cachedResults);
const options = this.getOptions();
const projection = this.projection();
// create model instance again from the results so that you get all the model functionalities with cached data
return Array.isArray(result)
? // eslint-disable-next-line new-cap
result.map((document) => new this.model(document, projection, options))
: // eslint-disable-next-line new-cap
new this.model(result, projection, options);
} else {
// get results from Database then cache it
const result = await exec.apply(this, arguments);
redisClient.set(key, JSON.stringify(result), 'EX', this.cacheTimeSeconds);
return result;
}
} else {
return exec.apply(this, arguments);
}
};
@Ifmr24
Copy link

Ifmr24 commented Nov 4, 2022

Thanks but, not working with populate ?

@suhrud-caizin
Copy link

I am trying to implement exactly the same logic in my app but
const result = await exec.apply(this, arguments);
this part of the code is giving me null in case of findOne and find calls even when matching documents are present in the DB
any idea how to overcome this problem?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment