1
0
Fork 0

Merge pull request #1145 from triAGENS/positional-query-args

Support positional arguments in Foxx queries
This commit is contained in:
Frank Celler 2014-12-03 14:48:39 +01:00
commit 3e430276d6
3 changed files with 92 additions and 18 deletions

View File

@ -27,12 +27,13 @@ console.log('usernames:', usernames);
Creates a query function that performs the given query and returns the result.
The returned query function optionally takes an object as its argument. If an object is provided, its properties will be used as the query's bind parameters. Note that collection bind parameters need to be prefixed with an at-sign, e.g. `{'@myCollectionVar': 'my_collection_name'}`.
The returned query function optionally takes an object as its argument. If an object is provided, its properties will be used as the query's bind parameters. Any additional arguments will be passed to the transform function (or dropped if no transform function is defined).
*Parameter*
* *cfg*: an object with the following properties:
* *query*: an AQL query string or an ArangoDB Query Builder query object.
* *params* (optional): an array of parameter names.
* *context* (optional): an *applicationContext*.
* *model* (optional): a *Foxx.Model* that will be applied to the query results.
* *defaults* (optional): default values for the query's bind parameters. These can be overridden by passing a value for the same name to the query function.
@ -42,7 +43,9 @@ If *cfg* is a string, it will be used as the value of *cfg.query* instead.
If a *context* is specified, the values of all collection bind parameters will be passed through the context's *collectionName* method.
Note that collection bind parameters in AQL need to be referenced with two at-signs instead of one, e.g. `@@myCollectionVar`.
Note that collection bind parameters in AQL need to be referenced with two at-signs instead of one, e.g. `@@myCollectionVar` and their parameter name needs to be prefixed with an at-sign as well, e.g. `{'@myCollectionVar': 'collection_name'}`.
If *params* is provided, the query function will accept positional arguments instead of an object. If *params* is a string, it will be treated as an array containing that string.
If both *model* and *transform* are provided, the *transform* function will be applied to the result array _after_ the results have been converted into model instances. The *transform* function is always passed the entire result array and its return value will be returned by the query function.
@ -62,6 +65,16 @@ var query = Foxx.createQuery('FOR u IN _users RETURN u[@propName]');
var usernames = query({propName: 'user'});
```
Using named bind parameters:
```js
var query = Foxx.createQuery({
query: 'FOR u IN _users RETURN u[@propName]',
params: ['propName']
);
var usernames = query('user');
```
Using models:
```js
@ -93,3 +106,44 @@ var query = Foxx.createQuery({
});
var user = query(); // first user by username
```
Using a transformation with extra arguments:
```js
var query = Foxx.createQuery({
query: 'FOR u IN _users SORT u.user ASC RETURN u[@propName]',
transform: function (results, uppercase) {
return uppercase ? results[0].toUpperCase() : results[0].toLowerCase();
}
});
query({propName: 'user'}, true); // username of first user in uppercase
query({propName: 'user'}, false); // username of first user in lowercase
```
Using a transformation with extra arguments (using positional arguments):
```js
var query = Foxx.createQuery({
query: 'FOR u IN _users SORT u.user ASC RETURN u[@propName]',
params: ['propName'],
transform: function (results, uppercase) {
return uppercase ? results[0].toUpperCase() : results[0].toLowerCase();
}
});
query('user', true); // username of first user in uppercase
query('user', false); // username of first user in lowercase
```
Using a transformation with extra arguments (and no query parameters):
```js
var query = Foxx.createQuery({
query: 'FOR u IN _users SORT u.user ASC RETURN u.user',
params: false, // an empty array would work, too
transform: function (results, uppercase) {
return uppercase ? results[0].toUpperCase() : results[0].toLowerCase();
}
});
query(true); // username of first user in uppercase
query(false); // username of first user in lowercase
```

View File

@ -25,7 +25,7 @@ For more details see the chapter on [Foxx Queries](../Foxx/FoxxQueries.md).
Making a simple query in the repository and using it from the controller:
```javascript
```js
// in the repository
var Foxx = require("org/arangodb/foxx");
@ -43,29 +43,31 @@ ctrl.get("/", function(req, res) {
It is also possible to supply parameters to the query:
```javascript
```js
// in the repository
getPendingItemById: Foxx.createQuery(
'FOR todo IN my_todos FILTER todo.completed == false FILTER todo._key == @id RETURN todo'
)
getPendingItemById: Foxx.createQuery({
query: 'FOR todo IN my_todos FILTER todo.completed == false FILTER todo._key == @id RETURN todo',
params: ['id']
})
// in the controller
ctrl.get("/:id", function(req, res) {
var id = req.params("id");
var rv = todosRepository.getPendingItemById({ id: id });
var rv = todosRepository.getPendingItemById(id);
res.json(rv);
});
```
The list of results can also be transformed before returning it from the repository:
```javascript
```js
// in the repository
getPendingItemById: Foxx.createQuery({
query: 'FOR todo IN my_todos FILTER todo.completed == false FILTER todo._key == @id RETURN todo',
transform: function(results, args) {
params: ['id'],
transform: function(results, extra) {
for (var i = 0; i < results.length; i++) {
results[i].extraProperty = args.extra;
results[i].extraProperty = extra;
}
}
})
@ -74,10 +76,7 @@ getPendingItemById: Foxx.createQuery({
ctrl.get("/:id", function(req, res) {
var id = req.params("id");
var extra = req.params("extra");
var rv = todosRepository.getPendingItemById(
{ id: id },
{ extra: extra }
);
var rv = todosRepository.getPendingItemById(id, extra);
res.json(rv);
});
```

View File

@ -40,11 +40,22 @@ exports.createQuery = function createQuery (cfg) {
}
var query = cfg.query,
params = cfg.params,
context = cfg.context,
Model = cfg.model,
defaults = cfg.defaults,
transform = cfg.transform;
if (params === false) {
params = [];
} else if (params && !Array.isArray(params)) {
params = [params];
}
if (params && !params.each(function (v) {return typeof v === 'string';})) {
throw new Error('Argument names must be a string, an array of strings or false.');
}
if (!query || (typeof query !== 'string' && typeof query.toAQL !== 'function')) {
throw new Error('Expected query to be a string or a QueryBuilder instance.');
}
@ -61,7 +72,17 @@ exports.createQuery = function createQuery (cfg) {
throw new Error('Expected transform to be a function.');
}
return function query(vars, trArgs) {
return function query() {
var args = Array.prototype.slice.call(arguments);
var vars;
if (params) {
vars = {};
params.forEach(function (name) {
vars[name] = args.shift();
});
} else {
vars = args.shift();
}
vars = _.extend({}, defaults, vars);
if (context) {
_.each(vars, function (value, key) {
@ -76,7 +97,7 @@ exports.createQuery = function createQuery (cfg) {
return new Model(data);
});
}
return transform ? transform(result, trArgs) : result;
args.unshift(result);
return transform ? transform.apply(null, args) : result;
};
};