audiovanish-plugin-dbsearch
Version:
A Plugin that let's users search posts and tracks
513 lines (449 loc) • 12.4 kB
JavaScript
;
var winston = require.main.require('winston');
var async = require.main.require('async');
var db = require.main.require('./src/database');
var tracks = require.main.require('./src/tracks');
var posts = require.main.require('./src/posts');
var utils = require.main.require('./public/src/utils');
var socketAdmin = require.main.require('./src/socket.io/admin');
var batch = require.main.require('./src/batch');
var nconf = require.main.require('nconf');
require('./' + nconf.get('database'))(db);
(function(search) {
var defaultPostLimit = 50;
var defaultTrackLimit = 50;
var postLimit = defaultPostLimit;
var trackLimit = defaultTrackLimit;
var batchSize = 500;
var trackCount = 0;
var postCount = 0;
var tracksProcessed = 0;
var postsProcessed = 0;
search.init = function(params, callback) {
params.router.get('/admin/plugins/dbsearch', params.middleware.applyCSRF, params.middleware.admin.buildHeader, renderAdmin);
params.router.get('/api/admin/plugins/dbsearch', params.middleware.applyCSRF, renderAdmin);
params.router.post('/api/admin/plugins/dbsearch/save', params.middleware.applyCSRF, save);
callback();
db.getObject('audiovanish-plugin-dbsearch', function(err, data) {
if (err) {
return winston.error(err.error);
}
if (data) {
postLimit = data.postLimit ? data.postLimit : defaultPostLimit;
trackLimit = data.trackLimit ? data.trackLimit : defaultTrackLimit;
}
});
};
search.postSave = function(postData, callback) {
callback = callback || function() {};
tracks.getTrackField(postData.tid, 'deleted', function(err, isTrackDeleted) {
if (err) {
return callback(err);
}
if (parseInt(isTrackDeleted, 10) === 1) {
return callback();
}
postsSave([postData], callback);
});
};
search.postRestore = function(postData) {
search.postSave(postData);
};
search.postEdit = function(postData) {
search.postSave(postData);
};
search.postDelete = function(pid, callback) {
db.searchRemove('post', [pid], callback);
};
search.postMove = function(data) {
tracks.getTrackFields(data.post.tid, ['cid', 'deleted'], function(err, track) {
if (err) {
return;
}
reIndexPids([data.post.pid], track);
});
};
search.trackSave = function(trackData, callback) {
callback = callback || function() {};
tracksSave([trackData], callback);
};
search.trackRestore = function(trackData) {
reIndexTids([trackData.tid]);
};
search.trackEdit = function(trackData) {
search.trackSave(trackData);
};
search.trackDelete = function(trackData) {
var tid = trackData.tid;
async.parallel({
track: function(next) {
db.searchRemove('track', [tid], next);
},
mainPid: function(next) {
tracks.getTrackField(tid, 'mainPid', function(err, mainPid) {
if (err) {
return next(err);
}
db.searchRemove('post', [mainPid], next);
});
},
posts: function(next) {
batch.processSortedSet('tid:' + tid + ':posts', function(pids, next) {
db.searchRemove('post', pids, next);
}, {
batch: batchSize
}, next);
}
}, function(err, results) {
if (err) {
winston.error(err);
}
});
};
search.trackMove = function(data) {
reIndexTids([data.tid]);
};
search.searchQuery = function(data, callback) {
if (!data || !data.index) {
return callback(null, []);
}
var limit = data.index === 'post' ? postLimit : trackLimit;
var query = {};
if (data.cid) {
query.cid = data.cid.filter(Boolean);
}
if (data.uid) {
query.uid = data.uid.filter(Boolean);
}
if (data.content) {
query.content = data.content;
}
if (!Object.keys(query).length) {
return callback(null, []);
}
db.search(data.index, query, limit, callback);
};
search.reindex = function(callback) {
tracksProcessed = 0;
postsProcessed = 0;
db.getObjectFields('global', ['trackCount', 'postCount'], function(err, data) {
if (err) {
return callback(err);
}
trackCount = data.trackCount;
postCount = data.postCount;
async.parallel([
function (next) {
reIndexTracks(next);
},
function (next) {
reIndexPosts(next);
}
], function(err) {
callback(err);
});
});
};
function reIndexTracks(callback) {
batch.processSortedSet('tracks:tid', function(tids, next) {
async.waterfall([
function(next) {
tracks.getTracksFields(tids, ['tid', 'title', 'uid', 'cid', 'deleted'], next);
},
function(trackData, next) {
tracksSave(trackData, next);
},
], function(err) {
if (err) {
return next(err);
}
tracksProcessed += tids.length;
next();
});
}, {
batch: batchSize
}, function(err) {
callback(err);
});
}
function tracksSave(tracks, callback) {
tracks = tracks.filter(function(track) {
return track && track.tid && parseInt(track.deleted, 10) !== 1;
});
var data = tracks.map(function(trackData) {
var indexData = {};
if (trackData.title) {
indexData.content = trackData.title;
}
if (trackData.cid) {
indexData.cid = trackData.cid;
}
if (trackData.uid) {
indexData.uid = trackData.uid;
}
if (!Object.keys(indexData).length) {
return null;
}
return indexData;
});
data = data.filter(Boolean);
if (!data.length) {
return callback();
}
var tids = tracks.map(function(track) {
return track.tid;
});
db.searchIndex('track', data, tids, callback);
}
function reIndexPosts(callback) {
batch.processSortedSet('posts:pid', function(pids, next) {
var postData;
async.waterfall([
function(next) {
posts.getPostsFields(pids, ['pid', 'content', 'uid', 'tid', 'deleted'], next);
},
function(_postData, next) {
postData = _postData.filter(function(post) {
return post && parseInt(post.deleted, 10) !== 1;
});
var tids = postData.map(function(post) {
return post && post.tid;
});
tracks.getTracksFields(tids, ['deleted', 'cid'], next);
},
function(trackData, next) {
postData.forEach(function(post, index) {
if (post && trackData[index]) {
post.cid = trackData[index].cid;
}
});
postData = postData.filter(function(post, index) {
return post && parseInt(trackData[index].deleted, 10) !== 1;
});
postsSave(postData, next);
}
], function(err) {
if (err) {
return next(err);
}
postsProcessed += pids.length;
next();
});
}, {
batch: batchSize
}, function(err) {
callback(err);
});
}
function postsSave(posts, callback) {
posts = posts.filter(function(post) {
return post && post.pid && parseInt(post.deleted, 10) !== 1;
});
var data = posts.map(function(postData) {
var indexData = {};
if (postData.content) {
indexData.content = postData.content;
}
if (postData.cid) {
indexData.cid = postData.cid;
}
if (postData.uid) {
indexData.uid = postData.uid;
}
if (!Object.keys(indexData).length) {
return null;
}
return indexData;
});
data = data.filter(Boolean);
if (!data.length) {
return callback();
}
var pids = posts.map(function(post) {
return post.pid;
});
db.searchIndex('post', data, pids, callback);
}
function reIndexTids(tids, callback) {
callback = callback || function() {};
if (!Array.isArray(tids) || !tids.length) {
return callback();
}
tracks.getTracksFields(tids, ['tid', 'title', 'uid', 'cid', 'deleted'], function(err, trackData) {
if (err) {
return callback(err);
}
trackData = trackData.filter(function(track) {
return parseInt(track.tid, 10) && parseInt(track.deleted, 10) !== 1;
});
if (!trackData.length) {
return callback(err);
}
async.parallel([
function(next) {
tracksSave(trackData, next);
},
function(next) {
async.each(trackData, function(track, next) {
async.parallel([
function (next) {
tracks.getTrackField(track.tid, 'mainPid', function(err, mainPid) {
if (err) {
return next(err);
}
reIndexPids([mainPid], track, next);
});
},
function (next) {
batch.processSortedSet('tid:' + track.tid + ':posts', function(pids, next) {
reIndexPids(pids, track, next);
}, {
batch: batchSize
}, next);
}
], next);
}, next);
}
], callback);
});
}
function reIndexPids(pids, track, callback) {
callback = callback || function() {};
if (!Array.isArray(pids) || !pids.length) {
winston.warn('[audiovanish-plugin-dbsearch] invalid-pid, skipping');
return callback();
}
if (parseInt(track.deleted) === 1) {
return callback();
}
async.waterfall([
function(next) {
posts.getPostsFields(pids, ['pid', 'content', 'uid', 'tid', 'deleted'], next);
},
function(posts, next) {
posts.forEach(function(post) {
if (post && track) {
post.cid = track.cid;
}
});
postsSave(posts, next);
}
], callback);
}
function renderAdmin(req, res, next) {
async.parallel({
data: function(next) {
db.getObject('audiovanish-plugin-dbsearch', next);
},
global: function(next) {
db.getObjectFields('global', ['trackCount', 'postCount'], next);
}
}, function(err, results) {
if (err) {
return next(err);
}
if (!results.data) {
results.data = {
trackLimit: defaultTrackLimit,
postLimit: defaultPostLimit
};
}
results.data.trackCount = results.global.trackCount;
results.data.postCount = results.global.postCount;
results.data.csrf = req.csrfToken();
res.render('admin/plugins/dbsearch', results.data);
});
}
function save(req, res, next) {
if (utils.isNumber(req.body.postLimit) && utils.isNumber(req.body.trackLimit)) {
var data = {
postLimit: req.body.postLimit,
trackLimit: req.body.trackLimit
};
db.setObject('audiovanish-plugin-dbsearch', data, function(err) {
if (err) {
return res.json(500, 'error-saving');
}
postLimit = data.postLimit;
trackLimit = data.trackLimit;
res.json('Settings saved!');
});
}
}
socketAdmin.plugins.dbsearch = {};
socketAdmin.plugins.dbsearch.checkProgress = function(socket, data, callback) {
var tracksPercent = trackCount ? (tracksProcessed / trackCount) * 100 : 0;
var postsPercent = postCount ? (postsProcessed / postCount) * 100 : 0;
var checkProgress = {
tracksPercent: Math.min(100, tracksPercent.toFixed(2)),
postsPercent: Math.min(100, postsPercent.toFixed(2)),
tracksProcessed: tracksPercent >= 100 ? trackCount : tracksProcessed,
postsProcessed: postsPercent >= 100 ? postCount : postsProcessed
};
callback(null, checkProgress);
};
socketAdmin.plugins.dbsearch.reindex = function(socket, data, callback) {
search.reindex(function(err) {
if (err) {
return callback(err);
}
tracksProcessed = trackCount;
postsProcessed = postCount;
var data = {postsIndexed: postCount, tracksIndexed: trackCount};
db.setObject('audiovanish-plugin-dbsearch', data);
callback(null, data);
});
};
socketAdmin.plugins.dbsearch.clearIndex = function(socket, data, callback) {
tracksProcessed = 0;
postsProcessed = 0;
db.getObjectFields('global', ['trackCount', 'postCount'], function(err, data) {
if (err) {
return callback(err);
}
trackCount = data.trackCount;
postCount = data.postCount;
async.parallel([
function (next) {
clearSet('tracks:tid', 'track', next);
},
function (next) {
clearSet('posts:pid', 'post', next);
}
], function(err) {
if (err) {
return callback(err);
}
db.setObject('audiovanish-plugin-dbsearch', {postsIndexed: 0, tracksIndexed: 0});
callback();
});
});
};
function clearSet(set, key, callback) {
batch.processSortedSet(set, function(ids, next) {
db.searchRemove(key, ids, function(err) {
if (err) {
return next(err);
}
if (key === 'track') {
tracksProcessed += ids.length;
} else if (key === 'post') {
postsProcessed += ids.length;
}
next();
});
}, {
batch: batchSize
}, callback);
}
var admin = {};
admin.menu = function(custom_header, callback) {
custom_header.plugins.push({
route: '/plugins/dbsearch',
icon: 'fa-search',
name: 'DB Search'
});
callback(null, custom_header);
};
search.admin = admin;
}(module.exports));