Skip to content

Commit fd3003c

Browse files
author
Alan Plum
committed
Change aql.join/literal to not be methods
1 parent 072cef7 commit fd3003c

File tree

3 files changed

+167
-148
lines changed

3 files changed

+167
-148
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ This driver uses semantic versioning:
132132
console.log(first.index, first.squared); // 1 1
133133
```
134134

135+
- Moved `aql.literal` and `aql.join` into `aql` module
136+
137+
Previously these were available as methods on the `aql` function. Now they
138+
need to be imported from the `aql` module.
139+
135140
- Changed return values of `db.getUserAccessLevel` and `db.getUserDatabases`
136141
to match documented return types
137142

@@ -248,6 +253,11 @@ This driver uses semantic versioning:
248253

249254
- Added new ArangoDB 3.10 `db.queryRules` method
250255

256+
- Added support for `Analyzer` in `aql` templates
257+
258+
`Analyzer` objects can now be passed into `aql` templates like `View` and
259+
`ArangoCollection` objects.
260+
251261
## [7.8.0] - 2022-05-19
252262

253263
### Added

MIGRATING.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,22 @@ method:
5454
+const db2 = db.database("database2");
5555
```
5656

57+
### Queries
58+
59+
The functions `aql.literal` and `aql.join` are no longer available as methods
60+
on the `aql` template handler and need to be imported separately:
61+
62+
```diff
63+
import { aql } from "arangojs";
64+
+import { join } from "arangojs/aql";
65+
66+
-const filters = aql.join([
67+
+const filters = join([
68+
aql`FILTER size == 'big'`,
69+
aql`FILTER color == 'yellow'`
70+
]);
71+
```
72+
5773
### Users
5874

5975
The return values of `db.getUserDatabases` and `db.getUserAccessLevel` have

src/aql.ts

