Skip to content

Commit d945563

Browse files
committed
feat: implement backend-site query filtering
1 parent 11617bf commit d945563

File tree

4 files changed

+124
-92
lines changed

4 files changed

+124
-92
lines changed

app/controllers/collection.js

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ var express = require("express"),
66
auth = require("./../../config/middlewares/auth"),
77
mongoose = require("mongoose"),
88
Collection = mongoose.model("Collection"),
9-
loadCollection = require("./utils").loadCollection,
9+
fetchLinks = require("./utils").fetchLinks,
1010
countLinks = require("./utils").countLinks;
1111

1212
var DEFAULT_PAGE_SIZE = 25;
@@ -103,14 +103,11 @@ module.exports = function (app, passport) {
103103
var _id = req.params.id;
104104
if (!_id) return res.makeError(404, "Not found. Please give an id.", err);
105105

106-
loadCollection({ _id: _id, owner: req.user._id }, true)
107-
.then(function (result) {
108-
if (!result) return res.makeError(404, "Collection not found or unauthorized.");
109-
res.send(result.toObject());
110-
})
111-
.catch(function (err) {
112-
res.makeError(500, r1.reason.message, r1.reason);
113-
});
106+
Collection.findOne({ _id: id, owner: req.user._id }, { links: 0 }, function(err, obj) {
107+
if (err) return res.makeError(500, err.message, err);
108+
if (!obj) return res.makeError(404, "Collection not found or unauthorized.");
109+
res.send(obj.toObject());
110+
});
114111
});
115112

