Merge remote-tracking branch 'origin/search-cid-uid'

v1.18.x
Barış Soner Uşaklı 10 years ago
commit 877b2d51fc

@ -89,8 +89,8 @@ define('forum/category', [
});
};
Category.navigatorCallback = function(element, elementCount) {
return parseInt(element.attr('data-index'), 10) + 1;
Category.navigatorCallback = function(topIndex, bottomIndex, elementCount) {
return bottomIndex;
};
$(window).on('action:popstate', function(ev, data) {

@ -148,18 +148,18 @@ define('forum/topic', [
return index;
};
Topic.navigatorCallback = function(element, elementCount) {
Topic.navigatorCallback = function(topPostIndex, bottomPostIndex, elementCount) {
var path = ajaxify.removeRelativePath(window.location.pathname.slice(1));
if (!path.startsWith('topic')) {
return 1;
}
var postIndex = parseInt(element.attr('data-index'), 10);
var index = postIndex + 1;
var postIndex = topPostIndex;
var index = bottomPostIndex;
if (config.topicPostSort !== 'oldest_to_newest') {
if (postIndex === 0) {
if (bottomPostIndex === 0) {
index = 1;
} else {
index = Math.max(elementCount - postIndex + 1, 1);
index = Math.max(elementCount - bottomPostIndex + 2, 1);
}
}
@ -175,8 +175,8 @@ define('forum/topic', [
var topicId = parts[1],
slug = parts[2];
var newUrl = 'topic/' + topicId + '/' + (slug ? slug : '');
if (postIndex > 0) {
newUrl += '/' + (postIndex + 1);
if (postIndex > 1) {
newUrl += '/' + postIndex;
}
if (newUrl !== currentUrl) {

@ -90,18 +90,26 @@ define('navigator', ['forum/pagination'], function(pagination) {
navigator.update = function() {
toggle(!!count);
$($(navigator.selector).get().reverse()).each(function() {
var topIndex = 0;
var bottomIndex = 0;
$(navigator.selector).each(function() {
var el = $(this);
if (elementInView(el)) {
if (typeof navigator.callback === 'function') {
index = navigator.callback(el, count);
navigator.updateTextAndProgressBar();
if (!topIndex) {
topIndex = parseInt(el.attr('data-index'), 10) + 1;
} else {
bottomIndex = parseInt(el.attr('data-index'), 10) + 1;
}
} else if (topIndex && bottomIndex) {
return false;
}
});
if (typeof navigator.callback === 'function' && topIndex && bottomIndex) {
index = navigator.callback(topIndex, bottomIndex, count);
navigator.updateTextAndProgressBar();
}
};
navigator.updateTextAndProgressBar = function() {

@ -50,7 +50,7 @@ define('share', function() {
function getPostUrl(clickedElement) {
var parts = window.location.pathname.split('/');
var postIndex = parseInt(clickedElement.parents('data-index').attr('data-index'), 10);
var postIndex = parseInt(clickedElement.parents('[data-index]').attr('data-index'), 10);
return '/' + parts[1] + '/' + parts[2] + (parts[3] ? '/' + parts[3] : '') + (postIndex ? '/' + (postIndex + 1) : '');
}

@ -74,37 +74,29 @@ module.exports = function(Categories) {
});
};
Categories.onNewPostMade = function(postData, callback) {
topics.getTopicFields(postData.tid, ['cid', 'pinned'], function(err, topicData) {
if (err) {
return callback(err);
}
if (!topicData || !topicData.cid) {
return callback();
}
var cid = topicData.cid;
Categories.onNewPostMade = function(cid, pinned, postData, callback) {
if (!cid || !postData) {
return callback();
}
async.parallel([
function(next) {
db.sortedSetAdd('cid:' + cid + ':pids', postData.timestamp, postData.pid, next);
},
function(next) {
db.incrObjectField('category:' + cid, 'post_count', next);
},
function(next) {
if (parseInt(topicData.pinned, 10) === 1) {
next();
} else {
db.sortedSetAdd('cid:' + cid + ':tids', postData.timestamp, postData.tid, next);
}
},
function(next) {
db.sortedSetIncrBy('cid:' + cid + ':tids:posts', 1, postData.tid, next);
async.parallel([
function(next) {
db.sortedSetAdd('cid:' + cid + ':pids', postData.timestamp, postData.pid, next);
},
function(next) {
db.incrObjectField('category:' + cid, 'post_count', next);
},
function(next) {
if (parseInt(pinned, 10) === 1) {
next();
} else {
db.sortedSetAdd('cid:' + cid + ':tids', postData.timestamp, postData.tid, next);
}
], callback);
});
},
function(next) {
db.sortedSetIncrBy('cid:' + cid + ':tids:posts', 1, postData.tid, next);
}
], callback);
};
};

@ -110,8 +110,14 @@
createIndex('objects', {_key: 1, value: -1}, {background:true});
createIndex('objects', {expireAt: 1}, {expireAfterSeconds:0, background:true});
createIndex('search', {content:'text'}, {background:true});
createIndex('search', {key: 1, id: 1}, {background:true});
createIndex('searchtopic', {content: 'text', uid: 1, cid: 1}, {background:true});
createIndex('searchtopic', {id: 1}, {background:true});
createIndex('searchpost', {content: 'text', uid: 1, cid: 1}, {background:true});
createIndex('searchpost', {id: 1}, {background:true});
if (typeof callback === 'function') {
callback();

@ -5,15 +5,18 @@ var winston = require('winston');
module.exports = function(db, module) {
var helpers = module.helpers.mongo;
module.searchIndex = function(key, content, id, callback) {
module.searchIndex = function(key, data, id, callback) {
callback = callback || function() {};
var data = {
id: id,
key: key,
content: content
var setData = {
id: id
};
for(var field in data) {
if (data.hasOwnProperty(field) && data[field]) {
setData[field] = data[field].toString();
}
}
db.collection('search').update({key:key, id:id}, {$set:data}, {upsert:true, w: 1}, function(err) {
db.collection('search' + key).update({id: id}, {$set: setData}, {upsert:true, w: 1}, function(err) {
if(err) {
winston.error('Error indexing ' + err.message);
}
@ -21,13 +24,35 @@ module.exports = function(db, module) {
});
};
module.search = function(key, term, limit, callback) {
db.collection('search').find({ $text: { $search: term }, key: key}, {limit: limit}).toArray(function(err, results) {
if(err) {
module.search = function(key, data, limit, callback) {
var searchQuery = {};
if (data.content) {
searchQuery.$text = {$search: data.content};
}
if (Array.isArray(data.cid) && data.cid.length) {
if (data.cid.length > 1) {
searchQuery.cid = {$in: data.cid.map(String)};
} else {
searchQuery.cid = data.cid[0].toString();
}
}
if (Array.isArray(data.uid) && data.uid.length) {
if (data.uid.length > 1) {
searchQuery.uid = {$in: data.uid.map(String)};
} else {
searchQuery.uid = data.uid[0].toString();
}
}
db.collection('search' + key).find(searchQuery, {limit: limit}).toArray(function(err, results) {
if (err) {
return callback(err);
}
if(!results || !results.length) {
if (!results || !results.length) {
return callback(null, []);
}
@ -44,7 +69,9 @@ module.exports = function(db, module) {
if (!id) {
return callback();
}
db.collection('search').remove({key: key, id: id}, callback);
db.collection('search' + key).remove({id: id}, function(err, res) {
callback(err);
});
};
module.flushdb = function(callback) {

@ -9,7 +9,7 @@
utils = require('./../../public/src/utils.js'),
redis,
connectRedis,
reds,
redisSearch,
redisClient,
postSearch,
topicSearch;
@ -41,7 +41,7 @@
try {
redis = require('redis');
connectRedis = require('connect-redis')(session);
reds = require('reds');
redisSearch = require('redisearch');
} catch (err) {
winston.error('Unable to initialize Redis! Is Redis installed? Error :' + err.message);
process.exit();
@ -56,12 +56,8 @@
ttl: 60 * 60 * 24 * 14
});
reds.createClient = function () {
return reds.client || (reds.client = redisClient);
};
module.postSearch = reds.createSearch('nodebbpostsearch');
module.topicSearch = reds.createSearch('nodebbtopicsearch');
module.postSearch = redisSearch.createSearch('nodebbpostsearch', redisClient);
module.topicSearch = redisSearch.createSearch('nodebbtopicsearch', redisClient);
require('./redis/main')(redisClient, module);
require('./redis/hash')(redisClient, module);

@ -1,28 +1,18 @@
"use strict";
module.exports = function(redisClient, module) {
module.searchIndex = function(key, content, id, callback) {
if (key === 'post') {
module.postSearch.index(content, id, callback);
} else if(key === 'topic') {
module.topicSearch.index(content, id, callback);
}
module.searchIndex = function(key, data, id, callback) {
var method = key === 'post' ? module.postSearch : module.topicSearch;
method.index(data, id, function(err, res) {
callback(err);
});
};
module.search = function(key, term, limit, callback) {
function search(searchObj, callback) {
searchObj
.query(term)
.between(0, limit - 1)
.type('or')
.end(callback);
}
module.search = function(key, data, limit, callback) {
var method = key === 'post' ? module.postSearch : module.topicSearch;
if(key === 'post') {
search(module.postSearch, callback);
} else if(key === 'topic') {
search(module.topicSearch, callback);
}
method.query(data, 0, limit - 1, callback);
};
module.searchRemove = function(key, id, callback) {
@ -30,12 +20,11 @@ module.exports = function(redisClient, module) {
if (!id) {
return callback();
}
var method = key === 'post' ? module.postSearch : module.topicSearch;
if (key === 'post') {
module.postSearch.remove(id, callback);
} else if(key === 'topic') {
module.topicSearch.remove(id, callback);
}
method.remove(id, function(err, res) {
callback(err);
});
};
module.flushdb = function(callback) {

@ -19,7 +19,7 @@ var winston = require('winston'),
var cache = LRU({
max: 1048576,
length: function (n) { return n.length },
length: function (n) { return n.length; },
maxAge: 1000 * 60 * 60
});
@ -63,22 +63,32 @@ var cache = LRU({
},
topic: function(next) {
var tid = postData.tid;
posts.isMain(data.pid, function(err, isMainPost) {
async.parallel({
cid: function(next) {
topics.getTopicField(tid, 'cid', next);
},
isMain: function(next) {
posts.isMain(data.pid, next);
}
}, function(err, results) {
if (err) {
return next(err);
}
options.tags = options.tags || [];
if (!isMainPost) {
if (!results.isMain) {
return next(null, {
tid: tid,
cid: results.cid,
isMainPost: false
});
}
var topicData = {
tid: tid,
cid: results.cid,
uid: postData.uid,
mainPid: data.pid,
title: title,
slug: tid + '/' + utils.slugify(title)
@ -98,8 +108,10 @@ var cache = LRU({
topics.getTopicTagsObjects(tid, function(err, tags) {
next(err, {
tid: tid,
cid: results.cid,
uid: postData.uid,
title: validator.escape(title),
isMainPost: isMainPost,
isMainPost: results.isMain,
tags: tags
});
});
@ -114,6 +126,7 @@ var cache = LRU({
if (err) {
return callback(err);
}
postData.cid = results.topic.cid;
results.content = results.postData.content;
plugins.fireHook('action:post.edit', postData);

@ -68,7 +68,13 @@ module.exports = function(Posts) {
topics.onNewPostMade(postData, next);
},
function(next) {
categories.onNewPostMade(postData, next);
topics.getTopicFields(tid, ['cid', 'pinned'], function(err, topicData) {
if (err) {
return next(err);
}
postData.cid = topicData.cid;
categories.onNewPostMade(topicData.cid, topicData.pinned, postData, next);
});
},
function(next) {
db.sortedSetAdd('posts:pid', timestamp, postData.pid, next);

@ -9,16 +9,19 @@ var async = require('async'),
module.exports = function(Posts) {
Posts.delete = function(pid, callback) {
Posts.setPostField(pid, 'deleted', 1, function(err) {
if (err) {
return callback(err);
}
Posts.getPostFields(pid, ['pid', 'tid', 'uid', 'timestamp'], function(err, postData) {
if (err) {
return callback(err);
}
var postData;
async.waterfall([
function(next) {
Posts.setPostField(pid, 'deleted', 1, next);
},
function(next) {
Posts.getPostFields(pid, ['pid', 'tid', 'uid', 'timestamp'], next);
},
function(_post, next) {
postData = _post;
topics.getTopicField(_post.tid, 'cid', next);
},
function(cid, next) {
plugins.fireHook('action:post.delete', pid);
async.parallel([
@ -26,7 +29,7 @@ module.exports = function(Posts) {
updateTopicTimestamp(postData.tid, next);
},
function(next) {
removeFromCategoryRecentPosts(pid, postData.tid, next);
db.sortedSetRemove('cid:' + cid + ':pids', pid, next);
},
function(next) {
Posts.dismissFlag(pid, next);
@ -34,21 +37,25 @@ module.exports = function(Posts) {
], function(err) {
callback(err, postData);
});
});
});
}
], callback);
};
Posts.restore = function(pid, callback) {
Posts.setPostField(pid, 'deleted', 0, function(err) {
if (err) {
return callback(err);
}
Posts.getPostFields(pid, ['pid', 'tid', 'uid', 'content', 'timestamp'], function(err, postData) {
if (err) {
return callback(err);
}
var postData;
async.waterfall([
function(next) {
Posts.setPostField(pid, 'deleted', 0, next);
},
function(next) {
Posts.getPostFields(pid, ['pid', 'tid', 'uid', 'content', 'timestamp'], next);
},
function(_post, next) {
postData = _post;
topics.getTopicField(_post.tid, 'cid', next);
},
function(cid, next) {
postData.cid = cid;
plugins.fireHook('action:post.restore', postData);
async.parallel([
@ -56,13 +63,13 @@ module.exports = function(Posts) {
updateTopicTimestamp(postData.tid, next);
},
function(next) {
addToCategoryRecentPosts(pid, postData.tid, postData.timestamp, next);
db.sortedSetAdd('cid:' + cid + ':pids', postData.timestamp, pid, next);
}
], function(err) {
callback(err, postData);
});
});
});
}
], callback);
};
function updateTopicTimestamp(tid, callback) {
@ -84,26 +91,6 @@ module.exports = function(Posts) {
});
}
function removeFromCategoryRecentPosts(pid, tid, callback) {
topics.getTopicField(tid, 'cid', function(err, cid) {
if (err) {
return callback(err);
}
db.sortedSetRemove('cid:' + cid + ':pids', pid, callback);
});
}
function addToCategoryRecentPosts(pid, tid, timestamp, callback) {
topics.getTopicField(tid, 'cid', function(err, cid) {
if (err) {
return callback(err);
}
db.sortedSetAdd('cid:' + cid + ':pids', timestamp, pid, callback);
});
}
Posts.purge = function(pid, callback) {
Posts.exists(pid, function(err, exists) {
if (err || !exists) {

@ -43,7 +43,7 @@ search.search = function(data, callback) {
};
if (searchIn === 'posts' || searchIn === 'titles' || searchIn === 'titlesposts') {
searchInContent(query, data, done);
searchInContent(data, done);
} else if (searchIn === 'users') {
searchInUsers(query, data.uid, done);
} else if (searchIn === 'tags') {
@ -53,89 +53,87 @@ search.search = function(data, callback) {
}
};
function searchInContent(query, data, callback) {
function searchInContent(data, callback) {
data.uid = data.uid || 0;
async.parallel({
pids: function(next) {
if (data.searchIn === 'posts' || data.searchIn === 'titlesposts') {
search.searchQuery('post', query, next);
} else {
next(null, []);
}
},
tids: function(next) {
if (data.searchIn === 'titles' || data.searchIn === 'titlesposts') {
search.searchQuery('topic', query, next);
} else {
next(null, []);
}
searchCids: function(next) {
getSearchCids(data, next);
},
searchCategories: function(next) {
getSearchCategories(data, next);
searchUids: function(next) {
getSearchUids(data, next);
}
}, function (err, results) {
}, function(err, results) {
if (err) {
return callback(err);
}
var matchCount = 0;
if (!results || (!results.pids.length && !results.tids.length)) {
return callback(null, {matches: [], matchCount: matchCount});
}
async.waterfall([
function(next) {
topics.getMainPids(results.tids, next);
},
function(mainPids, next) {
results.pids = mainPids.concat(results.pids).filter(function(pid, index, array) {
return pid && array.indexOf(pid) === index;
});
privileges.posts.filter('read', results.pids, data.uid, next);
},
function(pids, next) {
filterAndSort(pids, data, results.searchCategories, next);
async.parallel({
pids: function(next) {
if (data.searchIn === 'posts' || data.searchIn === 'titlesposts') {
search.searchQuery('post', data.query, results.searchCids, results.searchUids, next);
} else {
next(null, []);
}
},
function(pids, next) {
matchCount = pids.length;
if (data.page) {
var start = Math.max(0, (data.page - 1)) * 10;
pids = pids.slice(start, start + 10);
tids: function(next) {
if (data.searchIn === 'titles' || data.searchIn === 'titlesposts') {
search.searchQuery('topic', data.query, results.searchCids, results.searchUids, next);
} else {
next(null, []);
}
}
}, function (err, results) {
if (err) {
return callback(err);
}
posts.getPostSummaryByPids(pids, data.uid, {stripTags: true, parse: false}, next);
},
function(posts, next) {
next(null, {matches: posts, matchCount: matchCount});
var matchCount = 0;
if (!results || (!results.pids.length && !results.tids.length)) {
return callback(null, {matches: [], matchCount: matchCount});
}
], callback);
async.waterfall([
function(next) {
topics.getMainPids(results.tids, next);
},
function(mainPids, next) {
results.pids = mainPids.concat(results.pids).filter(function(pid, index, array) {
return pid && array.indexOf(pid) === index;
});
privileges.posts.filter('read', results.pids, data.uid, next);
},
function(pids, next) {
filterAndSort(pids, data, next);
},
function(pids, next) {
matchCount = pids.length;
if (data.page) {
var start = Math.max(0, (data.page - 1)) * 10;
pids = pids.slice(start, start + 10);
}
posts.getPostSummaryByPids(pids, data.uid, {stripTags: true, parse: false}, next);
},
function(posts, next) {
next(null, {matches: posts, matchCount: matchCount});
}
], callback);
});
});
}
function filterAndSort(pids, data, searchCategories, callback) {
async.parallel({
posts: function(next) {
getMatchedPosts(pids, data, searchCategories, next);
},
postedByUid: function(next) {
if (data.postedBy) {
user.getUidByUsername(data.postedBy, next);
} else {
next();
}
}
}, function(err, results) {
function filterAndSort(pids, data, callback) {
getMatchedPosts(pids, data, function(err, posts) {
if (err) {
return callback(err);
}
if (!results.posts) {
if (!Array.isArray(posts) || !posts.length) {
return callback(null, pids);
}
var posts = results.posts.filter(Boolean);
posts = posts.filter(Boolean);
posts = filterByUser(posts, results.postedByUid);
posts = filterByCategories(posts, searchCategories);
posts = filterByPostcount(posts, data.replies, data.repliesFilter);
posts = filterByTimerange(posts, data.timeRange, data.timeFilter);
@ -149,25 +147,19 @@ function filterAndSort(pids, data, searchCategories, callback) {
});
}
function getMatchedPosts(pids, data, searchCategories, callback) {
function getMatchedPosts(pids, data, callback) {
var postFields = ['pid', 'tid', 'timestamp'];
var topicFields = [];
var categoryFields = [];
if (data.postedBy) {
postFields.push('uid');
}
if (searchCategories.length || (data.sortBy && data.sortBy.startsWith('category.'))) {
topicFields.push('cid');
}
if (data.replies) {
topicFields.push('postcount');
}
if (data.sortBy) {
if (data.sortBy.startsWith('topic.')) {
if (data.sortBy.startsWith('category')) {
topicFields.push('cid');
} else if (data.sortBy.startsWith('topic.')) {
topicFields.push(data.sortBy.split('.')[1]);
} else if (data.sortBy.startsWith('user.')) {
postFields.push('uid');
@ -280,25 +272,6 @@ function getMatchedPosts(pids, data, searchCategories, callback) {
], callback);
}
function filterByUser(posts, postedByUid) {
if (postedByUid) {
postedByUid = parseInt(postedByUid, 10);
posts = posts.filter(function(post) {
return parseInt(post.uid, 10) === postedByUid;
});
}
return posts;
}
function filterByCategories(posts, searchCategories) {
if (searchCategories.length) {
posts = posts.filter(function(post) {
return post.topic && searchCategories.indexOf(post.topic.cid) !== -1;
});
}
return posts;
}
function filterByPostcount(posts, postCount, repliesFilter) {
postCount = parseInt(postCount, 10);
if (postCount) {
@ -389,7 +362,7 @@ function sortPosts(posts, data) {
}
}
function getSearchCategories(data, callback) {
function getSearchCids(data, callback) {
if (!Array.isArray(data.categories) || !data.categories.length || data.categories.indexOf('all') !== -1) {
return callback(null, []);
}
@ -439,6 +412,14 @@ function getChildrenCids(cids, uid, callback) {
});
}
function getSearchUids(data, callback) {
if (data.postedBy) {
user.getUidsByUsernames(Array.isArray(data.postedBy) ? data.postedBy : [data.postedBy], callback);
} else {
callback(null, []);
}
}
function searchInUsers(query, uid, callback) {
user.search({query: query, uid: uid}, function(err, results) {
if (err) {
@ -458,10 +439,12 @@ function searchInTags(query, callback) {
});
}
search.searchQuery = function(index, query, callback) {
search.searchQuery = function(index, content, cids, uids, callback) {
plugins.fireHook('filter:search.query', {
index: index,
query: query
content: content,
cid: cids,
uid: uids
}, callback);
};

@ -183,13 +183,16 @@ var async = require('async'),
categories.moveRecentReplies(tid, oldCid, cid);
topics.setTopicField(tid, 'cid', cid, callback);
plugins.fireHook('action:topic.move', {
tid: tid,
fromCid: oldCid,
toCid: cid,
uid: uid
topics.setTopicField(tid, 'cid', cid, function(err) {
if (err) {
return callback(err);
}
plugins.fireHook('action:topic.move', {
tid: tid,
fromCid: oldCid,
toCid: cid,
uid: uid
});
});
});
};

@ -158,7 +158,7 @@ var async = require('async'),
if (now - parseInt(userOnlineTime, 10) < 300000) {
return callback();
}
db.sortedSetAdd('users:online', now, uid, next);
db.sortedSetAdd('users:online', now, uid, next);
},
function(next) {
topics.pushUnreadCount(uid);
@ -334,6 +334,18 @@ var async = require('async'),
db.getObjectField('username:uid', username, callback);
};
User.getUidsByUsernames = function(usernames, callback) {
db.getObjectFields('username:uid', usernames, function(err, users) {
if (err) {
return callback(err);
}
var uids = usernames.map(function(username) {
return users[username];
});
callback(null, uids);
});
};
User.getUidByUserslug = function(userslug, callback) {
if (!userslug) {
return callback();

Loading…
Cancel
Save