From 509be067c31683e49ca4f930cb3392ce8ca58a57 Mon Sep 17 00:00:00 2001 From: Ruben Vereecken Date: Wed, 4 Jun 2014 20:47:32 +0200 Subject: [PATCH] Added support for the operator in LocalMongo --- app/lib/LocalMongo.coffee | 7 +- .../earned_achievement_handler.coffee | 29 ++++--- test/app/lib/local_mongo.spec.coffee | 86 ++++++++++--------- 3 files changed, 67 insertions(+), 55 deletions(-) diff --git a/app/lib/LocalMongo.coffee b/app/lib/LocalMongo.coffee index cdd8fd50e..2027e1c70 100644 --- a/app/lib/LocalMongo.coffee +++ b/app/lib/LocalMongo.coffee @@ -18,6 +18,7 @@ doQuerySelector = (value, operatorObj) -> when '$ne' then return false if mapred value, body, (l, r) -> l == r when '$in' then return false unless _.reduce value, ((result, val) -> result or val in body), false when '$nin' then return false if _.reduce value, ((result, val) -> result or val in body), false + when '$exists' then return false if value[0]? isnt body[0] else return false true @@ -34,11 +35,13 @@ matchesQuery = (target, queryObj) -> pieces = prop.split('.') obj = target for piece in pieces - return false unless piece of obj + unless piece of obj + obj = null + break obj = obj[piece] if typeof query != 'object' or _.isArray query return false unless obj == query or (query in obj if _.isArray obj) else return false unless doQuerySelector obj, query true -LocalMongo.matchesQuery = matchesQuery \ No newline at end of file +LocalMongo.matchesQuery = matchesQuery diff --git a/server/achievements/earned_achievement_handler.coffee b/server/achievements/earned_achievement_handler.coffee index 80570b8d0..0fefc5842 100644 --- a/server/achievements/earned_achievement_handler.coffee +++ b/server/achievements/earned_achievement_handler.coffee @@ -17,45 +17,45 @@ class EarnedAchievementHandler extends Handler recalculate: (req, res) -> onSuccess = (data) => @sendSuccess(res, data) if 'achievements' of req.query # Support both slugs and IDs separated by commas - achievementSlugsOrIDs = req.query.id.split(',') + achievementSlugsOrIDs = req.query.achievements.split(',') EarnedAchievementHandler.recalculate achievementSlugsOrIDs, onSuccess else EarnedAchievementHandler.recalculate onSuccess - @sendSuccess res + @sendSuccess res # Returns success: boolean @recalculate: (callbackOrSlugsOrIDs, callback) -> if _.isArray callbackOrSlugsOrIDs - achievementSlugs = (thing unless Handler.isID(thing) for thing in callbackOrSlugsOrIDs) - achievementIDs = (thing if Handler.isID(thing) for thing in callbackOrSlugsOrIDs) + achievementSlugs = (thing for thing in callbackOrSlugsOrIDs when not Handler.isID(thing)) + achievementIDs = (thing for thing in callbackOrSlugsOrIDs when Handler.isID(thing)) else callback = callbackOrSlugsOrIDs filter = {} filter.$or = [ - _id: $in: achievementIDs - slug: $in: achievementSlugs + {_id: $in: achievementIDs}, + {slug: $in: achievementSlugs} ] if achievementSlugs? or achievementIDs? Achievement.find filter, (err, achievements) -> return false and log.error err if err? User.find {}, (err, users) -> _.each users, (user) -> - # Keep track of a user's already achieved so as to set the notified values correctly + # Keep track of a user's already achieved in order to set the notified values correctly userID = user.get('_id').toHexString() EarnedAchievement.find {user: userID}, (err, alreadyEarned) -> alreadyEarnedIDs = [] previousPoints = 0 _.each alreadyEarned, (earned) -> - alreadyEarnedIDs.push earned.get('achievement') - previousPoints += earned.get 'earnedPoints' + if (_.find achievements, (single) -> earned.get('achievement') is single.get('_id').toHexString()) + alreadyEarnedIDs.push earned.get('achievement') + previousPoints += earned.get 'earnedPoints' # TODO maybe also delete earned? Make sure you don't delete too many newTotalPoints = 0 earnedAchievementSaverGenerator = (achievement) -> (callback) -> - log.debug 'Checking out tha fancy achievement' isRepeatable = achievement.get('proportionalTo')? model = mongoose.model(achievement.get('collection')) if not model? @@ -78,9 +78,12 @@ class EarnedAchievementHandler extends Handler earned.previouslyAchievedAmount = 0 expFunction = achievement.getExpFunction() - newTotalPoints += expFunction(earned.achievedAmount) * achievement.get('worth') + newPoints = expFunction(earned.achievedAmount) * achievement.get('worth') else - newTotalPoints += achievement.get 'worth' + newPoints = achievement.get 'worth' + + earned.earnedPoints = newPoints + newTotalPoints += newPoints EarnedAchievement.update {achievement:earned.achievement, user:earned.user}, earned, {upsert: true}, (err) -> log.error err if err? @@ -93,11 +96,13 @@ class EarnedAchievementHandler extends Handler if _.isEmpty filter # Completely clean User.update {_id: userID}, {$set: points: newTotalPoints}, {}, (err) -> log.error err if err? else + log.debug "Incrementing score for these achievements with #{newTotalPoints - previousPoints}" User.update {_id: userID}, {$inc: points: newTotalPoints - previousPoints}, {}, (err) -> log.error err if err? earnedAchievementSavers = (earnedAchievementSaverGenerator(achievement) for achievement in achievements) earnedAchievementSavers.push saveUserPoints + # We need to have all these database updates chained so we know the final score async.series earnedAchievementSavers diff --git a/test/app/lib/local_mongo.spec.coffee b/test/app/lib/local_mongo.spec.coffee index 6aa22a8f1..f3719021e 100644 --- a/test/app/lib/local_mongo.spec.coffee +++ b/test/app/lib/local_mongo.spec.coffee @@ -2,7 +2,7 @@ describe 'Local Mongo queries', -> LocalMongo = require 'lib/LocalMongo' beforeEach -> - this.fixture1 = + @fixture1 = 'id': 'somestring' 'value': 9000 'levels': [3, 8, 21] @@ -10,68 +10,72 @@ describe 'Local Mongo queries', -> 'type': 'unicorn' 'likes': ['poptarts', 'popsicles', 'popcorn'] - this.fixture2 = this: is: so: 'deep' + @fixture2 = this: is: so: 'deep' it 'regular match of a property', -> - expect(LocalMongo.matchesQuery(this.fixture1, 'gender': 'unicorn')).toBeFalsy() - expect(LocalMongo.matchesQuery(this.fixture1, 'type':'unicorn')).toBeTruthy() - expect(LocalMongo.matchesQuery(this.fixture1, 'type':'zebra')).toBeFalsy() - expect(LocalMongo.matchesQuery(this.fixture1, 'type':'unicorn', 'id':'somestring')).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, 'gender': 'unicorn')).toBeFalsy() + expect(LocalMongo.matchesQuery(@fixture1, 'type':'unicorn')).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, 'type':'zebra')).toBeFalsy() + expect(LocalMongo.matchesQuery(@fixture1, 'type':'unicorn', 'id':'somestring')).toBeTruthy() it 'array match of a property', -> - expect(LocalMongo.matchesQuery(this.fixture1, 'likes':'poptarts')).toBeTruthy() - expect(LocalMongo.matchesQuery(this.fixture1, 'likes':'walks on the beach')).toBeFalsy() + expect(LocalMongo.matchesQuery(@fixture1, 'likes':'poptarts')).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, 'likes':'walks on the beach')).toBeFalsy() it 'nested match', -> - expect(LocalMongo.matchesQuery(this.fixture2, 'this.is.so':'deep')).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture2, 'this.is.so':'deep')).toBeTruthy() it '$gt selector', -> - expect(LocalMongo.matchesQuery(this.fixture1, 'value': '$gt': 8000)).toBeTruthy() - expect(LocalMongo.matchesQuery(this.fixture1, 'value': '$gt': [8000, 10000])).toBeTruthy() - expect(LocalMongo.matchesQuery(this.fixture1, 'levels': '$gt': [10, 20, 30])).toBeTruthy() - expect(LocalMongo.matchesQuery(this.fixture1, 'value': '$gt': 9000)).toBeFalsy() - expect(LocalMongo.matchesQuery(this.fixture1, 'value': {'$gt': 8000}, 'worth': {'$gt': 5})).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, 'value': '$gt': 8000)).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, 'value': '$gt': [8000, 10000])).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, 'levels': '$gt': [10, 20, 30])).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, 'value': '$gt': 9000)).toBeFalsy() + expect(LocalMongo.matchesQuery(@fixture1, 'value': {'$gt': 8000}, 'worth': {'$gt': 5})).toBeTruthy() it '$gte selector', -> - expect(LocalMongo.matchesQuery(this.fixture1, 'value': '$gte': 9001)).toBeFalsy() - expect(LocalMongo.matchesQuery(this.fixture1, 'value': '$gte': 9000)).toBeTruthy() - expect(LocalMongo.matchesQuery(this.fixture1, 'value': '$gte': [9000, 10000])).toBeTruthy() - expect(LocalMongo.matchesQuery(this.fixture1, 'levels': '$gte': [21, 30])).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, 'value': '$gte': 9001)).toBeFalsy() + expect(LocalMongo.matchesQuery(@fixture1, 'value': '$gte': 9000)).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, 'value': '$gte': [9000, 10000])).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, 'levels': '$gte': [21, 30])).toBeTruthy() it '$lt selector', -> - expect(LocalMongo.matchesQuery(this.fixture1, 'value': '$lt': 9001)).toBeTruthy() - expect(LocalMongo.matchesQuery(this.fixture1, 'value': '$lt': 9000)).toBeFalsy() - expect(LocalMongo.matchesQuery(this.fixture1, 'value': '$lt': [9001, 9000])).toBeTruthy() - expect(LocalMongo.matchesQuery(this.fixture1, 'levels': '$lt': [10, 20, 30])).toBeTruthy() - expect(LocalMongo.matchesQuery(this.fixture1, 'value': {'$lt': 9001}, 'worth': {'$lt': 7})).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, 'value': '$lt': 9001)).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, 'value': '$lt': 9000)).toBeFalsy() + expect(LocalMongo.matchesQuery(@fixture1, 'value': '$lt': [9001, 9000])).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, 'levels': '$lt': [10, 20, 30])).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, 'value': {'$lt': 9001}, 'worth': {'$lt': 7})).toBeTruthy() it '$lte selector', -> - expect(LocalMongo.matchesQuery(this.fixture1, 'value': '$lte': 9000)).toBeTruthy() - expect(LocalMongo.matchesQuery(this.fixture1, 'value': '$lte': 8000)).toBeFalsy() - expect(LocalMongo.matchesQuery(this.fixture1, 'value': {'$lte': 9000}, 'worth': {'$lte': [6, 5]})).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, 'value': '$lte': 9000)).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, 'value': '$lte': 8000)).toBeFalsy() + expect(LocalMongo.matchesQuery(@fixture1, 'value': {'$lte': 9000}, 'worth': {'$lte': [6, 5]})).toBeTruthy() it '$ne selector', -> - expect(LocalMongo.matchesQuery(this.fixture1, 'value': '$ne': 9000)).toBeFalsy() - expect(LocalMongo.matchesQuery(this.fixture1, 'id': '$ne': 'otherstring')).toBeTruthy() - expect(LocalMongo.matchesQuery(this.fixture1, 'id': '$ne': ['otherstring', 'somestring'])).toBeFalsy() - expect(LocalMongo.matchesQuery(this.fixture1, 'likes': '$ne': ['popcorn', 'chicken'])).toBeFalsy() + expect(LocalMongo.matchesQuery(@fixture1, 'value': '$ne': 9000)).toBeFalsy() + expect(LocalMongo.matchesQuery(@fixture1, 'id': '$ne': 'otherstring')).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, 'id': '$ne': ['otherstring', 'somestring'])).toBeFalsy() + expect(LocalMongo.matchesQuery(@fixture1, 'likes': '$ne': ['popcorn', 'chicken'])).toBeFalsy() it '$in selector', -> - expect(LocalMongo.matchesQuery(this.fixture1, 'type': '$in': ['unicorn', 'zebra'])).toBeTruthy() - expect(LocalMongo.matchesQuery(this.fixture1, 'type': '$in': ['cats', 'dogs'])).toBeFalsy() - expect(LocalMongo.matchesQuery(this.fixture1, 'likes': '$in': ['popcorn', 'chicken'])).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, 'type': '$in': ['unicorn', 'zebra'])).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, 'type': '$in': ['cats', 'dogs'])).toBeFalsy() + expect(LocalMongo.matchesQuery(@fixture1, 'likes': '$in': ['popcorn', 'chicken'])).toBeTruthy() it '$nin selector', -> - expect(LocalMongo.matchesQuery(this.fixture1, 'type': '$nin': ['unicorn', 'zebra'])).toBeFalsy() - expect(LocalMongo.matchesQuery(this.fixture1, 'type': '$nin': ['cats', 'dogs'])).toBeTruthy() - expect(LocalMongo.matchesQuery(this.fixture1, 'likes': '$nin': ['popcorn', 'chicken'])).toBeFalsy() + expect(LocalMongo.matchesQuery(@fixture1, 'type': '$nin': ['unicorn', 'zebra'])).toBeFalsy() + expect(LocalMongo.matchesQuery(@fixture1, 'type': '$nin': ['cats', 'dogs'])).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, 'likes': '$nin': ['popcorn', 'chicken'])).toBeFalsy() it '$or operator', -> - expect(LocalMongo.matchesQuery(this.fixture1, $or: [{value:9000}, {type:'zebra'}])).toBeTruthy() - expect(LocalMongo.matchesQuery(this.fixture1, $or: [{value:9001}, {worth:$lt:10}])).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, $or: [{value:9000}, {type:'zebra'}])).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, $or: [{value:9001}, {worth:$lt:10}])).toBeTruthy() it '$and operator', -> - expect(LocalMongo.matchesQuery(this.fixture1, $and: [{value:9000}, {type:'zebra'}])).toBeFalsy() - expect(LocalMongo.matchesQuery(this.fixture1, $and: [{value:9000}, {type:'unicorn'}])).toBeTruthy() - expect(LocalMongo.matchesQuery(this.fixture1, $and: [{value:$gte:9000}, {worth:$lt:10}])).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, $and: [{value:9000}, {type:'zebra'}])).toBeFalsy() + expect(LocalMongo.matchesQuery(@fixture1, $and: [{value:9000}, {type:'unicorn'}])).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, $and: [{value:$gte:9000}, {worth:$lt:10}])).toBeTruthy() + + it '$exists operator', -> + expect(LocalMongo.matchesQuery(@fixture1, type: $exists: true)).toBeTruthy() + expect(LocalMongo.matchesQuery(@fixture1, interesting: $exists: false)).toBeTruthy()