ability to filter search by tags

v1.18.x
barisusakli 8 years ago
parent fef239d97a
commit 1fed01fe43

@ -13,8 +13,8 @@
"start": "node loader.js", "start": "node loader.js",
"lint": "eslint --cache .", "lint": "eslint --cache .",
"pretest": "npm run lint", "pretest": "npm run lint",
"test": "istanbul cover node_modules/mocha/bin/_mocha -- -R spec", "test": "istanbul cover node_modules/mocha/bin/_mocha -- -R dot",
"coveralls": "istanbul cover _mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage" "coveralls": "istanbul cover _mocha --report lcovonly -- -R dot && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage"
}, },
"dependencies": { "dependencies": {
"async": "~1.5.0", "async": "~1.5.0",

@ -8,6 +8,7 @@
"posted-by": "Posted by", "posted-by": "Posted by",
"in-categories": "In Categories", "in-categories": "In Categories",
"search-child-categories": "Search child categories", "search-child-categories": "Search child categories",
"has-tags": "Has tags",
"reply-count": "Reply Count", "reply-count": "Reply Count",
"at-least": "At least", "at-least": "At least",
"at-most": "At most", "at-most": "At most",

@ -12,8 +12,6 @@ define('forum/search', ['search', 'autocomplete'], function (searchModule, autoc
var searchIn = $('#search-in'); var searchIn = $('#search-in');
fillOutForm();
searchIn.on('change', function () { searchIn.on('change', function () {
updateFormItemVisiblity(searchIn.val()); updateFormItemVisiblity(searchIn.val());
}); });
@ -31,6 +29,8 @@ define('forum/search', ['search', 'autocomplete'], function (searchModule, autoc
handleSavePreferences(); handleSavePreferences();
enableAutoComplete(); enableAutoComplete();
fillOutForm();
}; };
function getSearchData() { function getSearchData() {
@ -43,6 +43,7 @@ define('forum/search', ['search', 'autocomplete'], function (searchModule, autoc
searchData.by = form.find('#posted-by-user').val(); searchData.by = form.find('#posted-by-user').val();
searchData.categories = form.find('#posted-in-categories').val(); searchData.categories = form.find('#posted-in-categories').val();
searchData.searchChildren = form.find('#search-children').is(':checked'); searchData.searchChildren = form.find('#search-children').is(':checked');
searchData.hasTags = form.find('#has-tags').tagsinput('items');
searchData.replies = form.find('#reply-count').val(); searchData.replies = form.find('#reply-count').val();
searchData.repliesFilter = form.find('#reply-count-filter').val(); searchData.repliesFilter = form.find('#reply-count-filter').val();
searchData.timeFilter = form.find('#post-time-filter').val(); searchData.timeFilter = form.find('#post-time-filter').val();
@ -79,7 +80,6 @@ define('forum/search', ['search', 'autocomplete'], function (searchModule, autoc
$('#posted-by-user').val(formData.by); $('#posted-by-user').val(formData.by);
} }
if (formData.categories) { if (formData.categories) {
$('#posted-in-categories').val(formData.categories); $('#posted-in-categories').val(formData.categories);
} }
@ -88,6 +88,13 @@ define('forum/search', ['search', 'autocomplete'], function (searchModule, autoc
$('#search-children').prop('checked', true); $('#search-children').prop('checked', true);
} }
if (formData.hasTags) {
formData.hasTags = Array.isArray(formData.hasTags) ? formData.hasTags : [formData.hasTags];
formData.hasTags.forEach(function (tag) {
$('#has-tags').tagsinput('add', tag);
});
}
if (formData.replies) { if (formData.replies) {
$('#reply-count').val(formData.replies); $('#reply-count').val(formData.replies);
$('#reply-count-filter').val(formData.repliesFilter); $('#reply-count-filter').val(formData.repliesFilter);
@ -157,6 +164,14 @@ define('forum/search', ['search', 'autocomplete'], function (searchModule, autoc
function enableAutoComplete() { function enableAutoComplete() {
autocomplete.user($('#posted-by-user')); autocomplete.user($('#posted-by-user'));
var tagEl = $('#has-tags');
tagEl.tagsinput({
confirmKeys: [13, 44],
trimValue: true
});
autocomplete.tag($('#has-tags').siblings('.bootstrap-tagsinput').find('input'));
} }
return Search; return Search;

@ -75,5 +75,40 @@ define('autocomplete', function () {
}); });
}; };
module.tag = function (input, onselect) {
app.loadJQueryUI(function () {
input.autocomplete({
delay: 100,
open: function () {
$(this).autocomplete('widget').css('z-index', 20000);
},
select: function (event, ui) {
onselect = onselect || function () {};
var e = jQuery.Event('keypress');
e.which = 13;
e.keyCode = 13;
setTimeout(function () {
input.trigger(e);
}, 100);
onselect(event, ui);
},
source: function (request, response) {
socket.emit('topics.autocompleteTags', {
query: request.term,
cid: ajaxify.data.cid || 0
}, function (err, tags) {
if (err) {
return app.alertError(err.message);
}
if (tags) {
response(tags);
}
$('.ui-autocomplete a').attr('data-ajaxify', 'false');
});
}
});
});
};
return module; return module;
}); });

@ -53,6 +53,10 @@ define('search', ['navigator', 'translator'], function (nav, translator) {
} }
} }
if (data.hasTags && data.hasTags.length) {
query.hasTags = data.hasTags;
}
if (parseInt(data.replies, 10) > 0) { if (parseInt(data.replies, 10) > 0) {
query.replies = data.replies; query.replies = data.replies;
query.repliesFilter = data.repliesFilter || 'atleast'; query.repliesFilter = data.repliesFilter || 'atleast';

@ -33,6 +33,7 @@ searchController.search = function (req, res, next) {
postedBy: req.query.by, postedBy: req.query.by,
categories: req.query.categories, categories: req.query.categories,
searchChildren: req.query.searchChildren, searchChildren: req.query.searchChildren,
hasTags: req.query.hasTags,
replies: req.query.replies, replies: req.query.replies,
repliesFilter: req.query.repliesFilter, repliesFilter: req.query.repliesFilter,
timeRange: req.query.timeRange, timeRange: req.query.timeRange,

@ -41,6 +41,7 @@ module.exports = function (Meta) {
next(true); next(true);
} }
} catch(e) { } catch(e) {
console.log(e);
process.stdout.write('[' + 'missing'.red + '] ' + module.bold + ' is a required dependency but could not be found\n'); process.stdout.write('[' + 'missing'.red + '] ' + module.bold + ' is a required dependency but could not be found\n');
depsMissing = true; depsMissing = true;
next(true); next(true);

@ -126,6 +126,7 @@ function filterAndSort(pids, data, callback) {
posts = filterByPostcount(posts, data.replies, data.repliesFilter); posts = filterByPostcount(posts, data.replies, data.repliesFilter);
posts = filterByTimerange(posts, data.timeRange, data.timeFilter); posts = filterByTimerange(posts, data.timeRange, data.timeFilter);
posts = filterByTags(posts, data.hasTags);
sortPosts(posts, data); sortPosts(posts, data);
@ -166,6 +167,7 @@ function getMatchedPosts(pids, data, callback) {
var keys = pids.map(function (pid) { var keys = pids.map(function (pid) {
return 'post:' + pid; return 'post:' + pid;
}); });
db.getObjectsFields(keys, postFields, next); db.getObjectsFields(keys, postFields, next);
}, },
function (_posts, next) { function (_posts, next) {
@ -185,7 +187,7 @@ function getMatchedPosts(pids, data, callback) {
} }
}, },
topics: function (next) { topics: function (next) {
var topics; var topicsData;
async.waterfall([ async.waterfall([
function (next) { function (next) {
var topicKeys = posts.map(function (post) { var topicKeys = posts.map(function (post) {
@ -194,12 +196,12 @@ function getMatchedPosts(pids, data, callback) {
db.getObjectsFields(topicKeys, topicFields, next); db.getObjectsFields(topicKeys, topicFields, next);
}, },
function (_topics, next) { function (_topics, next) {
topics = _topics; topicsData = _topics;
async.parallel({ async.parallel({
teasers: function (next) { teasers: function (next) {
if (topicFields.indexOf('teaserPid') !== -1) { if (topicFields.indexOf('teaserPid') !== -1) {
var teaserKeys = topics.map(function (topic) { var teaserKeys = topicsData.map(function (topic) {
return 'post:' + topic.teaserPid; return 'post:' + topic.teaserPid;
}); });
db.getObjectsFields(teaserKeys, ['timestamp'], next); db.getObjectsFields(teaserKeys, ['timestamp'], next);
@ -211,10 +213,20 @@ function getMatchedPosts(pids, data, callback) {
if (!categoryFields.length) { if (!categoryFields.length) {
return next(); return next();
} }
var cids = topics.map(function (topic) { var cids = topicsData.map(function (topic) {
return 'category:' + topic.cid; return 'category:' + topic.cid;
}); });
db.getObjectsFields(cids, categoryFields, next); db.getObjectsFields(cids, categoryFields, next);
},
tags: function (next) {
if (data.hasTags && data.hasTags.length) {
var tids = posts.map(function (post) {
return post && post.tid;
});
topics.getTopicsTags(tids, next);
} else {
setImmediate(next);
}
} }
}, next); }, next);
} }
@ -223,16 +235,19 @@ function getMatchedPosts(pids, data, callback) {
return next(err); return next(err);
} }
topics.forEach(function (topic, index) { topicsData.forEach(function (topic, index) {
if (topic && results.categories && results.categories[index]) { if (topic && results.categories && results.categories[index]) {
topic.category = results.categories[index]; topic.category = results.categories[index];
} }
if (topic && results.teasers && results.teasers[index]) { if (topic && results.teasers && results.teasers[index]) {
topic.teaser = results.teasers[index]; topic.teaser = results.teasers[index];
} }
if (topic && results.tags && results.tags[index]) {
topic.tags = results.tags[index];
}
}); });
next(null, topics); next(null, topicsData);
}); });
} }
}, next); }, next);
@ -297,6 +312,21 @@ function filterByTimerange(posts, timeRange, timeFilter) {
return posts; return posts;
} }
function filterByTags(posts, hasTags) {
if (hasTags && hasTags.length) {
posts = posts.filter(function (post) {
var hasAllTags = false;
if (post && post.topic && post.topic.tags && post.topic.tags.length) {
hasAllTags = hasTags.every(function (tag) {
return post.topic.tags.indexOf(tag) !== -1;
});
}
return hasAllTags;
});
}
return posts;
}
function sortPosts(posts, data) { function sortPosts(posts, data) {
if (!posts.length || !data.sortBy) { if (!posts.length || !data.sortBy) {
return; return;

@ -203,6 +203,13 @@ module.exports = function (Topics) {
db.getSetMembers('topic:' + tid + ':tags', callback); db.getSetMembers('topic:' + tid + ':tags', callback);
}; };
Topics.getTopicsTags = function (tids, callback) {
var keys = tids.map(function (tid) {
return 'topic:' + tid + ':tags';
});
db.getSetsMembers(keys, callback);
};
Topics.getTopicTagsObjects = function (tid, callback) { Topics.getTopicTagsObjects = function (tid, callback) {
Topics.getTopicsTagsObjects([tid], function (err, data) { Topics.getTopicsTagsObjects([tid], function (err, data) {
callback(err, Array.isArray(data) && data.length ? data[0] : []); callback(err, Array.isArray(data) && data.length ? data[0] : []);

@ -61,7 +61,7 @@ describe('Search', function () {
cid: cid1, cid: cid1,
title: 'nodebb mongodb bugs', title: 'nodebb mongodb bugs',
content: 'avocado cucumber apple orange fox', content: 'avocado cucumber apple orange fox',
tags: ['nodebb', 'bug', 'plugin', 'nodebb-plugin'] tags: ['nodebb', 'bug', 'plugin', 'nodebb-plugin', 'jquery']
}, next); }, next);
}, },
function (results, next) { function (results, next) {
@ -73,7 +73,7 @@ describe('Search', function () {
cid: cid2, cid: cid2,
title: 'java mongodb redis', title: 'java mongodb redis',
content: 'avocado cucumber carrot armadillo', content: 'avocado cucumber carrot armadillo',
tags: ['nodebb', 'bug', 'plugin', 'nodebb-plugin'] tags: ['nodebb', 'bug', 'plugin', 'nodebb-plugin', 'javascript']
}, next); }, next);
}, },
function (results, next) { function (results, next) {
@ -155,6 +155,18 @@ describe('Search', function () {
}); });
}); });
it('should search with tags filter', function (done) {
search.search({
query: 'mongodb',
searchIn: 'titles',
hasTags: ['nodebb', 'javascript']
}, function (err, data) {
assert.ifError(err);
assert.equal(data.posts[0].tid, topic2Data.tid);
done();
});
});
after(function (done) { after(function (done) {
db.emptydb(done); db.emptydb(done);
}); });

Loading…
Cancel
Save