Cache with mongoose ObjectId will fail to find the keyFieldName in cache
See original GitHub issueFirst just wanted to thank you for this awesome tool, and wanted to inform that when using the cache hook with feathers mongoose as a service in the after method it will return a document with the _id field of type ObjectId not a string, thus will fail to find it when searching the cache index.
Example of a way am calling a service and returning a document
class BlogModel {
async get(id, params) {
return app.getService('posts').get(id, {query: {$populate: 'owner'}});
}
}
module.exports = {
before: {
all: cache(cacheMap, '_id')
},
after: {
all: cache(cacheMap, '_id')
}
};
The fix am using, or should I have used a transformation hook before the caching
'use strict';
const common = require('feathers-hooks-common');
const mongoose = require("mongoose");
module.exports = function (cacheMap, keyFieldName, options = {}) {
const clone = options.clone || defaultClone;
return context => {
keyFieldName = keyFieldName || (context.service || {}).id; // Will be undefined on client
let items = common.getItems(context);
items = Array.isArray(items) ? items : [items];
if (context.type === 'after') {
if (context.method === 'remove') {
return;
}
items.forEach(item => {
const idName = getIdName(keyFieldName, item);
// cacheMap.set(item[idName], clone(item));
cacheMap.set(objectIdToString(item[idName]), clone(item));
});
return;
}
switch (context.method) {
case 'find': // fall through
case 'create':
return;
case 'get': {
const value = cacheMap.get(context.id);
if (value) {
context.result = value;
}
return context;
}
default: // update, patch, remove
if (context.id) {
cacheMap.delete(context.id);
return;
}
items.forEach(item => {
const idName = getIdName(keyFieldName, item);
// cacheMap.delete(item[idName]);
cacheMap.delete(objectIdToString(item[idName]));
});
}
};
};
function getIdName (keyFieldName, item) {
if (keyFieldName) return keyFieldName;
return ('_id' in item) ? '_id' : 'id';
}
function defaultClone (obj) {
return JSON.parse(JSON.stringify(obj));
}
function objectIdToString (id) {
return (id instanceof mongoose.Types.ObjectId) ? id.toString() : id;
}
Issue Analytics
- State:
- Created 5 years ago
- Comments:7 (1 by maintainers)
Top Results From Across the Web
Cache with mongoose ObjectId will fail to find the ... - GitHub
Cache with mongoose ObjectId will fail to find the keyFieldName in cache #424 ... module.exports = function (cacheMap, keyFieldName, ...
Read more >What's Mongoose error Cast to ObjectId failed for value XXX at ...
Mongoose's findById method casts the id parameter to the type of the model's _id field so that it can properly query for the...
Read more >Better caching logic by overwriting mongoose functionality
We will overwrite find method in mongoose library to search the Redis cache instead of generating the query directly to DB.
Read more >mongoose-redis - npm
Best Performance Mongoose caching module that works exactly how you would expect it to, with the latest version of Mongoose. Installation.
Read more >module keystone function - GitHub Pages
keyToPath(key, true); this.schema = new keystone.mongoose. ... emptyOption; // cached maps for options, labels and values this.map = utils.
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
The users service would be one of the better places to use
cache
if possible.There are several complications:
authenticate
andrestrictToUser
both call the users service if its an external call.context.result
, ascache
does, does not prevent the following hooks from running. It just prevents the DB call once all the before hooks have run.cache
from running ifcache
found a users record, then aftercache
you would need a hook likeiff(ctx => ctx.result && !ctx.provider, skipRemainingHooks())
.context.result
is set. They assume they have to processcontext.data
. Such hooks would ignore the record found bycache
, processcontext.data
needlessly ascontext.data
found be ignored as the DB call is not made.So using
cache
on the users service depends on the circumstances.The good news is
restrictToUser
andauthenticate
return immediately if the call has been made by the server. So if your hooks are only:The following hooks execute:
Which looks like it should work. Let me know your results should you decide to try it.
Yes its working now, also am testing it on the Users service I had to make sure in every place in code where I test for the users ID or any ID to do it using a helper function, since it could be either a string or an ObjectId depending if it came from the cacheMap or DB now. Its nice especially with web-socket it stopped hitting the DB, and that kind of feels wrong with the users case am not sure if its safe but I will keep on testing and see what happens. thanks for the help
// helper function
Reviewed thedocs and the makeCacheKey in the docs should have been