mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-02 03:55:55 +01:00
ability to filter search by tags
This commit is contained in:
@@ -13,8 +13,8 @@
|
||||
"start": "node loader.js",
|
||||
"lint": "eslint --cache .",
|
||||
"pretest": "npm run lint",
|
||||
"test": "istanbul cover node_modules/mocha/bin/_mocha -- -R spec",
|
||||
"coveralls": "istanbul cover _mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage"
|
||||
"test": "istanbul cover node_modules/mocha/bin/_mocha -- -R dot",
|
||||
"coveralls": "istanbul cover _mocha --report lcovonly -- -R dot && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"async": "~1.5.0",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"posted-by": "Posted by",
|
||||
"in-categories": "In Categories",
|
||||
"search-child-categories": "Search child categories",
|
||||
"has-tags": "Has tags",
|
||||
"reply-count": "Reply Count",
|
||||
"at-least": "At least",
|
||||
"at-most": "At most",
|
||||
|
||||
@@ -12,8 +12,6 @@ define('forum/search', ['search', 'autocomplete'], function (searchModule, autoc
|
||||
|
||||
var searchIn = $('#search-in');
|
||||
|
||||
fillOutForm();
|
||||
|
||||
searchIn.on('change', function () {
|
||||
updateFormItemVisiblity(searchIn.val());
|
||||
});
|
||||
@@ -31,6 +29,8 @@ define('forum/search', ['search', 'autocomplete'], function (searchModule, autoc
|
||||
handleSavePreferences();
|
||||
|
||||
enableAutoComplete();
|
||||
|
||||
fillOutForm();
|
||||
};
|
||||
|
||||
function getSearchData() {
|
||||
@@ -43,6 +43,7 @@ define('forum/search', ['search', 'autocomplete'], function (searchModule, autoc
|
||||
searchData.by = form.find('#posted-by-user').val();
|
||||
searchData.categories = form.find('#posted-in-categories').val();
|
||||
searchData.searchChildren = form.find('#search-children').is(':checked');
|
||||
searchData.hasTags = form.find('#has-tags').tagsinput('items');
|
||||
searchData.replies = form.find('#reply-count').val();
|
||||
searchData.repliesFilter = form.find('#reply-count-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);
|
||||
}
|
||||
|
||||
|
||||
if (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);
|
||||
}
|
||||
|
||||
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) {
|
||||
$('#reply-count').val(formData.replies);
|
||||
$('#reply-count-filter').val(formData.repliesFilter);
|
||||
@@ -157,6 +164,14 @@ define('forum/search', ['search', 'autocomplete'], function (searchModule, autoc
|
||||
|
||||
function enableAutoComplete() {
|
||||
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;
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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) {
|
||||
query.replies = data.replies;
|
||||
query.repliesFilter = data.repliesFilter || 'atleast';
|
||||
|
||||
@@ -33,6 +33,7 @@ searchController.search = function (req, res, next) {
|
||||
postedBy: req.query.by,
|
||||
categories: req.query.categories,
|
||||
searchChildren: req.query.searchChildren,
|
||||
hasTags: req.query.hasTags,
|
||||
replies: req.query.replies,
|
||||
repliesFilter: req.query.repliesFilter,
|
||||
timeRange: req.query.timeRange,
|
||||
|
||||
@@ -41,6 +41,7 @@ module.exports = function (Meta) {
|
||||
next(true);
|
||||
}
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
process.stdout.write('[' + 'missing'.red + '] ' + module.bold + ' is a required dependency but could not be found\n');
|
||||
depsMissing = true;
|
||||
next(true);
|
||||
|
||||
@@ -126,6 +126,7 @@ function filterAndSort(pids, data, callback) {
|
||||
|
||||
posts = filterByPostcount(posts, data.replies, data.repliesFilter);
|
||||
posts = filterByTimerange(posts, data.timeRange, data.timeFilter);
|
||||
posts = filterByTags(posts, data.hasTags);
|
||||
|
||||
sortPosts(posts, data);
|
||||
|
||||
@@ -166,6 +167,7 @@ function getMatchedPosts(pids, data, callback) {
|
||||
var keys = pids.map(function (pid) {
|
||||
return 'post:' + pid;
|
||||
});
|
||||
|
||||
db.getObjectsFields(keys, postFields, next);
|
||||
},
|
||||
function (_posts, next) {
|
||||
@@ -185,7 +187,7 @@ function getMatchedPosts(pids, data, callback) {
|
||||
}
|
||||
},
|
||||
topics: function (next) {
|
||||
var topics;
|
||||
var topicsData;
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
var topicKeys = posts.map(function (post) {
|
||||
@@ -194,12 +196,12 @@ function getMatchedPosts(pids, data, callback) {
|
||||
db.getObjectsFields(topicKeys, topicFields, next);
|
||||
},
|
||||
function (_topics, next) {
|
||||
topics = _topics;
|
||||
topicsData = _topics;
|
||||
|
||||
async.parallel({
|
||||
teasers: function (next) {
|
||||
if (topicFields.indexOf('teaserPid') !== -1) {
|
||||
var teaserKeys = topics.map(function (topic) {
|
||||
var teaserKeys = topicsData.map(function (topic) {
|
||||
return 'post:' + topic.teaserPid;
|
||||
});
|
||||
db.getObjectsFields(teaserKeys, ['timestamp'], next);
|
||||
@@ -211,10 +213,20 @@ function getMatchedPosts(pids, data, callback) {
|
||||
if (!categoryFields.length) {
|
||||
return next();
|
||||
}
|
||||
var cids = topics.map(function (topic) {
|
||||
var cids = topicsData.map(function (topic) {
|
||||
return 'category:' + topic.cid;
|
||||
});
|
||||
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);
|
||||
}
|
||||
@@ -223,16 +235,19 @@ function getMatchedPosts(pids, data, callback) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
topics.forEach(function (topic, index) {
|
||||
topicsData.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];
|
||||
}
|
||||
if (topic && results.tags && results.tags[index]) {
|
||||
topic.tags = results.tags[index];
|
||||
}
|
||||
});
|
||||
|
||||
next(null, topics);
|
||||
next(null, topicsData);
|
||||
});
|
||||
}
|
||||
}, next);
|
||||
@@ -297,6 +312,21 @@ function filterByTimerange(posts, timeRange, timeFilter) {
|
||||
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) {
|
||||
if (!posts.length || !data.sortBy) {
|
||||
return;
|
||||
|
||||
@@ -203,6 +203,13 @@ module.exports = function (Topics) {
|
||||
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.getTopicsTagsObjects([tid], function (err, data) {
|
||||
callback(err, Array.isArray(data) && data.length ? data[0] : []);
|
||||
|
||||
@@ -61,7 +61,7 @@ describe('Search', function () {
|
||||
cid: cid1,
|
||||
title: 'nodebb mongodb bugs',
|
||||
content: 'avocado cucumber apple orange fox',
|
||||
tags: ['nodebb', 'bug', 'plugin', 'nodebb-plugin']
|
||||
tags: ['nodebb', 'bug', 'plugin', 'nodebb-plugin', 'jquery']
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
@@ -73,7 +73,7 @@ describe('Search', function () {
|
||||
cid: cid2,
|
||||
title: 'java mongodb redis',
|
||||
content: 'avocado cucumber carrot armadillo',
|
||||
tags: ['nodebb', 'bug', 'plugin', 'nodebb-plugin']
|
||||
tags: ['nodebb', 'bug', 'plugin', 'nodebb-plugin', 'javascript']
|
||||
}, 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) {
|
||||
db.emptydb(done);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user