Skip to content

Commit e9cbc79

Browse files
committed
Added support for Filter Builder
1 parent ffb554a commit e9cbc79

File tree

4 files changed

+135
-17
lines changed

4 files changed

+135
-17
lines changed

README.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -100,40 +100,40 @@ Another example - we need to add a ```/api/users``` route that on _GET_ method w
100100
--/echo
101101
----all.js
102102
--/users
103-
----get.js
103+
----post.js
104104
----post.js
105105
```
106106

107107
In other words, file path your API route method handler mirrors your URL path
108108

109109
| **API endpoint** | **Handler** |
110110
| -------------------------------- | --------------------------------- |
111-
| GET /api/echo | /api/echo/get.js |
112-
| GET /api/users | /api/users/get.js |
111+
| GET /api/echo | /api/echo/post.js |
112+
| GET /api/users | /api/users/post.js |
113113
| POST /api/users | /api/users/post.js |
114-
| GET /api/users/_:id_/tasks | /api/users/$id/tasks/get.js |
115-
| GET /api/users/_:id_/tasks/:task | /api/users/$id/tasks/$task/get.js |
114+
| GET /api/users/_:id_/tasks | /api/users/$id/tasks/post.js |
115+
| GET /api/users/_:id_/tasks/:task | /api/users/$id/tasks/$task/post.js |
116116

117117
### Parametrized path
118118

119119
Adding parameters to your URL point handling is pretty simple. Probably, you already noticed from the table above, when we require some parameter, we add its name with $ in front of it in our folder name. Just make sure you don't have duplicating parameters.
120120

121121
| **API endpoint** | **Handler** |
122122
| -------------------------------- | --------------------------------- |
123-
| GET /api/users/_:id_/tasks/:task | /api/users/$id/tasks/$task/get.js |
123+
| GET /api/users/_:id_/tasks/:task | /api/users/$id/tasks/$task/post.js |
124124

125125
```
126126
/foxx/
127127
--/users/
128-
----get.js
128+
----post.js
129129
----post.js
130130
----/$id/
131-
------get.js
131+
------post.js
132132
------/tasks/
133-
--------get.js
133+
--------post.js
134134
--------post.js
135135
--------/$task/
136-
----------get.js
136+
----------post.js
137137
```
138138

139139
More on path parameters you can read on [https://www.arangodb.com/docs/stable/foxx-getting-started.html#parameter-validation](https://www.arangodb.com/docs/stable/foxx-getting-started.html#parameter-validation).
@@ -214,7 +214,7 @@ Arguments used for context operations:
214214
**Using context utils**
215215

216216
```javascript
217-
//users/$id/get.js
217+
//users/$id/post.js
218218

