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.
nodebb/src/search.js

332 lines
7.5 KiB
JavaScript

11 years ago
'use strict';
var async = require('async'),
db = require('./database'),
11 years ago
posts = require('./posts'),
topics = require('./topics'),
categories = require('./categories'),
10 years ago
user = require('./user'),
11 years ago
plugins = require('./plugins'),
privileges = require('./privileges');
var search = {};
module.exports = search;
search.search = function(data, callback) {
10 years ago
function done(err, data) {
if (err) {
return callback(err);
}
result.search_query = query;
result[searchIn] = data.matches;
result.matchCount = data.matchCount;
result.hidePostedBy = searchIn !== 'posts';
10 years ago
result.time = (process.elapsedTimeSince(start) / 1000).toFixed(2);
callback(null, result);
}
11 years ago
var start = process.hrtime();
var query = data.query;
var searchIn = data.searchIn || 'posts';
11 years ago
10 years ago
var result = {
posts: [],
users: [],
tags: []
};
if (searchIn === 'posts') {
searchInPosts(query, data, done);
10 years ago
} else if (searchIn === 'users') {
searchInUsers(query, data.uid, done);
10 years ago
} else if (searchIn === 'tags') {
searchInTags(query, done);
} else {
callback(new Error('[[error:unknown-search-filter]]'));
}
};
function searchInPosts(query, data, callback) {
data.uid = data.uid || 0;
11 years ago
async.parallel({
pids: function(next) {
10 years ago
searchQuery('post', query, next);
11 years ago
},
tids: function(next) {
10 years ago
searchQuery('topic', query, next);
},
searchCategories: function(next) {
getSearchCategories(data, next);
11 years ago
}
}, function (err, results) {
if (err) {
return callback(err);
}
var matchCount = 0;
11 years ago
if (!results || (!results.pids.length && !results.tids.length)) {
return callback(null, {matches: [], matchCount: matchCount});
11 years ago
}
10 years ago
async.waterfall([
function(next) {
getMainPids(results.tids, next);
},
function(mainPids, next) {
results.pids.forEach(function(pid) {
if (mainPids.indexOf(pid) === -1) {
mainPids.push(pid);
11 years ago
}
});
privileges.posts.filter('read', mainPids, data.uid, next);
10 years ago
},
function(pids, next) {
filterAndSort(pids, data, results.searchCategories, 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});
10 years ago
}
], callback);
11 years ago
});
10 years ago
}
11 years ago
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);
},
postedByUid: function(next) {
if (data.postedBy) {
user.getUidByUsername(data.postedBy, next);
} else {
next();
}
}
}, function(err, results) {
if (err) {
return callback(err);
}
if (!results.posts) {
return callback(null, pids);
}
var posts = results.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);
sortPosts(posts, data);
pids = posts.map(function(post) {
return post && post.pid;
});
callback(null, pids);
});
}
function getMatchedPosts(pids, postFields, topicFields, callback) {
var keys = pids.map(function(pid) {
return 'post:' + pid;
});
var posts;
async.waterfall([
function(next) {
db.getObjectsFields(keys, postFields, next);
},
function(_posts, next) {
posts = _posts;
if (!topicFields.length) {
return callback(null, posts);
}
var topicKeys = posts.map(function(post) {
return 'topic:' + post.tid;
});
db.getObjectsFields(topicKeys, topicFields, next);
},
function(topics, next) {
posts.forEach(function(post, index) {
post.topic = topics[index];
});
next(null, posts);
}
], 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) {
if (repliesFilter === 'atleast') {
posts = posts.filter(function(post) {
return post.topic && post.topic.postcount >= postCount;
});
} else {
posts = posts.filter(function(post) {
return post.topic && post.topic.postcount <= postCount;
});
}
}
return posts;
}
function filterByTimerange(posts, timeRange, timeFilter) {
timeRange = parseInt(timeRange) * 1000;
if (timeRange) {
var time = Date.now() - timeRange;
if (timeFilter === 'newer') {
posts = posts.filter(function(post) {
return post.timestamp >= time;
});
} else {
posts = posts.filter(function(post) {
return post.timestamp <= time;
});
}
}
return posts;
}
function sortPosts(posts, data) {
posts.sort(function(p1, p2) {
return p2.timestamp - p1.timestamp;
});
}
function getSearchCategories(data, callback) {
if (!Array.isArray(data.categories) || !data.categories.length || data.categories.indexOf('all') !== -1) {
return callback(null, []);
}
async.parallel({
watchedCids: function(next) {
if (data.categories.indexOf('watched') !== -1) {
user.getWatchedCategories(data.uid, next);
} else {
next(null, []);
}
},
childrenCids: function(next) {
if (data.searchChildren) {
getChildrenCids(data.categories, data.uid, next);
} else {
next(null, []);
}
}
}, function(err, results) {
if (err) {
return callback(err);
}
var cids = results.watchedCids.concat(results.childrenCids).concat(data.categories).filter(function(cid, index, array) {
return cid && array.indexOf(cid) === index;
});
callback(null, cids);
});
}
function getChildrenCids(cids, uid, callback) {
categories.getChildren(cids, uid, function(err, childrenCategories) {
if (err) {
return callback(err);
}
var childrenCids = [];
childrenCategories.forEach(function(childrens) {
childrenCids = childrenCids.concat(childrens.map(function(category) {
return category && category.cid;
}));
});
callback(null, childrenCids);
});
}
function searchInUsers(query, uid, callback) {
user.search({query: query, uid: uid}, function(err, results) {
if (err) {
return callback(err);
}
callback(null, {matches: results.users, matchCount: results.matchCount});
10 years ago
});
}
function searchInTags(query, callback) {
topics.searchAndLoadTags({query: query}, function(err, tags) {
if (err) {
return callback(err);
}
callback(null, {matches: tags, matchCount: tags.length});
});
10 years ago
}
11 years ago
function getMainPids(tids, callback) {
topics.getTopicsFields(tids, ['mainPid'], function(err, topics) {
if (err) {
return callback(err);
}
topics = topics.map(function(topic) {
return topic && topic.mainPid;
}).filter(Boolean);
11 years ago
callback(null, topics);
});
}
10 years ago
function searchQuery(index, query, callback) {
11 years ago
plugins.fireHook('filter:search.query', {
index: index,
10 years ago
query: query
11 years ago
}, callback);
}