116113
/**
@@ -126,6 +123,7 @@ module.exports = function (app, passport) {
126123
* - $ref: '#/parameters/collectionId'
127124
* - $ref: '#/parameters/page'
128125
* - $ref: '#/parameters/pageSize'
126+
* - $ref: '#/parameters/q'
129127
* produces:
130128
* - application/json
131129
* responses:
@@ -142,20 +140,22 @@ module.exports = function (app, passport) {
142140
var pageSize = Math.max(req.query.pageSize, 0) || DEFAULT_PAGE_SIZE;
143141

144142
Promise.allSettled([
145-
loadCollection({ _id: _id, owner: req.user._id }, false, pageSize, page),
146-
countLinks(_id),
143+
fetchLinks({ _id: _id, owner: req.user._id }, req.query.q, pageSize, page),
144+
countLinks(_id, req.query.q),
147145
]).then(function (results) {
148146
var r1 = results[0];
149147
var r2 = results[1];
150148

151149
if (r1.status === "rejected") return res.makeError(500, r1.reason.message, r1.reason);
152150
if (!r1.value) return res.makeError(404, "Collection not found or unauthorized.");
153151

154-
var obj = r1.value.toObject();
152+
var links = r1.value;
155153
if (r2.status === "fulfilled") {
156154
res.set("Link", "<?pageSize=" + pageSize + "&page=" + Math.ceil(r2.value / pageSize) + '>; rel="last"');
157155
}
158-
res.send(obj.links);
156+
res.send(links);
157+
}).catch(function(e) {
158+
return res.makeError(500, e);
159159
});
160160
});
161161

app/controllers/shared.js

Lines changed: 49 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ var express = require("express"),
44
_ = require("underscore"),
55
mongoose = require("mongoose"),
66
Collection = mongoose.model("Collection"),
7-
loadCollection = require("./utils").loadCollection,
7+
fetchLinks = require("./utils").fetchLinks,
88
countLinks = require("./utils").countLinks;
99

1010
module.exports = function (app) {
@@ -33,60 +33,58 @@ module.exports = function (app) {
3333
var _id = req.params.id;
3434
if (!_id) return res.makeError(404, "Not found. Please give an id.", err);
3535

36-
loadCollection({ _id: _id, shared: true }, true)
37-
.then(function (result) {
38-
if (!result)
39-
return res.makeError(404, "Collection not found or unauthorized.");
40-
res.send(result.toObject());
41-
})
42-
.catch(function (err) {
43-
res.makeError(500, r1.reason.message, r1.reason);
44-
});
36+
Collection.findOne({ _id: _id, shared: 1 }, { links: 0 }, function(err, obj) {
37+
if (err) return res.makeError(500, err.message, err);
38+
if (!obj) return res.makeError(404, "Collection not found or unauthorized.");
39+
res.send(obj.toObject());
40+
});
4541
});
46-
};
4742

48-
/**
49-
* @swagger
50-
* /shared/{id}/links:
51-
* get:
52-
* summary: Get links contained in a collection
53-
* tags:
54-
* - collection
55-
* security:
56-
* - ApiKeyAuth: []
57-
* parameters:
58-
* - $ref: '#/parameters/collectionId'
59-
* - $ref: '#/parameters/page'
60-
* - $ref: '#/parameters/pageSize'
61-
* produces:
62-
* - application/json
63-
* responses:
64-
* 200:
65-
* description: List of links in the collection
66-
* schema:
67-
* $ref: '#/definitions/LinkList'
68-
*/
69-
router.get("/:id/links", function (req, res) {
70-
var _id = req.params.id;
71-
if (!_id) return res.makeError(404, "Not found. Please give an id.");
43+
/**
44+
* @swagger
45+
* /shared/{id}/links:
46+
* get:
47+
* summary: Get links contained in a collection
48+
* tags:
49+
* - collection
50+
* security:
51+
* - ApiKeyAuth: []
52+
* parameters:
53+
* - $ref: '#/parameters/collectionId'
54+
* - $ref: '#/parameters/page'
55+
* - $ref: '#/parameters/pageSize'
56+
* produces:
57+
* - application/json
58+
* responses:
59+
* 200:
60+
* description: List of links in the collection
61+
* schema:
62+
* $ref: '#/definitions/LinkList'
63+
*/
64+
router.get("/:id/links", function (req, res) {
65+
var _id = req.params.id;
66+
if (!_id) return res.makeError(404, "Not found. Please give an id.");
7267

73-
var page = Math.max(req.query.page, 1);
74-
var pageSize = Math.max(req.query.pageSize, 0) || DEFAULT_PAGE_SIZE;
68+
var page = Math.max(req.query.page, 1);
69+
var pageSize = Math.max(req.query.pageSize, 0) || DEFAULT_PAGE_SIZE;
7570

76-
Promise.allSettled([
77-
loadCollection({ _id: _id, shared: true }, false, pageSize, page),
78-
countLinks(_id),
79-
]).then(function (results) {
80-
var r1 = results[0];
81-
var r2 = results[1];
71+
Promise.allSettled([
72+
fetchLinks({ _id: _id, shared: true }, req.query.q, pageSize, page),
73+
countLinks(_id, req.query.q),
74+
]).then(function (results) {
75+
var r1 = results[0];
76+
var r2 = results[1];
8277

83-
if (r1.status === "rejected") return res.makeError(500, r1.reason.message, r1.reason);
84-
if (!r1.value) return res.makeError(404, "Collection not found or unauthorized.");
78+
if (r1.status === "rejected") return res.makeError(500, r1.reason.message, r1.reason);
79+
if (!r1.value) return res.makeError(404, "Collection not found or unauthorized.");
8580

86-
var obj = r1.value.toObject();
87-
if (r2.status === "fulfilled") {
88-
res.set("Link", "<?pageSize=" + pageSize + "&page=" + Math.ceil(r2.value / pageSize) + '>; rel="last"');
89-
}
90-
res.send(obj.links);
81+
var links = r1.value;
82+
if (r2.status === "fulfilled") {
83+
res.set("Link", "<?pageSize=" + pageSize + "&page=" + Math.ceil(r2.value / pageSize) + '>; rel="last"');
84+
}
85+
res.send(links);
86+
}).catch(function(e) {
87+
return res.makeError(500, e);
88+
});
9189
});
92-
});
90+
};

app/controllers/utils/index.js

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,68 @@
11
var mongoose = require('mongoose'),
2-
Collection = mongoose.model('Collection');
2+
Collection = mongoose.model('Collection'),
3+
_ = require('underscore');
4+
5+
function fetchLinks(filter, q, pageSize, page, cb) {
6+
if (!cb) cb = function() {};
7+
8+
var skip = (page - 1) * pageSize;
9+
var regex = new RegExp('.*' + q + '.*', 'i');
10+
var filters = _.clone(filter);
11+
12+
// https://coderedirect.com/questions/302814/regex-as-filter-in-projection
13+
14+
var projection = {
15+
links: {
16+
$slice: ['$links', skip, pageSize]
17+
},
18+
_id: 0
19+
};
20+
21+
if (q) {
22+
filters.$or = [
23+
{ 'links.description': regex },
24+
{ 'links.url': regex }
25+
];
26+
};
27+
328

4-
function loadCollection(filter, excludeLinks, pageSize, page, cb) {
5-
if (!cb) cb = function() {}
629
return new Promise(function(resolve, reject) {
7-
var skip = (page - 1) * pageSize;
8-
9-
Collection.findOne(filter, {
10-
__v: false,
11-
created: false,
12-
modified: false,
13-
links: excludeLinks ? false : { $slice: [skip, pageSize] }
14-
}, function(err, obj) {
15-
if (err) {
16-
cb(err, obj);
30+
Collection.aggregate([
31+
{ $unwind: '$links' },
32+
{ $match: filters },
33+
{ $group: { _id: null, links: { $push: '$links' } } },
34+
{ $project: projection }
35+
], function(err, obj) {
36+
if (err || !obj) {
37+
cb(err, null);
1738
return reject(err);
1839
}
19-
cb(err, obj);
20-
return resolve(obj);
21-
});
40+
var links = obj.length ? obj[0].links : []
41+
cb(err, links);
42+
return resolve(links);
43+
})
2244
});
2345
};
2446

25-
function countLinks(id, cb) {
47+
function countLinks(id, q, cb) {
2648
if (!cb) cb = function() {}
49+
50+
var regex = new RegExp('.*' + q + '.*', 'i');
51+
var filters = { _id: id };
52+
53+
if (q) {
54+
filters.$or = [
55+
{ 'links.description': regex },
56+
{ 'links.url': regex }
57+
];
58+
};
59+
2760
return new Promise(function (resolve, reject) {
2861
Collection.aggregate([
29-
{
30-
$match: {
31-
_id: id
32-
}
33-
},
34-
{
35-
$project: {
36-
totalLinks: { $size: '$links' }
37-
}
38-
}
62+
{ $unwind: '$links' },
63+
{ $match: filters },
64+
{ $group: { _id: null, links: { $push: '$links' } } },
65+
{ $project: { totalLinks: { $size: '$links' } } }
3966
], function (err, obj) {
4067
if (err || !obj || !obj.length) {
4168
var errMessage = err || 'failed to count links';
@@ -49,6 +76,6 @@ function countLinks(id, cb) {
4976
}
5077

5178
module.exports = {
52-
loadCollection: loadCollection,
79+
fetchLinks: fetchLinks,
5380
countLinks: countLinks
5481
}

config/swagger/parameters.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ parameters:
1313
require: false
1414
description: Number of items per page
1515

16+
q:
17+
in: query
18+
name: q
19+
type: string
20+
require: false
21+
description: Query string to filter by
22+
1623
userSignup:
1724
in: body
1825
name: user

0 commit comments

Comments
 (0)