Lines changed: 141 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
*
1111
* @packageDocumentation
1212
*/
13+
import { isArangoAnalyzer } from "./analyzer";
1314
import { ArangoCollection, isArangoCollection } from "./collection";
1415
import { Graph, isArangoGraph } from "./graph";
1516
import { isArangoView, View } from "./view";
@@ -51,7 +52,7 @@ export interface GeneratedAqlQuery extends AqlQuery {
5152
* when used in an AQL template or passed to AQL helper functions.
5253
*
5354
* Arbitrary values can be converted to trusted AQL literals by passing them
54-
* to the {@link aql.literal} helper function.
55+
* to the {@link literal} helper function.
5556
*/
5657
export interface AqlLiteral {
5758
/**
@@ -244,7 +245,8 @@ export function aql(
244245
if (
245246
isArangoCollection(rawValue) ||
246247
isArangoGraph(rawValue) ||
247-
isArangoView(rawValue)
248+
isArangoView(rawValue) ||
249+
isArangoAnalyzer(rawValue)
248250
) {
249251
name = `@${name}`;
250252
value = rawValue.name;
@@ -262,153 +264,144 @@ export function aql(
262264
};
263265
}
264266

265-
// eslint-disable-next-line @typescript-eslint/no-namespace
266-
export namespace aql {
267-
/**
268-
* Marks an arbitrary scalar value (i.e. a string, number or boolean) as
269-
* safe for being inlined directly into AQL queries when used in an `aql`
270-
* template string, rather than being converted into a bind parameter.
271-
*
272-
* **Note**: Nesting `aql` template strings is a much safer alternative for
273-
* most use cases. This low-level helper function only exists to help with
274-
* rare edge cases where a trusted AQL query fragment must be read from a
275-
* string (e.g. when reading query fragments from JSON) and should only be
276-
* used as a last resort.
277-
*
278-
* @example
279-
* ```js
280-
* // BAD! DO NOT DO THIS!
281-
* const sortDirection = aql.literal('ASC');
282-
*
283-
* // GOOD! DO THIS INSTEAD!
284-
* const sortDirection = aql`ASC`;
285-
* ```
286-
*
287-
* @example
288-
* ```js
289-
* // BAD! DO NOT DO THIS!
290-
* const filterColor = aql.literal('FILTER d.color == "green"');
291-
* const result = await db.query(aql`
292-
* FOR d IN some-collection
293-
* ${filterColor}
294-
* RETURN d
295-
* `);
296-
*
297-
* // GOOD! DO THIS INSTEAD!
298-
* const color = "green";
299-
* const filterColor = aql`FILTER d.color === ${color}`;
300-
* const result = await db.query(aql`
301-
* FOR d IN some-collection
302-
* ${filterColor}
303-
* RETURN d
304-
* `);
305-
* ```
306-
*
307-
* @example
308-
* ```js
309-
* // WARNING: We explicitly trust the environment variable to be safe!
310-
* const filter = aql.literal(process.env.FILTER_STATEMENT);
311-
* const users = await db.query(aql`
312-
* FOR user IN users
313-
* ${filter}
314-
* RETURN user
315-
* `);
316-
* ```
317-
*/
318-
export function literal(
319-
value: string | number | boolean | AqlLiteral | null | undefined
320-
): AqlLiteral {
321-
if (isAqlLiteral(value)) {
322-
return value;
323-
}
324-
return {
325-
toAQL() {
326-
if (value === undefined) {
327-
return "";
328-
}
329-
return String(value);
330-
},
331-
};
267+
/**
268+
* Marks an arbitrary scalar value (i.e. a string, number or boolean) as
269+
* safe for being inlined directly into AQL queries when used in an `aql`
270+
* template string, rather than being converted into a bind parameter.
271+
*
272+
* **Note**: Nesting `aql` template strings is a much safer alternative for
273+
* most use cases. This low-level helper function only exists to help with
274+
* rare edge cases where a trusted AQL query fragment must be read from a
275+
* string (e.g. when reading query fragments from JSON) and should only be
276+
* used as a last resort.
277+
*
278+
* @example
279+
* ```js
280+
* // BAD! DO NOT DO THIS!
281+
* const sortDirection = literal('ASC');
282+
*
283+
* // GOOD! DO THIS INSTEAD!
284+
* const sortDirection = aql`ASC`;
285+
* ```
286+
*
287+
* @example
288+
* ```js
289+
* // BAD! DO NOT DO THIS!
290+
* const filterColor = literal('FILTER d.color == "green"');
291+
* const result = await db.query(aql`
292+
* FOR d IN some-collection
293+
* ${filterColor}
294+
* RETURN d
295+
* `);
296+
*
297+
* // GOOD! DO THIS INSTEAD!
298+
* const color = "green";
299+
* const filterColor = aql`FILTER d.color === ${color}`;
300+
* const result = await db.query(aql`
301+
* FOR d IN some-collection
302+
* ${filterColor}
303+
* RETURN d
304+
* `);
305+
* ```
306+
*
307+
* @example
308+
* ```js
309+
* // WARNING: We explicitly trust the environment variable to be safe!
310+
* const filter = literal(process.env.FILTER_STATEMENT);
311+
* const users = await db.query(aql`
312+
* FOR user IN users
313+
* ${filter}
314+
* RETURN user
315+
* `);
316+
* ```
317+
*/
318+
export function literal(
319+
value: string | number | boolean | AqlLiteral | null | undefined
320+
): AqlLiteral {
321+
if (isAqlLiteral(value)) {
322+
return value;
332323
}
324+
return {
325+
toAQL() {
326+
if (value === undefined) {
327+
return "";
328+
}
329+
return String(value);
330+
},
331+
};
332+
}
333333

334-
/**
335-
* Constructs {@link AqlQuery} objects from an array of arbitrary values.
336-
*
337-
* **Note**: Nesting `aql` template strings is a much safer alternative
338-
* for most use cases. This low-level helper function only exists to
339-
* complement the `aql` tag when constructing complex queries from dynamic
340-
* arrays of query fragments.
341-
*
342-
* @param values - Array of values to join. These values will behave exactly
343-
* like values interpolated in an `aql` template string.
344-
* @param sep - Seperator to insert between values. This value will behave
345-
* exactly like a value passed to {@link literal}, i.e. it will be
346-
* inlined as-is, rather than being converted into a bind parameter.
347-
*
348-
* @example
349-
* ```js
350-
* const users = db.collection("users");
351-
* const filters = [];
352-
* if (adminsOnly) filters.push(aql`FILTER user.admin`);
353-
* if (activeOnly) filters.push(aql`FILTER user.active`);
354-
* const result = await db.query(aql`
355-
* FOR user IN ${users}
356-
* ${aql.join(filters)}
357-
* RETURN user
358-
* `);
359-
* ```
360-
*
361-
* @example
362-
* ```js
363-
* const users = db.collection("users");
364-
* const keys = ["jreyes", "ghermann"];
365-
*
366-
* // BAD! NEEDLESSLY COMPLEX!
367-
* const docs = keys.map(key => aql`DOCUMENT(${users}, ${key}`));
368-
* const result = await db.query(aql`
369-
* FOR user IN [
370-
* ${aql.join(docs, ", ")}
371-
* ]
372-
* RETURN user
373-
* `);
374-
* // Query:
375-
* // FOR user IN [
376-
* // DOCUMENT(@@value0, @value1), DOCUMENT(@@value0, @value2)
377-
* // ]
378-
* // RETURN user
379-
* // Bind parameters:
380-
* // @value0 -> "users"
381-
* // value1 -> "jreyes"
382-
* // value2 -> "ghermann"
383-
*
384-
* // GOOD! MUCH SIMPLER!
385-
* const result = await db.query(aql`
386-
* FOR key IN ${keys}
387-
* LET user = DOCUMENT(${users}, key)
388-
* RETURN user
389-
* `);
390-
* // Query:
391-
* // FOR user IN @value0
392-
* // LET user = DOCUMENT(@@value1, key)
393-
* // RETURN user
394-
* // Bind parameters:
395-
* // value0 -> ["jreyes", "ghermann"]
396-
* // @value1 -> "users"
397-
* ```
398-
*/
399-
export function join(
400-
values: AqlValue[],
401-
sep: string = " "
402-
): GeneratedAqlQuery {
403-
if (!values.length) {
404-
return aql``;
405-
}
406-
if (values.length === 1) {
407-
return aql`${values[0]}`;
408-
}
409-
return aql(
410-
["", ...Array(values.length - 1).fill(sep), ""] as any,
411-
...values
412-
);
334+
/**
335+
* Constructs {@link AqlQuery} objects from an array of arbitrary values.
336+
*
337+
* **Note**: Nesting `aql` template strings is a much safer alternative
338+
* for most use cases. This low-level helper function only exists to
339+
* complement the `aql` tag when constructing complex queries from dynamic
340+
* arrays of query fragments.
341+
*
342+
* @param values - Array of values to join. These values will behave exactly
343+
* like values interpolated in an `aql` template string.
344+
* @param sep - Seperator to insert between values. This value will behave
345+
* exactly like a value passed to {@link literal}, i.e. it will be
346+
* inlined as-is, rather than being converted into a bind parameter.
347+
*
348+
* @example
349+
* ```js
350+
* const users = db.collection("users");
351+
* const filters = [];
352+
* if (adminsOnly) filters.push(aql`FILTER user.admin`);
353+
* if (activeOnly) filters.push(aql`FILTER user.active`);
354+
* const result = await db.query(aql`
355+
* FOR user IN ${users}
356+
* ${join(filters)}
357+
* RETURN user
358+
* `);
359+
* ```
360+
*
361+
* @example
362+
* ```js
363+
* const users = db.collection("users");
364+
* const keys = ["jreyes", "ghermann"];
365+
*
366+
* // BAD! NEEDLESSLY COMPLEX!
367+
* const docs = keys.map(key => aql`DOCUMENT(${users}, ${key}`));
368+
* const result = await db.query(aql`
369+
* FOR user IN [
370+
* ${join(docs, ", ")}
371+
* ]
372+
* RETURN user
373+
* `);
374+
* // Query:
375+
* // FOR user IN [
376+
* // DOCUMENT(@@value0, @value1), DOCUMENT(@@value0, @value2)
377+
* // ]
378+
* // RETURN user
379+
* // Bind parameters:
380+
* // @value0 -> "users"
381+
* // value1 -> "jreyes"
382+
* // value2 -> "ghermann"
383+
*
384+
* // GOOD! MUCH SIMPLER!
385+
* const result = await db.query(aql`
386+
* FOR key IN ${keys}
387+
* LET user = DOCUMENT(${users}, key)
388+
* RETURN user
389+
* `);
390+
* // Query:
391+
* // FOR user IN @value0
392+
* // LET user = DOCUMENT(@@value1, key)
393+
* // RETURN user
394+
* // Bind parameters:
395+
* // value0 -> ["jreyes", "ghermann"]
396+
* // @value1 -> "users"
397+
* ```
398+
*/
399+
export function join(values: AqlValue[], sep: string = " "): GeneratedAqlQuery {
400+
if (!values.length) {
401+
return aql``;
402+
}
403+
if (values.length === 1) {
404+
return aql`${values[0]}`;
413405
}
406+
return aql(["", ...Array(values.length - 1).fill(sep), ""] as any, ...values);
414407
}

0 commit comments

Comments
 (0)