You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

842 lines
19 KiB
JavaScript

11 years ago
11 years ago
'use strict';
11 years ago
(function(module) {
11 years ago
11 years ago
var winston = require('winston'),
async = require('async'),
11 years ago
nconf = require('nconf'),
11 years ago
express = require('express'),
11 years ago
db,
mongoClient,
mongoStore;
try {
mongoClient = require('mongodb').MongoClient;
mongoStore = require('connect-mongo')(express);
} catch (err) {
11 years ago
winston.error('Unable to initialize MongoDB! Is MongoDB installed? Error :' + err.message);
11 years ago
process.exit();
}
11 years ago
module.init = function(callback) {
11 years ago
mongoClient.connect('mongodb://'+ nconf.get('mongo:host') + ':' + nconf.get('mongo:port') + '/' + nconf.get('mongo:database'), function(err, _db) {
if(err) {
winston.error("NodeBB could not connect to your Mongo database. Mongo returned the following error: " + err.message);
process.exit();
}
11 years ago
db = _db;
module.client = db;
module.sessionStore = new mongoStore({
db: db
});
11 years ago
if(nconf.get('mongo:password') && nconf.get('mongo:username')) {
db.authenticate(nconf.get('mongo:username'), nconf.get('mongo:password'), function (err) {
if(err) {
winston.error(err.message);
process.exit();
}
createIndices();
11 years ago
});
} else {
winston.warn('You have no mongo password setup!');
createIndices();
}
11 years ago
function createIndices() {
db.collection('objects').ensureIndex({_key :1}, {background:true}, function(err) {
if(err) {
winston.error('Error creating index ' + err.message);
}
});
db.collection('objects').ensureIndex({'expireAt':1}, {expireAfterSeconds:0, background:true}, function(err) {
if(err) {
winston.error('Error creating index ' + err.message);
}
});
11 years ago
db.collection('search').ensureIndex({content:'text'}, {background:true}, function(err) {
if(err) {
winston.error('Error creating index ' + err.message);
}
});
if(typeof callback === 'function') {
11 years ago
callback();
}
}
});
11 years ago
};
11 years ago
module.close = function() {
db.close();
11 years ago
};
//
// helper functions
//
function findItem(data, key) {
if(!data) {
return null;
}
for(var i=0; i<data.length; ++i) {
if(data[i]._key === key) {
var item = data.splice(i, 1);
if(item && item.length) {
return item[0];
} else {
return null;
}
}
}
return null;
}
function fieldToString(field) {
if(field === null || field === undefined) {
return field;
}
if(typeof field !== 'string') {
field = field.toString();
}
// if there is a '.' in the field name it inserts subdocument in mongo, replace '.'s with \uff0E
field = field.replace(/\./g, '\uff0E');
return field;
}
function toString(value) {
if(value === null || value === undefined) {
return value;
}
return value.toString();
}
11 years ago
//
// Exported functions
//
module.searchIndex = function(key, content, id) {
var data = {
id:id,
key:key,
content:content
};
db.collection('search').update({id:id, key:key}, {$set:data}, {upsert:true, w: 1}, function(err, result) {
if(err) {
winston.error('Error indexing ' + err.message);
}
});
11 years ago
};
module.search = function(key, term, limit, callback) {
db.command({text:"search" , search: term, filter: {key:key}, limit: limit }, function(err, result) {
if(err) {
return callback(err);
}
if(!result) {
return callback(null, []);
}
if(result.results && result.results.length) {
var data = result.results.map(function(item) {
return item.obj.id;
});
callback(null, data);
} else {
callback(null, []);
}
});
11 years ago
};
module.searchRemove = function(key, id, callback) {
db.collection('search').remove({id:id, key:key}, function(err, result) {
if(err) {
winston.error('Error removing search ' + err.message);
}
if (typeof callback === 'function') {
callback();
}
});
11 years ago
};
module.flushdb = function(callback) {
db.dropDatabase(function(err, result) {
11 years ago
if (typeof callback === 'function') {
11 years ago
callback(err);
}
});
11 years ago
};
11 years ago
module.info = function(callback) {
db.stats({scale:1024}, function(err, stats) {
11 years ago
if(err) {
return callback(err);
}
stats.avgObjSize = (stats.avgObjSize / 1024).toFixed(2);
11 years ago
stats.raw = JSON.stringify(stats, null, 4);
stats.mongo = true;
11 years ago
callback(null, stats);
});
11 years ago
};
11 years ago
// key
module.exists = function(key, callback) {
db.collection('objects').findOne({_key:key}, function(err, item) {
11 years ago
callback(err, item !== undefined && item !== null);
});
11 years ago
};
11 years ago
module.delete = function(key, callback) {
11 years ago
db.collection('objects').remove({_key:key}, function(err, result) {
if(typeof callback === 'function') {
11 years ago
callback(err, result);
11 years ago
}
});
11 years ago
};
11 years ago
module.get = function(key, callback) {
11 years ago
module.getObjectField(key, 'value', callback);
11 years ago
};
11 years ago
module.set = function(key, value, callback) {
11 years ago
var data = {value:value};
module.setObject(key, data, callback);
11 years ago
};
11 years ago
module.keys = function(key, callback) {
db.collection('objects').find( { _key: { $regex: key /*, $options: 'i'*/ } }, function(err, result) {
callback(err, result);
});
11 years ago
};
11 years ago
module.rename = function(oldKey, newKey, callback) {
db.collection('objects').update({_key: oldKey}, {$set:{_key: newKey}}, function(err, result) {
if(typeof callback === 'function') {
11 years ago
callback(err, result);
}
});
11 years ago
};
11 years ago
module.expire = function(key, seconds, callback) {
module.expireAt(key, Math.round(Date.now() / 1000) + seconds, callback);
11 years ago
};
module.expireAt = function(key, timestamp, callback) {
module.setObjectField(key, 'expireAt', new Date(timestamp * 1000), callback);
11 years ago
};
11 years ago
//hashes
11 years ago
module.setObject = function(key, data, callback) {
11 years ago
data._key = key;
11 years ago
db.collection('objects').update({_key:key}, {$set:data}, {upsert:true, w: 1}, function(err, result) {
if(typeof callback === 'function') {
callback(err, result);
}
11 years ago
});
11 years ago
};
11 years ago
11 years ago
module.setObjectField = function(key, field, value, callback) {
11 years ago
var data = {};
field = fieldToString(field);
11 years ago
data[field] = value;
db.collection('objects').update({_key:key}, {$set:data}, {upsert:true, w: 1}, function(err, result) {
if(typeof callback === 'function') {
callback(err, result);
}
11 years ago
});
11 years ago
};
11 years ago
module.getObject = function(key, callback) {
db.collection('objects').findOne({_key:key}, {_id:0, _key:0}, callback);
11 years ago
};
11 years ago
module.getObjects = function(keys, callback) {
db.collection('objects').find({_key:{$in:keys}}, {_id:0}).toArray(function(err, data) {
if(err) {
return callback(err);
}
var returnData = [];
for(var i=0; i<keys.length; ++i) {
returnData.push(findItem(data, keys[i]));
}
11 years ago
callback(null, returnData);
});
11 years ago
};
11 years ago
module.getObjectField = function(key, field, callback) {
field = fieldToString(field);
module.getObjectFields(key, [field], function(err, data) {
if(err) {
return callback(err);
}
callback(null, data ? data[field] : null);
11 years ago
});
11 years ago
};
11 years ago
module.getObjectFields = function(key, fields, callback) {
module.getObjectsFields([key], fields, function(err, items) {
callback(err, items ? items[0] : null);
});
};
module.getObjectsFields = function(keys, fields, callback) {
var _fields = {
_id: 0,
_key: 1
};
for(var i=0; i<fields.length; ++i) {
fields[i] = fieldToString(fields[i]);
_fields[fields[i]] = 1;
}
keys = keys.map(function(key) {
return { _key : key};
});
db.collection('objects').find({$or: keys}, _fields).toArray(function(err, items) {
if (err) {
return callback(err);
}
if (items === null) {
items = [];
}
var returnData = [],
index = 0,
item;
for (var i=0; i<keys.length; ++i) {
if (items[index] && items[index]._key === keys[i]._key) {
item = items[index];
index++;
} else {
item = {};
}
returnData.push(item);
for (var k=0; k<fields.length; ++k) {
if (item[fields[k]] === null || item[fields[k]] === undefined) {
item[fields[k]] = null;
}
}
}
callback(null, returnData);
});
11 years ago
};
11 years ago
module.getObjectKeys = function(key, callback) {
module.getObject(key, function(err, data) {
if(err) {
return callback(err);
}
if(data) {
callback(null, Object.keys(data));
} else {
callback(null, []);
}
});
11 years ago
};
11 years ago
module.getObjectValues = function(key, callback) {
module.getObject(key, function(err, data) {
if(err) {
return callback(err);
}
var values = [];
for(var key in data) {
if (data && data.hasOwnProperty(key)) {
values.push(data[key]);
}
}
callback(null, values);
});
11 years ago
};
11 years ago
module.isObjectField = function(key, field, callback) {
11 years ago
var data = {};
field = fieldToString(field);
11 years ago
data[field] = '';
db.collection('objects').findOne({_key:key}, {fields:data}, function(err, item) {
if(err) {
return callback(err);
}
callback(err, !!item && item[field] !== undefined && item[field] !== null);
});
11 years ago
};
11 years ago
module.deleteObjectField = function(key, field, callback) {
var data = {};
field = fieldToString(field);
data[field] = '';
db.collection('objects').update({_key:key}, {$unset : data}, function(err, result) {
if(typeof callback === 'function') {
callback(err, result);
}
});
11 years ago
};
11 years ago
module.incrObjectField = function(key, field, callback) {
module.incrObjectFieldBy(key, field, 1, callback);
11 years ago
};
11 years ago
module.decrObjectField = function(key, field, callback) {
module.incrObjectFieldBy(key, field, -1, callback);
11 years ago
};
11 years ago
11 years ago
module.incrObjectFieldBy = function(key, field, value, callback) {
var data = {};
field = fieldToString(field);
data[field] = value;
db.collection('objects').findAndModify({_key:key}, {}, {$inc: data}, {new:true, upsert:true}, function(err, result) {
if(typeof callback === 'function') {
callback(err, result ? result[field] : null);
}
});
11 years ago
};
11 years ago
11 years ago
// sets
11 years ago
11 years ago
module.setAdd = function(key, value, callback) {
if(!Array.isArray(value)) {
value = [value];
}
value.forEach(function(element, index, array) {
array[index] = toString(element);
});
11 years ago
db.collection('objects').update({_key: key}, { $addToSet: { members: { $each: value } } }, { upsert: true, w: 1 }, function(err, result) {
if(typeof callback === 'function') {
callback(err, result);
}
});
11 years ago
};
11 years ago
module.setRemove = function(key, value, callback) {
if(!Array.isArray(value)) {
value = [value];
}
value.forEach(function(element, index, array) {
array[index] = toString(element);
});
db.collection('objects').update( { _key: key }, { $pullAll: { members: value } }, function(err, result) {
if(typeof callback === 'function') {
callback(err, result);
}
11 years ago
});
11 years ago
};
11 years ago
module.isSetMember = function(key, value, callback) {
value = toString(value);
db.collection('objects').findOne({_key:key, members: value}, function(err, item) {
11 years ago
callback(err, item !== null && item !== undefined);
});
11 years ago
};
11 years ago
module.isMemberOfSets = function(sets, value, callback) {
value = toString(value);
db.collection('objects').find({_key: {$in : sets}, members: value}).toArray(function(err, result) {
if(err) {
return callback(err);
}
result = result.map(function(item) {
return item._key;
});
result = sets.map(function(set) {
return result.indexOf(set) !== -1 ? 1 : 0;
});
callback(err, result);
});
11 years ago
};
11 years ago
module.getSetMembers = function(key, callback) {
db.collection('objects').findOne({_key:key}, {members:1}, function(err, data) {
11 years ago
if(err) {
return callback(err);
}
if(!data) {
callback(null, []);
} else {
callback(null, data.members);
11 years ago
}
11 years ago
});
11 years ago
};
11 years ago
module.setCount = function(key, callback) {
db.collection('objects').findOne({_key:key}, function(err, data) {
11 years ago
if(err) {
return callback(err);
}
if(!data) {
11 years ago
return callback(null, 0);
}
callback(null, data.members.length);
11 years ago
});
11 years ago
};
11 years ago
module.setRemoveRandom = function(key, callback) {
db.collection('objects').findOne({_key:key}, function(err, data) {
11 years ago
if(err) {
if(typeof callback === 'function') {
return callback(err);
} else {
return winston.error(err.message);
}
11 years ago
}
if(!data) {
if(typeof callback === 'function') {
callback(null, 0);
}
11 years ago
} else {
var randomIndex = Math.floor(Math.random() * data.members.length);
var value = data.members[randomIndex];
module.setRemove(data._key, value, function(err, result) {
if(typeof callback === 'function') {
callback(err, value);
}
11 years ago
});
}
});
11 years ago
};
11 years ago
11 years ago
// sorted sets
module.sortedSetAdd = function(key, score, value, callback) {
value = toString(value);
var data = {
score: parseInt(score, 10),
value: value
};
db.collection('objects').update({_key:key, value:value}, {$set:data}, {upsert:true, w: 1}, function(err, result) {
if(typeof callback === 'function') {
callback(err, result);
}
});
11 years ago
};
11 years ago
module.sortedSetRemove = function(key, value, callback) {
value = toString(value);
db.collection('objects').remove({_key:key, value:value}, function(err, result) {
if(typeof callback === 'function') {
callback(err, result);
}
});
11 years ago
};
11 years ago
function getSortedSetRange(key, start, stop, sort, callback) {
db.collection('objects').find({_key:key}, {fields:{value:1}})
.limit(stop - start + 1)
.skip(start)
.sort({score: sort})
.toArray(function(err, data) {
11 years ago
if (err) {
return callback(err);
}
11 years ago
if (!data) {
return callback(null, null);
}
data = data.map(function(item) {
return item.value;
});
11 years ago
callback(null, data);
});
}
11 years ago
module.getSortedSetRange = function(key, start, stop, callback) {
getSortedSetRange(key, start, stop, 1, callback);
11 years ago
};
11 years ago
module.getSortedSetRevRange = function(key, start, stop, callback) {
getSortedSetRange(key, start, stop, -1, callback);
11 years ago
};
11 years ago
module.getSortedSetRevRangeByScore = function(args, callback) {
//var args = ['topics:recent', '+inf', timestamp - since, 'LIMIT', start, end - start + 1];
var key = args[0],
max = (args[1] === '+inf')?Number.MAX_VALUE:args[1],
min = args[2],
start = args[4],
11 years ago
count = args[5];
11 years ago
if(parseInt(count, 10) === -1) {
count = 0;
}
db.collection('objects').find({_key:key, score: {$gte:min, $lte:max}}, {fields:{value:1}})
11 years ago
.limit(count)
.skip(start)
.sort({score: -1})
.toArray(function(err, data) {
if(err) {
return callback(err);
}
// maybe this can be done with mongo?
data = data.map(function(item) {
return item.value;
});
callback(err, data);
});
11 years ago
};
11 years ago
module.sortedSetCount = function(key, min, max, callback) {
db.collection('objects').count({_key:key, score: {$gte:min, $lte:max}}, function(err, count) {
11 years ago
if(err) {
return callback(err);
}
if(!count) {
return callback(null, 0);
}
callback(null,count);
});
11 years ago
};
11 years ago
module.sortedSetCard = function(key, callback) {
db.collection('objects').count({_key:key}, function(err, count) {
if(err) {
return callback(err);
}
if(!count) {
return callback(null, 0);
}
callback(null, count);
});
11 years ago
};
module.sortedSetRank = function(key, value, callback) {
value = toString(value);
module.getSortedSetRange(key, 0, -1, function(err, result) {
if(err) {
return callback(err);
}
var rank = result.indexOf(value);
if(rank === -1) {
return callback(null, null);
}
callback(null, rank);
});
11 years ago
};
11 years ago
module.sortedSetRevRank = function(key, value, callback) {
value = toString(value);
module.getSortedSetRevRange(key, 0, -1, function(err, result) {
11 years ago
if(err) {
return callback(err);
}
11 years ago
var rank = result.indexOf(value);
11 years ago
if(rank === -1) {
return callback(null, null);
}
callback(null, rank);
11 years ago
});
11 years ago
};
11 years ago
module.sortedSetScore = function(key, value, callback) {
value = toString(value);
db.collection('objects').findOne({_key:key, value: value}, {fields:{score:1}}, function(err, result) {
callback(err, result ? result.score : null);
});
11 years ago
};
module.isSortedSetMember = function(key, value, callback) {
module.sortedSetScore(key, value, function(err, score) {
callback(err, !!score);
});
11 years ago
};
module.sortedSetsScore = function(keys, value, callback) {
value = toString(value);
db.collection('objects').find({_key:{$in:keys}, value: value}).toArray(function(err, result) {
if(err) {
return callback(err);
}
var returnData = [],
item;
for(var i=0; i<keys.length; ++i) {
item = findItem(result, keys[i]);
returnData.push(item ? item.score : null);
}
callback(null, returnData);
});
11 years ago
};
11 years ago
// lists
module.listPrepend = function(key, value, callback) {
value = toString(value);
11 years ago
module.isObjectField(key, 'array', function(err, exists) {
if(err) {
if(typeof callback === 'function') {
return callback(err);
} else {
return winston.error(err.message);
}
}
if(exists) {
11 years ago
db.collection('objects').update({_key:key}, {'$set': {'array.-1': value}}, {upsert:true, w:1 }, function(err, result) {
if(typeof callback === 'function') {
callback(err, result);
}
11 years ago
});
} else {
module.listAppend(key, value, callback);
}
});
};
11 years ago
module.listAppend = function(key, value, callback) {
value = toString(value);
db.collection('objects').update({ _key: key }, { $push: { array: value } }, {upsert:true, w:1}, function(err, result) {
if(typeof callback === 'function') {
callback(err, result);
}
});
11 years ago
};
11 years ago
module.listRemoveLast = function(key, callback) {
module.getListRange(key, -1, 0, function(err, value) {
if(err) {
if(typeof callback === 'function') {
return callback(err);
}
return;
}
db.collection('objects').update({_key: key }, { $pop: { array: 1 } }, function(err, result) {
if(typeof callback === 'function') {
if(err) {
return callback(err);
}
if(value && value.length) {
callback(err, value[0]);
} else {
callback(err, null);
}
}
});
});
11 years ago
};
module.listRemoveAll = function(key, value, callback) {
value = toString(value);
db.collection('objects').update({_key: key }, { $pull: { array: value } }, function(err, result) {
if(typeof callback === 'function') {
callback(err, result);
}
});
11 years ago
};
11 years ago
module.getListRange = function(key, start, stop, callback) {
var skip = start,
limit = stop - start + 1,
splice = false;
if((start < 0 && stop >= 0) || (start >= 0 && stop < 0)) {
skip = 0;
limit = Math.pow(2, 31) - 2;
splice = true;
} else if (start > stop) {
return callback(null, []);
}
db.collection('objects').findOne({_key:key}, { array: { $slice: [skip, limit] }}, function(err, data) {
if(err) {
return callback(err);
}
if(data && data.array) {
if(splice) {
if(start < 0) {
start = data.array.length - Math.abs(start);
}
if(stop < 0) {
stop = data.array.length - Math.abs(stop);
}
if(start > stop) {
return callback(null, []);
}
var howMany = stop - start + 1;
if(start !== 0 || howMany !== data.array.length) {
data.array = data.array.splice(start, howMany);
}
}
callback(null, data.array);
} else {
callback(null, []);
}
});
11 years ago
};
11 years ago
}(exports));