mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-01 03:26:04 +01:00
convert uid mappings to sorted sets
email:uid, username:uid, userslug:uid, fullname:uid all converted to sorted sets prevents hitting mongodb document size limit
This commit is contained in:
@@ -21,7 +21,7 @@ var db = require('./database'),
|
|||||||
schemaDate, thisSchemaDate,
|
schemaDate, thisSchemaDate,
|
||||||
|
|
||||||
// IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema
|
// IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema
|
||||||
latestSchema = Date.UTC(2015, 1, 25, 6);
|
latestSchema = Date.UTC(2015, 4, 7);
|
||||||
|
|
||||||
Upgrade.check = function(callback) {
|
Upgrade.check = function(callback) {
|
||||||
db.get('schemaDate', function(err, value) {
|
db.get('schemaDate', function(err, value) {
|
||||||
@@ -938,7 +938,7 @@ Upgrade.upgrade = function(callback) {
|
|||||||
}
|
}
|
||||||
winston.info('[2015/02/24] Upgrading privilege groups to system groups done');
|
winston.info('[2015/02/24] Upgrading privilege groups to system groups done');
|
||||||
Upgrade.update(thisSchemaDate, next);
|
Upgrade.update(thisSchemaDate, next);
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
winston.info('[2015/02/24] Upgrading privilege groups to system groups skipped');
|
winston.info('[2015/02/24] Upgrading privilege groups to system groups skipped');
|
||||||
@@ -963,6 +963,48 @@ Upgrade.upgrade = function(callback) {
|
|||||||
winston.info('[2015/02/25] Upgrading menu items to dynamic navigation system skipped');
|
winston.info('[2015/02/25] Upgrading menu items to dynamic navigation system skipped');
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
function upgradeHashToSortedSet(hash, callback) {
|
||||||
|
db.getObject(hash, function(err, oldHash) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
db.rename(hash, hash + '_old', function(err) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
var keys = Object.keys(oldHash);
|
||||||
|
async.each(keys, function(key, next) {
|
||||||
|
db.sortedSetAdd(hash, oldHash[key], key, next);
|
||||||
|
}, callback);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
thisSchemaDate = Date.UTC(2015, 4, 7);
|
||||||
|
if (schemaDate < thisSchemaDate) {
|
||||||
|
updatesMade = true;
|
||||||
|
winston.info('[2015/02/25] Upgrading uid mappings to sorted set');
|
||||||
|
|
||||||
|
async.series([
|
||||||
|
async.apply(upgradeHashToSortedSet, 'email:uid'),
|
||||||
|
async.apply(upgradeHashToSortedSet, 'fullname:uid'),
|
||||||
|
async.apply(upgradeHashToSortedSet, 'username:uid'),
|
||||||
|
async.apply(upgradeHashToSortedSet, 'userslug:uid'),
|
||||||
|
], function(err) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
winston.info('[2015/05/07] Upgrading uid mappings to sorted set done');
|
||||||
|
Upgrade.update(thisSchemaDate, next);
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
winston.info('[2015/05/07] Upgrading uid mappings to sorted set skipped');
|
||||||
|
next();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add new schema updates here
|
// Add new schema updates here
|
||||||
|
|||||||
18
src/user.js
18
src/user.js
@@ -334,26 +334,18 @@ var async = require('async'),
|
|||||||
if (!username) {
|
if (!username) {
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
db.getObjectField('username:uid', username, callback);
|
db.sortedSetScore('username:uid', username, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getUidsByUsernames = function(usernames, callback) {
|
User.getUidsByUsernames = function(usernames, callback) {
|
||||||
db.getObjectFields('username:uid', usernames, function(err, users) {
|
db.sortedSetScores('username:uid', usernames, callback);
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
var uids = usernames.map(function(username) {
|
|
||||||
return users[username];
|
|
||||||
});
|
|
||||||
callback(null, uids);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getUidByUserslug = function(userslug, callback) {
|
User.getUidByUserslug = function(userslug, callback) {
|
||||||
if (!userslug) {
|
if (!userslug) {
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
db.getObjectField('userslug:uid', userslug, callback);
|
db.sortedSetScore('userslug:uid', userslug, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getUsernamesByUids = function(uids, callback) {
|
User.getUsernamesByUids = function(uids, callback) {
|
||||||
@@ -382,11 +374,11 @@ var async = require('async'),
|
|||||||
};
|
};
|
||||||
|
|
||||||
User.getUidByEmail = function(email, callback) {
|
User.getUidByEmail = function(email, callback) {
|
||||||
db.getObjectField('email:uid', email.toLowerCase(), callback);
|
db.sortedSetScore('email:uid', email.toLowerCase(), callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getUsernameByEmail = function(email, callback) {
|
User.getUsernameByEmail = function(email, callback) {
|
||||||
db.getObjectField('email:uid', email.toLowerCase(), function(err, uid) {
|
db.sortedSetScore('email:uid', email.toLowerCase(), function(err, uid) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ module.exports = function(User) {
|
|||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function(next) {
|
function(next) {
|
||||||
db.getObjectValues('username:uid', next);
|
db.getSortedSetRange('username:uid', 0, -1, next);
|
||||||
},
|
},
|
||||||
function(uids, next) {
|
function(uids, next) {
|
||||||
User.getMultipleUserFields(uids, ['uid', 'email', 'username'], next);
|
User.getMultipleUserFields(uids, ['uid', 'email', 'username'], next);
|
||||||
|
|||||||
@@ -91,10 +91,10 @@ module.exports = function(User) {
|
|||||||
function(next) {
|
function(next) {
|
||||||
async.parallel([
|
async.parallel([
|
||||||
function(next) {
|
function(next) {
|
||||||
db.setObjectField('username:uid', userData.username, userData.uid, next);
|
db.sortedSetAdd('username:uid', userData.uid, userData.username, next);
|
||||||
},
|
},
|
||||||
function(next) {
|
function(next) {
|
||||||
db.setObjectField('userslug:uid', userData.userslug, userData.uid, next);
|
db.sortedSetAdd('userslug:uid', userData.uid, userData.userslug, next);
|
||||||
},
|
},
|
||||||
function(next) {
|
function(next) {
|
||||||
db.sortedSetAdd('users:joindate', timestamp, userData.uid, next);
|
db.sortedSetAdd('users:joindate', timestamp, userData.uid, next);
|
||||||
@@ -107,7 +107,7 @@ module.exports = function(User) {
|
|||||||
},
|
},
|
||||||
function(next) {
|
function(next) {
|
||||||
if (userData.email) {
|
if (userData.email) {
|
||||||
db.setObjectField('email:uid', userData.email.toLowerCase(), userData.uid, next);
|
db.sortedSetAdd('email:uid', userData.uid, userData.email.toLowerCase(), next);
|
||||||
if (parseInt(userData.uid, 10) !== 1 && parseInt(meta.config.requireEmailConfirmation, 10) === 1) {
|
if (parseInt(userData.uid, 10) !== 1 && parseInt(meta.config.requireEmailConfirmation, 10) === 1) {
|
||||||
User.email.sendValidationEmail(userData.uid, userData.email);
|
User.email.sendValidationEmail(userData.uid, userData.email);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,17 +49,17 @@ module.exports = function(User) {
|
|||||||
|
|
||||||
async.parallel([
|
async.parallel([
|
||||||
function(next) {
|
function(next) {
|
||||||
db.deleteObjectField('username:uid', userData.username, next);
|
db.sortedSetRemove('username:uid', userData.username, next);
|
||||||
},
|
},
|
||||||
function(next) {
|
function(next) {
|
||||||
db.deleteObjectField('userslug:uid', userData.userslug, next);
|
db.sortedSetRemove('userslug:uid', userData.userslug, next);
|
||||||
},
|
},
|
||||||
function(next) {
|
function(next) {
|
||||||
db.deleteObjectField('fullname:uid', userData.fullname, next);
|
db.sortedSetRemove('fullname:uid', userData.fullname, next);
|
||||||
},
|
},
|
||||||
function(next) {
|
function(next) {
|
||||||
if (userData.email) {
|
if (userData.email) {
|
||||||
db.deleteObjectField('email:uid', userData.email.toLowerCase(), next);
|
db.sortedSetRemove('email:uid', userData.email.toLowerCase(), next);
|
||||||
} else {
|
} else {
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ var async = require('async'),
|
|||||||
};
|
};
|
||||||
|
|
||||||
UserEmail.available = function(email, callback) {
|
UserEmail.available = function(email, callback) {
|
||||||
db.isObjectField('email:uid', email.toLowerCase(), function(err, exists) {
|
db.isSortedSetMember('email:uid', email.toLowerCase(), function(err, exists) {
|
||||||
callback(err, !exists);
|
callback(err, !exists);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ module.exports = function(User) {
|
|||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
db.deleteObjectField('email:uid', userData.email.toLowerCase(), function(err) {
|
db.sortedSetRemove('email:uid', userData.email.toLowerCase(), function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
@@ -176,7 +176,7 @@ module.exports = function(User) {
|
|||||||
User.setUserField(uid, 'gravatarpicture', gravatarpicture, next);
|
User.setUserField(uid, 'gravatarpicture', gravatarpicture, next);
|
||||||
},
|
},
|
||||||
function(next) {
|
function(next) {
|
||||||
db.setObjectField('email:uid', newEmail.toLowerCase(), uid, next);
|
db.sortedSetAdd('email:uid', uid, newEmail.toLowerCase(), next);
|
||||||
},
|
},
|
||||||
function(next) {
|
function(next) {
|
||||||
User.setUserField(uid, 'email', newEmail, next);
|
User.setUserField(uid, 'email', newEmail, next);
|
||||||
@@ -205,64 +205,51 @@ module.exports = function(User) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
User.getUserFields(uid, ['username', 'userslug'], function(err, userData) {
|
User.getUserFields(uid, ['username', 'userslug'], function(err, userData) {
|
||||||
function update(field, object, value, callback) {
|
|
||||||
async.series([
|
|
||||||
function(next) {
|
|
||||||
db.deleteObjectField(field + ':uid', userData[field], next);
|
|
||||||
},
|
|
||||||
function(next) {
|
|
||||||
User.setUserField(uid, field, value, next);
|
|
||||||
},
|
|
||||||
function(next) {
|
|
||||||
db.setObjectField(object, value, uid, next);
|
|
||||||
}
|
|
||||||
], callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
async.parallel([
|
async.parallel([
|
||||||
function(next) {
|
function(next) {
|
||||||
if (newUsername === userData.username) {
|
updateUidMapping('username', uid, newUsername, userData.username, next);
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
update('username', 'username:uid', newUsername, next);
|
|
||||||
},
|
},
|
||||||
function(next) {
|
function(next) {
|
||||||
var newUserslug = utils.slugify(newUsername);
|
var newUserslug = utils.slugify(newUsername);
|
||||||
if (newUserslug === userData.userslug) {
|
updateUidMapping('userslug', uid, newUserslug, userData.userslug, next);
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
update('userslug', 'userslug:uid', newUserslug, next);
|
|
||||||
}
|
}
|
||||||
], callback);
|
], callback);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateUidMapping(field, uid, value, oldValue, callback) {
|
||||||
|
if (value === oldValue) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
async.series([
|
||||||
|
function(next) {
|
||||||
|
db.sortedSetRemove(field + ':uid', oldValue, next);
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
User.setUserField(uid, field, value, next);
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
if (value) {
|
||||||
|
db.sortedSetAdd(field + ':uid', uid, value, next);
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
], callback);
|
||||||
|
}
|
||||||
|
|
||||||
function updateFullname(uid, newFullname, callback) {
|
function updateFullname(uid, newFullname, callback) {
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function(next) {
|
function(next) {
|
||||||
User.getUserField(uid, 'fullname', next);
|
User.getUserField(uid, 'fullname', next);
|
||||||
},
|
},
|
||||||
function(fullname, next) {
|
function(fullname, next) {
|
||||||
if (newFullname === fullname) {
|
updateUidMapping('fullname', uid, newFullname, fullname, next);
|
||||||
return callback();
|
|
||||||
}
|
|
||||||
db.deleteObjectField('fullname:uid', fullname, next);
|
|
||||||
},
|
|
||||||
function(next) {
|
|
||||||
User.setUserField(uid, 'fullname', newFullname, next);
|
|
||||||
},
|
|
||||||
function(next) {
|
|
||||||
if (newFullname) {
|
|
||||||
db.setObjectField('fullname:uid', newFullname, uid, next);
|
|
||||||
} else {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
], callback);
|
], callback);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,9 @@ module.exports = function(User) {
|
|||||||
return searchBy + ':uid';
|
return searchBy + ':uid';
|
||||||
});
|
});
|
||||||
|
|
||||||
db.getObjects(keys, function(err, hashes) {
|
async.map(keys, function(key, next) {
|
||||||
|
db.getSortedSetRangeWithScores(key, 0, -1, next);
|
||||||
|
}, function(err, hashes) {
|
||||||
if (err || !hashes) {
|
if (err || !hashes) {
|
||||||
return callback(err, []);
|
return callback(err, []);
|
||||||
}
|
}
|
||||||
@@ -97,16 +99,16 @@ module.exports = function(User) {
|
|||||||
var resultsPerPage = parseInt(meta.config.userSearchResultsPerPage, 10) || 20;
|
var resultsPerPage = parseInt(meta.config.userSearchResultsPerPage, 10) || 20;
|
||||||
var hardCap = resultsPerPage * 10;
|
var hardCap = resultsPerPage * 10;
|
||||||
|
|
||||||
for(var i=0; i<hashes.length; ++i) {
|
for (var i=0; i<hashes.length; ++i) {
|
||||||
for(var field in hashes[i]) {
|
for (var k=0; k<hashes[i].length; ++k) {
|
||||||
|
var field = hashes[i][k].value;
|
||||||
if ((startsWith && field.toLowerCase().startsWith(query)) || (!startsWith && field.toLowerCase().indexOf(query) !== -1)) {
|
if ((startsWith && field.toLowerCase().startsWith(query)) || (!startsWith && field.toLowerCase().indexOf(query) !== -1)) {
|
||||||
uids.push(hashes[i][field]);
|
uids.push(hashes[i][k].score);
|
||||||
if (uids.length >= hardCap) {
|
if (uids.length >= hardCap) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uids.length >= hardCap) {
|
if (uids.length >= hardCap) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ describe('User', function() {
|
|||||||
beforeEach(function(){
|
beforeEach(function(){
|
||||||
userData = {
|
userData = {
|
||||||
username: 'John Smith',
|
username: 'John Smith',
|
||||||
|
fullname: 'John Smith McNamara',
|
||||||
password: 'swordfish',
|
password: 'swordfish',
|
||||||
email: 'john@example.com',
|
email: 'john@example.com',
|
||||||
callback: undefined
|
callback: undefined
|
||||||
@@ -254,6 +255,33 @@ describe('User', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('hash methods', function() {
|
||||||
|
|
||||||
|
it('should return uid from email', function(next) {
|
||||||
|
User.getUidByEmail('john@example.com', function(err, uid) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(parseInt(uid, 10), parseInt(testUid, 10));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return uid from username', function(next) {
|
||||||
|
User.getUidByUsername('John Smith', function(err, uid) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(parseInt(uid, 10), parseInt(testUid, 10));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return uid from userslug', function(next) {
|
||||||
|
User.getUidByUserslug('john-smith', function(err, uid) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(parseInt(uid, 10), parseInt(testUid, 10));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
after(function() {
|
after(function() {
|
||||||
db.flushdb();
|
db.flushdb();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user