mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-07 22:45:46 +01:00
search sort, search in posts, titles, posts& titles
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
"no-matches": "No matches found",
|
||||
"in": "In",
|
||||
"by": "By",
|
||||
"titles": "Titles",
|
||||
"titles-posts": "Titles and Posts",
|
||||
"posted-by": "Posted by",
|
||||
"in-categories": "In Categories",
|
||||
"search-child-categories": "Search child categories",
|
||||
@@ -19,5 +21,16 @@
|
||||
"one-month": "One month",
|
||||
"three-months": "Three months",
|
||||
"six-months": "Six months",
|
||||
"one-year": "One year"
|
||||
"one-year": "One year",
|
||||
"sort-by": "Sort by",
|
||||
"post-time": "Post time",
|
||||
"last-reply-time": "Last reply time",
|
||||
"topic-title": "Topic title",
|
||||
"number-of-replies": "Number of replies",
|
||||
"number-of-views": "Number of views",
|
||||
"topic-start-date": "Topic start date",
|
||||
"username": "Username",
|
||||
"category": "Category",
|
||||
"descending": "In descending order",
|
||||
"ascending": "In ascending order"
|
||||
}
|
||||
|
||||
@@ -436,7 +436,7 @@ app.uid = null;
|
||||
e.preventDefault();
|
||||
var input = $(this).find('input');
|
||||
|
||||
search.query({term: input.val(), in: 'posts'}, function() {
|
||||
search.query({term: input.val()}, function() {
|
||||
input.val('');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,26 +15,31 @@ define('forum/search', ['search'], function(searchModule) {
|
||||
fillOutFormFromQueryParams();
|
||||
|
||||
searchIn.on('change', function() {
|
||||
$('.post-search-item').toggleClass('hide', searchIn.val() !== 'posts');
|
||||
updateFormItemVisiblity(searchIn.val());
|
||||
});
|
||||
|
||||
highlightMatches(searchQuery);
|
||||
|
||||
$('#advanced-search').off('submit').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
var input = $(this).find('#search-input');
|
||||
var $this = $(this)
|
||||
var input = $this.find('#search-input');
|
||||
|
||||
searchModule.query({
|
||||
var searchData = {
|
||||
term: input.val(),
|
||||
in: $(this).find('#search-in').val(),
|
||||
by: $(this).find('#posted-by-user').val(),
|
||||
categories: $(this).find('#posted-in-categories').val(),
|
||||
searchChildren: $(this).find('#search-children').is(':checked'),
|
||||
replies: $(this).find('#reply-count').val(),
|
||||
repliesFilter: $(this).find('#reply-count-filter').val(),
|
||||
timeFilter: $(this).find('#post-time-filter').val(),
|
||||
timeRange: $(this).find('#post-time-range').val()
|
||||
}, function() {
|
||||
in: $this.find('#search-in').val(),
|
||||
by: $this.find('#posted-by-user').val(),
|
||||
categories: $this.find('#posted-in-categories').val(),
|
||||
searchChildren: $this.find('#search-children').is(':checked'),
|
||||
replies: $this.find('#reply-count').val(),
|
||||
repliesFilter: $this.find('#reply-count-filter').val(),
|
||||
timeFilter: $this.find('#post-time-filter').val(),
|
||||
timeRange: $this.find('#post-time-range').val(),
|
||||
sortBy: $this.find('#post-sort-by').val(),
|
||||
sortDirection: $this.find('#post-sort-direction').val()
|
||||
};
|
||||
|
||||
searchModule.query(searchData, function() {
|
||||
input.val('');
|
||||
});
|
||||
});
|
||||
@@ -42,12 +47,17 @@ define('forum/search', ['search'], function(searchModule) {
|
||||
enableAutoComplete();
|
||||
};
|
||||
|
||||
function updateFormItemVisiblity(searchIn) {
|
||||
var hide = searchIn.indexOf('posts') === -1 && searchIn.indexOf('titles') === -1;
|
||||
$('.post-search-item').toggleClass('hide', hide);
|
||||
}
|
||||
|
||||
function fillOutFormFromQueryParams() {
|
||||
var params = utils.params();
|
||||
if (params) {
|
||||
if (params.in) {
|
||||
$('#search-in').val(params.in);
|
||||
$('.post-search-item').toggleClass('hide', params.in !== 'posts');
|
||||
updateFormItemVisiblity(params.in);
|
||||
}
|
||||
|
||||
if (params.by) {
|
||||
@@ -71,6 +81,11 @@ define('forum/search', ['search'], function(searchModule) {
|
||||
$('#post-time-range').val(params.timeRange);
|
||||
$('#post-time-filter').val(params.timeFilter);
|
||||
}
|
||||
|
||||
if (params.sortBy) {
|
||||
$('#post-sort-by').val(params.sortBy);
|
||||
$('#post-sort-direction').val(params.sortDirection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,6 @@ define('search', ['navigator'], function(nav) {
|
||||
|
||||
Search.query = function(data, callback) {
|
||||
var term = data.term;
|
||||
var searchIn = data.in || 'posts';
|
||||
var postedBy = data.by || '';
|
||||
|
||||
// Detect if a tid was specified
|
||||
var topicSearch = term.match(/in:topic-([\d]+)/);
|
||||
@@ -23,8 +21,23 @@ define('search', ['navigator'], function(nav) {
|
||||
} catch(e) {
|
||||
return app.alertError('[[error:invalid-search-term]]');
|
||||
}
|
||||
|
||||
ajaxify.go('search/' + term + '?' + createQueryString(data));
|
||||
callback();
|
||||
} else {
|
||||
var cleanedTerm = term.replace(topicSearch[0], ''),
|
||||
tid = topicSearch[1];
|
||||
|
||||
Search.queryTopic(tid, cleanedTerm, callback);
|
||||
}
|
||||
};
|
||||
|
||||
function createQueryString(data) {
|
||||
var searchIn = data.in || 'titlesposts';
|
||||
var postedBy = data.by || '';
|
||||
var query = {in: searchIn};
|
||||
if (postedBy && searchIn === 'posts') {
|
||||
|
||||
if (postedBy && (searchIn === 'posts' || searchIn === 'titles' || searchIn === 'titlesposts')) {
|
||||
query.by = postedBy;
|
||||
}
|
||||
|
||||
@@ -45,15 +58,12 @@ define('search', ['navigator'], function(nav) {
|
||||
query.timeFilter = data.timeFilter || 'newer';
|
||||
}
|
||||
|
||||
ajaxify.go('search/' + term + '?' + decodeURIComponent($.param(query)));
|
||||
callback();
|
||||
} else {
|
||||
var cleanedTerm = term.replace(topicSearch[0], ''),
|
||||
tid = topicSearch[1];
|
||||
|
||||
Search.queryTopic(tid, cleanedTerm, callback);
|
||||
if (data.sortBy) {
|
||||
query.sortBy = data.sortBy;
|
||||
query.sortDirection = data.sortDirection;
|
||||
}
|
||||
return decodeURIComponent($.param(query));
|
||||
}
|
||||
};
|
||||
|
||||
Search.queryTopic = function(tid, term, callback) {
|
||||
socket.emit('topics.search', {
|
||||
|
||||
@@ -28,7 +28,6 @@ searchController.search = function(req, res, next) {
|
||||
time: 0,
|
||||
search_query: '',
|
||||
posts: [],
|
||||
topics: [],
|
||||
users: [],
|
||||
tags: [],
|
||||
categories: categories,
|
||||
@@ -49,6 +48,8 @@ searchController.search = function(req, res, next) {
|
||||
repliesFilter: req.query.repliesFilter,
|
||||
timeRange: req.query.timeRange,
|
||||
timeFilter: req.query.timeFilter,
|
||||
sortBy: req.query.sortBy,
|
||||
sortDirection: req.query.sortDirection,
|
||||
page: page,
|
||||
uid: uid
|
||||
}, function(err, results) {
|
||||
|
||||
228
src/search.js
228
src/search.js
@@ -8,7 +8,8 @@ var async = require('async'),
|
||||
categories = require('./categories'),
|
||||
user = require('./user'),
|
||||
plugins = require('./plugins'),
|
||||
privileges = require('./privileges');
|
||||
privileges = require('./privileges'),
|
||||
utils = require('../public/src/utils');
|
||||
|
||||
var search = {};
|
||||
|
||||
@@ -21,6 +22,9 @@ search.search = function(data, callback) {
|
||||
}
|
||||
|
||||
result.search_query = query;
|
||||
if (searchIn === 'titles' || searchIn === 'titlesposts') {
|
||||
searchIn = 'posts';
|
||||
}
|
||||
result[searchIn] = data.matches;
|
||||
result.matchCount = data.matchCount;
|
||||
result.hidePostedBy = searchIn !== 'posts';
|
||||
@@ -31,7 +35,7 @@ search.search = function(data, callback) {
|
||||
var start = process.hrtime();
|
||||
|
||||
var query = data.query;
|
||||
var searchIn = data.searchIn || 'posts';
|
||||
var searchIn = data.searchIn || 'titlesposts';
|
||||
|
||||
var result = {
|
||||
posts: [],
|
||||
@@ -39,8 +43,8 @@ search.search = function(data, callback) {
|
||||
tags: []
|
||||
};
|
||||
|
||||
if (searchIn === 'posts') {
|
||||
searchInPosts(query, data, done);
|
||||
if (searchIn === 'posts' || searchIn === 'titles' || searchIn === 'titlesposts') {
|
||||
searchInContent(query, data, done);
|
||||
} else if (searchIn === 'users') {
|
||||
searchInUsers(query, data.uid, done);
|
||||
} else if (searchIn === 'tags') {
|
||||
@@ -50,14 +54,22 @@ search.search = function(data, callback) {
|
||||
}
|
||||
};
|
||||
|
||||
function searchInPosts(query, data, callback) {
|
||||
function searchInContent(query, data, callback) {
|
||||
data.uid = data.uid || 0;
|
||||
async.parallel({
|
||||
pids: function(next) {
|
||||
if (data.searchIn === 'posts' || data.searchIn === 'titlesposts') {
|
||||
searchQuery('post', query, next);
|
||||
} else {
|
||||
next(null, []);
|
||||
}
|
||||
},
|
||||
tids: function(next) {
|
||||
if (data.searchIn === 'titles' || data.searchIn === 'titlesposts') {
|
||||
searchQuery('topic', query, next);
|
||||
} else {
|
||||
next(null, []);
|
||||
}
|
||||
},
|
||||
searchCategories: function(next) {
|
||||
getSearchCategories(data, next);
|
||||
@@ -104,24 +116,9 @@ function searchInPosts(query, data, callback) {
|
||||
}
|
||||
|
||||
function filterAndSort(pids, data, searchCategories, callback) {
|
||||
var postFields = ['pid', 'tid', 'timestamp'];
|
||||
var topicFields = [];
|
||||
|
||||
if (data.postedBy) {
|
||||
postFields.push('uid');
|
||||
}
|
||||
|
||||
if (searchCategories.length) {
|
||||
topicFields.push('cid');
|
||||
}
|
||||
|
||||
if (data.replies) {
|
||||
topicFields.push('postcount');
|
||||
}
|
||||
|
||||
async.parallel({
|
||||
posts: function(next) {
|
||||
getMatchedPosts(pids, postFields, topicFields, next);
|
||||
getMatchedPosts(pids, data, searchCategories, next);
|
||||
},
|
||||
postedByUid: function(next) {
|
||||
if (data.postedBy) {
|
||||
@@ -154,28 +151,130 @@ function filterAndSort(pids, data, searchCategories, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function getMatchedPosts(pids, postFields, topicFields, callback) {
|
||||
var keys = pids.map(function(pid) {
|
||||
return 'post:' + pid;
|
||||
});
|
||||
function getMatchedPosts(pids, data, searchCategories, 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.')) {
|
||||
topicFields.push(data.sortBy.split('.')[1]);
|
||||
} else if (data.sortBy.startsWith('user.')) {
|
||||
postFields.push('uid');
|
||||
} else if (data.sortBy.startsWith('category.')) {
|
||||
categoryFields.push(data.sortBy.split('.')[1]);
|
||||
} else if (data.sortBy.startsWith('teaser')) {
|
||||
topicFields.push('teaserPid');
|
||||
}
|
||||
}
|
||||
|
||||
var posts;
|
||||
async.waterfall([
|
||||
function(next) {
|
||||
var keys = pids.map(function(pid) {
|
||||
return 'post:' + pid;
|
||||
});
|
||||
db.getObjectsFields(keys, postFields, next);
|
||||
},
|
||||
function(_posts, next) {
|
||||
posts = _posts;
|
||||
if (!topicFields.length) {
|
||||
return callback(null, posts);
|
||||
|
||||
async.parallel({
|
||||
users: function(next) {
|
||||
if (data.sortBy && data.sortBy.startsWith('user')) {
|
||||
var uids = posts.map(function(post) {
|
||||
return post.uid;
|
||||
});
|
||||
user.getMultipleUserFields(uids, ['username'], next);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
},
|
||||
topics: function(next) {
|
||||
if (!topicFields.length) {
|
||||
return next();
|
||||
}
|
||||
|
||||
var topics;
|
||||
async.waterfall([
|
||||
function(next) {
|
||||
var topicKeys = posts.map(function(post) {
|
||||
return 'topic:' + post.tid;
|
||||
});
|
||||
db.getObjectsFields(topicKeys, topicFields, next);
|
||||
},
|
||||
function(topics, next) {
|
||||
function(_topics, next) {
|
||||
topics = _topics;
|
||||
|
||||
async.parallel({
|
||||
teasers: function(next) {
|
||||
if (topicFields.indexOf('teaserPid') !== -1) {
|
||||
var teaserKeys = topics.map(function(topic) {
|
||||
return 'post:' + topic.teaserPid;
|
||||
});
|
||||
db.getObjectsFields(teaserKeys, ['timestamp'], next);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
},
|
||||
categories: function(next) {
|
||||
if (!categoryFields.length) {
|
||||
return next();
|
||||
}
|
||||
var cids = topics.map(function(topic) {
|
||||
return 'category:' + topic.cid;
|
||||
});
|
||||
db.getObjectsFields(cids, categoryFields, next);
|
||||
}
|
||||
}, next);
|
||||
}
|
||||
], function(err, results) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
topics.forEach(function(topic, index) {
|
||||
if (topic && results.categories && results.categories[index]) {
|
||||
topic.category = results.categories[index];
|
||||
}
|
||||
if (topic && results.teasers && results.teasers[index]) {
|
||||
topic.teaser = results.teasers[index];
|
||||
}
|
||||
});
|
||||
|
||||
next(null, topics);
|
||||
});
|
||||
}
|
||||
}, next);
|
||||
},
|
||||
function(results, next) {
|
||||
|
||||
posts.forEach(function(post, index) {
|
||||
post.topic = topics[index];
|
||||
if (results.topics && results.topics[index]) {
|
||||
post.topic = results.topics[index];
|
||||
if (results.topics[index].category) {
|
||||
post.category = results.topics[index].category;
|
||||
}
|
||||
if (results.topics[index].teaser) {
|
||||
post.teaser = results.topics[index].teaser;
|
||||
}
|
||||
}
|
||||
|
||||
if (results.users && results.users[index]) {
|
||||
post.user = results.users[index];
|
||||
}
|
||||
});
|
||||
|
||||
next(null, posts);
|
||||
@@ -236,9 +335,76 @@ function filterByTimerange(posts, timeRange, timeFilter) {
|
||||
}
|
||||
|
||||
function sortPosts(posts, data) {
|
||||
if (!posts.length) {
|
||||
return;
|
||||
}
|
||||
data.sortBy = data.sortBy || 'timestamp';
|
||||
data.sortDirection = data.sortDirection || 'desc';
|
||||
if (data.sortBy === 'timestamp') {
|
||||
if (data.sortDirection === 'desc') {
|
||||
posts.sort(function(p1, p2) {
|
||||
return p2.timestamp - p1.timestamp;
|
||||
});
|
||||
} else {
|
||||
posts.sort(function(p1, p2) {
|
||||
return p1.timestamp - p2.timestamp;
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var firstPost = posts[0];
|
||||
var fields = data.sortBy.split('.');
|
||||
|
||||
if (!fields || fields.length !== 2 || !firstPost[fields[0]] || !firstPost[fields[0]][fields[1]]) {
|
||||
return;
|
||||
}
|
||||
|
||||
var value = firstPost[fields[0]][fields[1]];
|
||||
var isNumeric = utils.isNumber(value);
|
||||
|
||||
if (isNumeric) {
|
||||
if (data.sortDirection === 'desc') {
|
||||
sortDescendingNumeric(posts, fields);
|
||||
} else {
|
||||
sortAscendingNumeric(posts, fields);
|
||||
}
|
||||
} else {
|
||||
if (data.sortDirection === 'desc') {
|
||||
sortDescendingAlpha(posts, fields);
|
||||
} else {
|
||||
sortAscendingAlpha(posts, fields);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sortAscendingNumeric(posts, fields) {
|
||||
posts.sort(function(p1, p2) {
|
||||
return p1[fields[0]][fields[1]] - p2[fields[0]][fields[1]];
|
||||
});
|
||||
}
|
||||
|
||||
function sortDescendingNumeric(posts, fields) {
|
||||
posts.sort(function(p1, p2) {
|
||||
return p2[fields[0]][fields[1]] - p1[fields[0]][fields[1]];
|
||||
});
|
||||
}
|
||||
|
||||
function sortAscendingAlpha(posts, fields) {
|
||||
posts.sort(function(p1, p2) {
|
||||
if (p1[fields[0]][fields[1]] > p2[fields[0]][fields[1]]) return -1;
|
||||
if (p1[fields[0]][fields[1]] < p2[fields[0]][fields[1]]) return 1;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
function sortDescendingAlpha(posts, fields) {
|
||||
posts.sort(function(p1, p2) {
|
||||
if (p1[fields[0]][fields[1]] < p2[fields[0]][fields[1]]) return -1;
|
||||
if (p1[fields[0]][fields[1]] > p2[fields[0]][fields[1]]) return 1;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
function getSearchCategories(data, callback) {
|
||||
@@ -311,6 +477,10 @@ function searchInTags(query, callback) {
|
||||
}
|
||||
|
||||
function getMainPids(tids, callback) {
|
||||
if (!Array.isArray(tids) || !tids.length) {
|
||||
return callback(null, []);
|
||||
}
|
||||
|
||||
topics.getTopicsFields(tids, ['mainPid'], function(err, topics) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
|
||||
Reference in New Issue
Block a user