219219
module.exports = {
220220
contentType: 'application/json',

builder/index.js

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* @version 1.3.20201023
55
* @author Skitsanos, info@skitsanos.com, https://github.com/skitsanos
66
*/
7-
const {db, query} = require('@arangodb');
7+
const {db, query, aql} = require('@arangodb');
88
const createRouter = require('@arangodb/foxx/router');
99
const fs = require('fs');
1010
const path = require('path');
@@ -15,6 +15,7 @@ const queue = queues.create('default');
1515
const tasks = require('@arangodb/tasks');
1616

1717
const crypto = require('@arangodb/crypto');
18+
const joi = require('joi');
1819

1920
const index = {
2021
foxxServicesLocation: path.join(module.context.basePath, '/foxx'),
@@ -158,7 +159,7 @@ const index = {
158159
module.context.appRoot = path.join(__dirname, '..');
159160

160161
//get record by id
161-
module.context.get = (store, docId) => query`return document(${db._collection(store)}, ${docId})`;
162+
module.context.get = (store, docId) => query`return unset(document(${db._collection(store)}, ${docId}), "_id", "_rev")`;
162163

163164
//insert new record
164165
module.context.insert = (store, doc) => query`INSERT ${{
@@ -180,11 +181,12 @@ const index = {
180181
{
181182
return null;
182183
}
183-
const exists = module.context.get(store, docId).toArray()[0];
184184

185-
return exists
185+
const [exists] = module.context.get(store, docId).toArray();
186+
187+
return Boolean(exists)
186188
? query`REMOVE ${docId} IN ${db._collection(store)} RETURN KEEP(OLD, "_key")`
187-
: null;
189+
: {toArray: () => []};
188190
};
189191

190192
module.context.runScript = (scriptName, data, opts) =>
@@ -244,6 +246,65 @@ const index = {
244246
isEmail(str)
245247
{
246248
return str.match(/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/);
249+
},
250+
251+
filterBuilder(q = [])
252+
{
253+
const filtersSchema = joi.array().required().items(joi.object({
254+
key: joi.string().required(),
255+
op: joi.string().valid('=', '~', '>', '<', '%').default('='),
256+
value: joi.any()
257+
}));
258+
259+
const validation = filtersSchema.validate(q);
260+
if(validation.error){
261+
throw validation.error;
262+
}
263+
264+
if (q.length === 0)
265+
{
266+
return aql.join([aql``], ' ');
267+
}
268+
269+
const parts = [
270+
aql` FILTER`
271+
];
272+
273+
for (let i = 0; i < q.length; i++)
274+
{
275+
const el = q[i];
276+
switch (el.op)
277+
{
278+
case '~':
279+
parts.push(aql` doc[${el.key}] != ${el.value}`);
280+
break;
281+
282+
case '>':
283+
parts.push(aql`(doc[${el.key}] > ${el.value})`);
284+
break;
285+
286+
case '<':
287+
parts.push(aql` doc[${el.key}] < ${el.value}`);
288+
break;
289+
290+
case '%':
291+
parts.push(aql`LIKE(doc[${el.key}], ${el.value}, true)`);
292+
break;
293+
294+
default:
295+
parts.push(aql`(doc[${el.key}] == ${el.value})`);
296+
break;
297+
}
298+
299+
//parts.push(output);
300+
301+
if (i < q.length - 1 && !Boolean(el.logic))
302+
{
303+
parts.push(aql`&&`);
304+
}
305+
}
306+
307+
return aql.join(parts, ' ');
247308
}
248309
};
249310

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/**
2+
* Exmaple on how to use FilterBuilder utility
3+
* ArangoDB Foxx Microservices Handler
4+
* @author skitsanos, https://github.com/skitsanos
5+
* @version 1.0.20201023
6+
*/
7+
8+
const joi = require('joi');
9+
const {query} = require('@arangodb');
10+
11+
/*
12+
Payload for POST request:
13+
14+
{
15+
"query": [
16+
{
17+
"key":"email",
18+
"op":"%",
19+
"value": "%skitsanos%"
20+
}
21+
]
22+
}
23+
*/
24+
25+
module.exports = {
26+
contentType: 'application/json',
27+
name: 'Examples - Complex Query via GET',
28+
body: {
29+
model: joi.object({
30+
query: joi.array().required()
31+
}).required()
32+
},
33+
handler: (req, res) =>
34+
{
35+
const {filterBuilder} = module.context.utils;
36+
37+
const {skip, pageSize} = req.queryParams;
38+
39+
const {query: queryPayload} = req.body;
40+
41+
const qb = filterBuilder(queryPayload);
42+
43+
console.log(qb);
44+
45+
const queryResult = query`
46+
LET skip=${skip ? Number(skip) : 0}
47+
LET pageSize=${pageSize ? Number(pageSize) : 25}
48+
49+
for doc in users
50+
${qb}
51+
limit skip,pageSize
52+
return doc
53+
`.toArray();
54+
55+
56+
res.send({result: queryResult});
57+
}
58+
};

tasks/ga.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ const task = ({clientId, path, headers, context}) =>
4545
params.geoid = headers['x-country'];
4646
}
4747

48-
console.log(params);
4948
request({
5049
method: 'get',
5150
url: 'https://www.google-analytics.com/collect',

0 commit comments

Comments
 (0)