From e8404e48c6890362aed55d5360efee978413420e Mon Sep 17 00:00:00 2001 From: David Worms Date: Thu, 15 Mar 2012 22:48:38 +0100 Subject: [PATCH] Rename `id` method to `identify`; Also the creation of records with a defined identifier; Optimize id incremental creation --- lib/Records.coffee | 63 +++++++++++++++-------------- test/create.coffee | 51 +++++++++++++---------- test/{id.coffee => identify.coffee} | 44 ++++++++++---------- 3 files changed, 85 insertions(+), 73 deletions(-) rename test/{id.coffee => identify.coffee} (80%) diff --git a/lib/Records.coffee b/lib/Records.coffee index ee2d0ef..7b3f573 100644 --- a/lib/Records.coffee +++ b/lib/Records.coffee @@ -223,27 +223,31 @@ module.exports = class Records extends Schema # Get current date once if schema is temporal date = new Date Date.now() if temporal? # Generate new identifiers - multi.incr "#{db}:#{name}_incr" for x in records - multi.exec (err, recordIds) => + incr = 0 + for record in records then incr++ unless record[identifier] + multi.incrby "#{db}:#{name}_incr", incr + multi.exec (err, recordId) => + recordId = recordId - incr return callback err if err multi = redis.multi() for record, i in records + recordId++ unless record[identifier] # Enrich the record with its identifier - record[identifier] = recordId = recordIds[i] + record[identifier] = recordId unless record[identifier] # Enrich the record with a creation date record[temporal.creation] = date if temporal?.creation? and not record[temporal.creation]? # Enrich the record with a creation date record[temporal.modification] = date if temporal?.modification? and not record[temporal.modification]? # Register new identifier - multi.sadd "#{db}:#{name}_#{identifier}", recordId + multi.sadd "#{db}:#{name}_#{identifier}", record[identifier] # Deal with Unique for property of unique - multi.hset "#{db}:#{name}_#{property}", record[property], recordId if record[property] + multi.hset "#{db}:#{name}_#{property}", record[property], record[identifier] if record[property] # Deal with Index for property of index value = record[property] value = hash value - multi.sadd "#{db}:#{name}_#{property}:#{value}", recordId + multi.sadd "#{db}:#{name}_#{property}:#{value}", record[identifier] #multi.zadd "#{s.db}:#{s.name}_#{property}", 0, record[property] # Store the record r = {} @@ -253,7 +257,7 @@ module.exports = class Records extends Schema # Filter null values r[property] = value if value? @serialize r - multi.hmset "#{db}:#{name}:#{recordId}", r + multi.hmset "#{db}:#{name}:#{record[identifier]}", r multi.exec (err, results) => return callback err if err for result in results @@ -313,7 +317,7 @@ module.exports = class Records extends Schema `options` All options are optional. Options properties include: - * `properties` Array of properties to fetch, all properties if not defined. + * `properties` Array of properties to fetch, all properties unless defined. * `force` Force the retrieval of properties even if already present in the record objects. * `accept_null` Skip objects if they are provided as null. * `object` If `true`, return an object where keys are the identifier and value are the fetched records @@ -338,7 +342,7 @@ module.exports = class Records extends Schema if options.accept_null? and not records.some((record) -> record isnt null) return callback null, if isArray then records else records[0] # Retrieve records identifiers - @id records, {object: true, accept_null: options.accept_null?}, (err, records) => + @identify records, {object: true, accept_null: options.accept_null?}, (err, records) => return callback err if err cmds = [] records.forEach (record, i) -> @@ -374,8 +378,8 @@ module.exports = class Records extends Schema callback null, if isArray then records else records[0] ### - `id(records, [options], callback)` - ---------------------------------- + `identify(records, [options], callback)` + ---------------------------------------- Extract record identifiers or set the identifier to null if its associated record could not be found. The method doesn't hit the database to validate record values and if an id is @@ -414,7 +418,7 @@ module.exports = class Records extends Schema console.log ids ### - id: (records, options, callback) -> + identify: (records, options, callback) -> if arguments.length is 2 callback = options options = {} @@ -447,20 +451,19 @@ module.exports = class Records extends Schema records[i][identifier] = record else return callback new Error 'Invalid id, got ' + (JSON.stringify record) - # No need to hit redis if no comand are registered - if cmds.length is 0 - if not options.object + finalize = -> + unless options.object records = for record in records if record? then record[identifier] else record - return callback null, if isArray then records else records[0] + callback null, if isArray then records else records[0] + # No need to hit redis if there is no command + return finalize() if cmds.length is 0 # Run the commands multi = redis.multi cmds multi.exec (err, results) => - unless options.object - records = for record in records - record[identifier] + return callback err if err @unserialize records - callback null, if isArray then records else records[0] + finalize() ### `list([options], callback)` @@ -566,7 +569,7 @@ module.exports = class Records extends Schema multi.del tempkey if tempkey multi.exec() ### - + `remove(records, callback)` --------------------------- Remove one or several records from the database. The function will also @@ -598,24 +601,24 @@ module.exports = class Records extends Schema return callback err if err callback null, records.length ### - + `update(records, [options], callback)` -------------------------------------- Update one or several records. The records must exists in the database or an error will be returned in the callback. The existence of a record may be discovered through its identifier or the presence of a unique property. - + `records` Record object or array of record objects. - + `options` Options properties include: - + * `validate` Validate the records. - + `callback` Called on success or failure. Received parameters are: - + * `err` Error object if any. * `records` Records with their newly created identifier. - + Records are not validated, it is the responsability of the client program calling `create` to either call `validate` before calling `create` or to passs the `validate` options. @@ -625,7 +628,7 @@ module.exports = class Records extends Schema username: 'my_username' age: 28 , (err, user) -> console.log user - + ### update: (records, options, callback) -> if arguments.length is 2 @@ -644,7 +647,7 @@ module.exports = class Records extends Schema # 2.1 Make sure the new property is not assigned to another record # 2.2 Erase old index & Create new index # 3. Save the record - @id records, {object: true}, (err, records) => + @identify records, {object: true}, (err, records) => return callback err if err # Stop here if a record is invalid for record in records diff --git a/test/create.coffee b/test/create.coffee index 43d8890..2ba756f 100644 --- a/test/create.coffee +++ b/test/create.coffee @@ -26,8 +26,8 @@ describe 'create', -> it 'Test create # one user', (next) -> Users.create - username: 'my_username', - email: 'my@email.com', + username: 'my_username' + email: 'my@email.com' password: 'my_password' , (err, user) -> should.not.exist err @@ -38,12 +38,12 @@ describe 'create', -> it 'Test create # multiple users', (next) -> Users.create [ - username: 'my_username_1', - email: 'my_first@email.com', + username: 'my_username_1' + email: 'my_first@email.com' password: 'my_password' , - username: 'my_username_2', - email: 'my_second@email.com', + username: 'my_username_2' + email: 'my_second@email.com' password: 'my_password' ], (err, users) -> should.not.exist err @@ -54,8 +54,8 @@ describe 'create', -> it 'Test create # existing id', (next) -> Users.create - username: 'my_username', - email: 'my@email.com', + username: 'my_username' + email: 'my@email.com' password: 'my_password' , (err, user) -> should.not.exist err @@ -76,8 +76,8 @@ describe 'create', -> , (err, user) -> should.not.exist err Users.create - username: 'my_username', - email: 'my@email.com', + username: 'my_username' + email: 'my@email.com' password: 'my_password' , (err, user) -> err.message.should.eql 'Record 1 already exists' @@ -85,12 +85,12 @@ describe 'create', -> it 'should only return the newly created identifiers', (next) -> Users.create [ - username: 'my_username_1', - email: 'my_first@email.com', + username: 'my_username_1' + email: 'my_first@email.com' password: 'my_password' , - username: 'my_username_2', - email: 'my_second@email.com', + username: 'my_username_2' + email: 'my_second@email.com' password: 'my_password' ], identifiers: true, (err, ids) -> should.not.exist err @@ -100,12 +100,12 @@ describe 'create', -> it 'should only return selected properties', (next) -> Users.create [ - username: 'my_username_1', - email: 'my_first@email.com', + username: 'my_username_1' + email: 'my_first@email.com' password: 'my_password' , - username: 'my_username_2', - email: 'my_second@email.com', + username: 'my_username_2' + email: 'my_second@email.com' password: 'my_password' ], properties: ['email'], (err, users) -> should.not.exist err @@ -115,14 +115,23 @@ describe 'create', -> it 'should only insert defined properties', (next) -> Users.create - username: 'my_username_1', - email: 'my_first@email.com', - password: 'my_password' + username: 'my_username_1' + email: 'my_first@email.com' zombie: 'e.t. maison' , (err, user) -> Users.get user.user_id, (err, user) -> should.not.exist user.zombie Users.clear next + it 'should let you pass your own identifiers', (next) -> + Users.create + user_id: 1 + username: 'my_username_1' + , (err, user) -> + Users.get 1, (err, user) -> + user.username.should.equal 'my_username_1' + Users.clear next + + diff --git a/test/id.coffee b/test/identify.coffee similarity index 80% rename from test/id.coffee rename to test/identify.coffee index 3e01ad7..3dc678f 100644 --- a/test/id.coffee +++ b/test/identify.coffee @@ -25,19 +25,19 @@ describe 'id', -> client.quit next it 'Test id # number', (next) -> - Users.id 3, (err, userId) -> + Users.identify 3, (err, userId) -> should.not.exist err userId.should.eql 3 - Users.id [3], (err, userId) -> + Users.identify [3], (err, userId) -> should.not.exist err userId.should.eql [3] next() it 'Test id # user.user_id', (next) -> - Users.id {user_id: 3}, (err, userId) -> + Users.identify {user_id: 3}, (err, userId) -> should.not.exist err userId.should.eql 3 - Users.id [{user_id: 3, username: 'my_username'}], (err, userId) -> + Users.identify [{user_id: 3, username: 'my_username'}], (err, userId) -> should.not.exist err userId.should.eql [3] next() @@ -49,11 +49,11 @@ describe 'id', -> password: 'my_password' , (err, user) -> # Pass an object - Users.id {username: 'my_username'}, (err, userId) -> + Users.identify {username: 'my_username'}, (err, userId) -> should.not.exist err userId.should.eql user.user_id # Pass an array of ids and objects - Users.id [1, {username: 'my_username'}, 2], (err, userId) -> + Users.identify [1, {username: 'my_username'}, 2], (err, userId) -> should.not.exist err userId.should.eql [1, user.user_id, 2] Users.clear next @@ -61,9 +61,9 @@ describe 'id', -> it 'Test id # invalid object empty', (next) -> # Test an array of 3 arguments, # but the second is invalid since it's an empty object - Users.id [1, {}, {user_id: 2}], (err, user) -> + Users.identify [1, {}, {user_id: 2}], (err, user) -> err.message.should.eql 'Invalid record, got {}' - Users.id {}, (err, user) -> + Users.identify {}, (err, user) -> err.message.should.eql 'Invalid record, got {}' Users.clear next @@ -75,7 +75,7 @@ describe 'id', -> { username: 'my_username_2', email: 'my2@mail.com' } ], (err, users) -> # Test return id - Users.id [ + Users.identify [ { username: users[1].username } # By unique { user_id: users[0].user_id } # By identifier { username: 'who are you' } # Alien @@ -92,7 +92,7 @@ describe 'id', -> { username: 'my_username_1', email: 'my1@mail.com' } { username: 'my_username_2', email: 'my2@mail.com' } ], (err, users) -> - Users.id [ + Users.identify [ { username: users[1].username } # By unique { user_id: users[0].user_id } # By identifier { username: 'who are you' } # Alien @@ -106,46 +106,46 @@ describe 'id', -> it 'Test id # invalid type id', (next) -> # Test an array of 3 arguments, # but the second is invalid since it's a boolean - Users.id [1, true, {user_id: 2}], (err, user) -> + Users.identify [1, true, {user_id: 2}], (err, user) -> err.message.should.eql 'Invalid id, got true' - Users.id false, (err, user) -> + Users.identify false, (err, user) -> err.message.should.eql 'Invalid id, got false' Users.clear next it 'Test id # invalid type null', (next) -> # Test an array of 3 arguments, # but the second is invalid since it's a boolean - Users.id [1, null, {user_id: 2}], (err, users) -> + Users.identify [1, null, {user_id: 2}], (err, users) -> err.message.should.eql 'Null record' - Users.id null, (err, user) -> + Users.identify null, (err, user) -> err.message.should.eql 'Null record' Users.clear next it 'Test id # accept null', (next) -> # Test an array of 3 arguments, # but the second is invalid since it's a boolean - Users.id [1, null, {user_id: 2}], {accept_null: true}, (err, users) -> + Users.identify [1, null, {user_id: 2}], {accept_null: true}, (err, users) -> should.not.exist err users.length.should.eql 3 should.exist users[0] should.not.exist users[1] should.exist users[2] # Test null - Users.id null, {accept_null: true}, (err, user) -> + Users.identify null, {accept_null: true}, (err, user) -> should.not.exist err should.not.exist user Users.clear next it 'Test id # accept null return object', (next) -> # Same test than 'Test id # accept null' with the 'object' option - Users.id [1, null, {user_id: 2}], {accept_null: true, object: true}, (err, users) -> + Users.identify [1, null, {user_id: 2}], {accept_null: true, object: true}, (err, users) -> should.not.exist err users.length.should.eql 3 users[0].user_id.should.eql 1 should.not.exist users[1] users[2].user_id.should.eql 2 # Test null - Users.id null, {accept_null: true, object: true}, (err, user) -> + Users.identify null, {accept_null: true, object: true}, (err, user) -> should.not.exist err should.not.exist user Users.clear next @@ -157,11 +157,11 @@ describe 'id', -> password: 'my_password' }, (err, orgUser) -> # Pass an id - Users.id orgUser.user_id, {object: true}, (err, user) -> + Users.identify orgUser.user_id, {object: true}, (err, user) -> should.not.exist err user.should.eql {user_id: orgUser.user_id} # Pass an array of ids - Users.id [orgUser.user_id, orgUser.user_id], {object: true}, (err, user) -> + Users.identify [orgUser.user_id, orgUser.user_id], {object: true}, (err, user) -> user.should.eql [{user_id: orgUser.user_id}, {user_id: orgUser.user_id}] Users.clear next @@ -172,11 +172,11 @@ describe 'id', -> password: 'my_password' }, (err, orgUser) -> # Pass an object - Users.id {username: 'my_username'}, {object: true}, (err, user) -> + Users.identify {username: 'my_username'}, {object: true}, (err, user) -> should.not.exist err user.should.eql {username: 'my_username', user_id: orgUser.user_id} # Pass an array of ids and objects - Users.id [1, {username: 'my_username'}, 2], {object: true}, (err, user) -> + Users.identify [1, {username: 'my_username'}, 2], {object: true}, (err, user) -> should.not.exist err user.should.eql [{user_id: 1}, {username: 'my_username', user_id: orgUser.user_id}, {user_id: 2}] Users.clear next