├── .eslintrc ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── assumptions.js ├── bower.json ├── dist ├── aqb.js └── aqb.min.js ├── errors.js ├── index.js ├── package.json ├── test ├── auto-cast.js ├── examples.js ├── query-builder.js └── types │ ├── BinaryOperation.js │ ├── BooleanLiteral.js │ ├── CollectExpression.js │ ├── CollectWithCountIntoExpression.js │ ├── Definitions.js │ ├── FilterExpression.js │ ├── ForExpression.js │ ├── FunctionCall.js │ ├── Identifier.js │ ├── InsertExpression.js │ ├── IntegerLiteral.js │ ├── Keyword.js │ ├── LetExpression.js │ ├── LimitExpression.js │ ├── ListLiteral.js │ ├── NAryOperation.js │ ├── NullLiteral.js │ ├── NumberLiteral.js │ ├── ObjectLiteral.js │ ├── PropertyAccess.js │ ├── RangeExpression.js │ ├── RawExpression.js │ ├── RemoveExpression.js │ ├── ReplaceExpression.js │ ├── ReturnExpression.js │ ├── SimpleReference.js │ ├── SortExpression.js │ ├── StringLiteral.js │ ├── TernaryOperation.js │ ├── UnaryOperation.js │ ├── UpdateExpression.js │ └── UpsertExpression.js └── types.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "es6": true 5 | }, 6 | "rules": { 7 | "new-cap": 0, 8 | "curly": [2, "multi-line"], 9 | "dot-notation": [2, {"allowPattern": "[^_a-z0-9]+"}], 10 | "global-strict": 0, 11 | "no-shadow": 0, 12 | "no-underscore-dangle": 0, 13 | "no-use-before-define": [2, "nofunc"], 14 | "quotes": [1, "single"], 15 | "strict": [2, "global"] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | coverage 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | - iojs 5 | script: 6 | - npm run coveralls -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ArangoDB Query Builder 2 | 3 | **We don't recommend using this lib anymore. We've implemented an [aql template handler](https://github.com/arangodb/arangojs/blob/master/docs/Drivers/JS/Reference/README.md#aql) in arangojs which should be used instead.** 4 | 5 | The query builder allows constructing complex AQL queries with a pure JavaScript fluid API. 6 | 7 | [![license - APACHE-2.0](https://img.shields.io/npm/l/aqb.svg)](http://opensource.org/licenses/APACHE-2.0) [![Dependencies](https://img.shields.io/david/arangodb/aqbjs.svg)](https://david-dm.org/arangodb/aqbjs) 8 | 9 | [![NPM status](https://nodei.co/npm/aqb.png?downloads=true&stars=true)](https://npmjs.org/package/aqb) 10 | 11 | [![Build status](https://img.shields.io/travis/arangodb/aqbjs.svg)](https://travis-ci.org/arangodb/aqbjs) [![Coverage Status](https://img.shields.io/coveralls/arangodb/aqbjs.svg)](https://coveralls.io/r/arangodb/aqbjs?branch=master) [![Codacy rating](https://img.shields.io/codacy/1d5a1be201024e0caaf0aa6bc990231e.svg)](https://www.codacy.com/public/me_4/aqbjs) 12 | 13 | # Install 14 | 15 | ## With NPM 16 | 17 | ```sh 18 | npm install aqb 19 | ``` 20 | 21 | ## ArangoDB 22 | 23 | A version of `aqb` comes pre-installed with ArangoDB. 24 | 25 | ```js 26 | var qb = require('aqb'); 27 | ``` 28 | 29 | If you want to use a more recent version of `aqb` in a Foxx app, you can add it to your NPM dependencies as usual. 30 | 31 | ## From source 32 | 33 | ```sh 34 | git clone https://github.com/arangodb/aqbjs.git 35 | cd aqbjs 36 | npm install 37 | npm run dist 38 | ``` 39 | 40 | # Example 41 | 42 | ```js 43 | // in arangosh 44 | var db = require('org/arangodb').db; 45 | var qb = require('aqb'); 46 | console.log(db._query(qb.for('x').in('1..5').return('x')).toArray()); // [1, 2, 3, 4, 5] 47 | ``` 48 | 49 | # API 50 | 51 | ## Auto-casting raw data 52 | 53 | By default, the query builder will attempt to interpret raw strings as identifiers or references or other kinds of expressions. This may not always be what you want, especially when handling raw untrusted data. 54 | 55 | As of version 1.8 you can now pass arbitrary data directly to the query builder itself and it will be translated to the equivalent AQL structure (e.g. strings will be strings, dates will be converted to JSON, arrays and objects will be translated recursively, and so on): 56 | 57 | ```js 58 | var doc = { 59 | aString: "hello", 60 | aDate: new Date(), 61 | aNumber: 23, 62 | anArray: [1, 2, 3, "potato"] 63 | }; 64 | db._query(qb.insert(qb(doc)).into('my_collection')); 65 | ``` 66 | 67 | ## AQL Types 68 | 69 | If raw JavaScript values are passed to AQL statements, they will be wrapped in a matching AQL type automatically. 70 | 71 | JavaScript strings wrapped in quotation marks will be wrapped in AQL strings, all other JavaScript strings will be wrapped as simple references (see below) and throw an *AQLError* if they are not well-formed. 72 | 73 | ### Boolean 74 | 75 | Wraps the given value as an AQL Boolean literal. 76 | 77 | `qb.bool(value)` 78 | 79 | If the value is truthy, it will be converted to the AQL Boolean *true*, otherwise it will be converted to the AQL Boolean *false*. 80 | 81 | If the value is already an AQL Boolean, its own value will be wrapped instead. 82 | 83 | ### Number 84 | 85 | Wraps the given value as an AQL Number literal. 86 | 87 | `qb.num(value)` 88 | 89 | If the value is not a JavaScript Number, it will be converted first. 90 | 91 | If the value does not represent a finite number, an *AQLError* will be thrown. 92 | 93 | If the value is already an AQL Number or AQL Integer, its own value will be wrapped instead. 94 | 95 | ### Integer 96 | 97 | Wraps the given value as an AQL Integer literal. 98 | 99 | `qb.int(value)` 100 | 101 | If the value is not a JavaScript Number, it will be converted first. 102 | 103 | If the value does not represent a finite integer, an *AQLError* will be thrown. 104 | 105 | If the value is already an AQL Number or AQL Integer, its own value will be wrapped instead. 106 | 107 | 108 | ### String 109 | 110 | Wraps the given value as an AQL String literal. 111 | 112 | `qb.str(value)` 113 | 114 | If the value is not a JavaScript String, it will be converted first. 115 | 116 | If the value is a quoted string, it will be treated as a string literal. 117 | 118 | If the value is an object with a *toAQL* method, the result of calling that method will be wrapped instead. 119 | 120 | **Examples** 121 | 122 | * `23` => `"23"` 123 | * `"some string"` => `"some string"` 124 | * `'"some string"'` => `"\"some string\""` 125 | 126 | ### List 127 | 128 | Wraps the given value as an AQL List (Array) literal. 129 | 130 | `qb.list(value)` 131 | 132 | If the value is not a JavaScript Array, an *AQLError* will be thrown. 133 | 134 | If the value is already an AQL List, its own value will be wrapped instead. 135 | 136 | Any list elements that are not already AQL values will be converted automatically. 137 | 138 | ### Object 139 | 140 | Wraps the given value as an AQL Object literal. 141 | 142 | `qb.obj(value)` 143 | 144 | If the value is not a JavaScript Object, an *AQLError* will be thrown. 145 | 146 | If the value is already an AQL Object, its own value will be wrapped instead. 147 | 148 | Any property values that are not already AQL values will be converted automatically. 149 | 150 | Any keys that are quoted strings will be treated as string literals. 151 | 152 | Any keys that start with the character "`:`" will be treated as dynamic properties and must be well-formed simple references. 153 | 154 | Any other keys that need escaping will be quoted if necessary. 155 | 156 | If you need to pass in raw JavaScript objects that shouldn't be converted according to these rules, you can use the `qb` function directly instead. 157 | 158 | **Examples** 159 | 160 | * `qb.obj({'some.name': 'value'})` => `{"some.name": value}` 161 | * `qb.obj({hello: world})` => `{hello: world}` 162 | * `qb.obj({'"hello"': world})` => `{"hello": world}` 163 | * `qb.obj({':dynamic': 'props'})` => `{[dynamic]: props}` 164 | * `qb.obj({': invalid': 'key'})` => throws an error (` invalid` is not a well-formed reference) 165 | 166 | ### Simple Reference 167 | 168 | Wraps a given value in an AQL Simple Reference. 169 | 170 | `qb.ref(value)` 171 | 172 | If the value is not a JavaScript string or not a well-formed simple reference, an *AQLError* will be thrown. 173 | 174 | If the value is an *ArangoCollection*, its *name* property will be used instead. 175 | 176 | If the value is already an AQL Simple Reference, its value is wrapped instead. 177 | 178 | **Examples** 179 | 180 | Valid values: 181 | 182 | * `foo` 183 | * `foo.bar` 184 | * `foo[*].bar` 185 | * `foo.bar.QUX` 186 | * `_foo._bar._qux` 187 | * `foo1.bar2` 188 | * `` `foo`.bar `` 189 | * `` foo.`bar` `` 190 | 191 | Invalid values: 192 | 193 | * `1foo` 194 | * `föö` 195 | * `foo bar` 196 | * `foo[bar]` 197 | 198 | ArangoDB collection objects can be passed directly: 199 | 200 | ```js 201 | var myUserCollection = applicationContext.collection('users'); 202 | var users = db._query(qb.for('u').in(myUserCollection).return('u')).toArray(); 203 | ``` 204 | 205 | ## AQL Expressions 206 | 207 | ### Range 208 | 209 | Creates a range expression from the given values. 210 | 211 | `qb.range(value1, value2)` => `value1..value2` 212 | 213 | OR: 214 | 215 | `aqlValue.range(value2)` => `value1..value2` 216 | 217 | If the values are not already AQL values, they will be converted automatically. 218 | 219 | *Alias:* `qb.to(value1, value2)` 220 | 221 | **Examples** 222 | 223 | `qb(2).to(5)` => `2..5` 224 | 225 | ### Property Access 226 | 227 | Creates a property access expression from the given values. 228 | 229 | `qb.get(obj, key)` => `obj[key]` 230 | 231 | OR: 232 | 233 | `aqlObj.get(key)` => `obj[key]` 234 | 235 | If the values are not already AQL values, they will be converted automatically. 236 | 237 | **Examples** 238 | 239 | `qb.ref('x').get('y') => `x[y]` 240 | 241 | ### Raw Expression 242 | 243 | Wraps a given value in a raw AQL expression. 244 | 245 | `qb.expr(value)` 246 | 247 | If the value is already an AQL Raw Expression, its value is wrapped instead. 248 | 249 | **Warning:** Whenever possible, you should use one of the other methods or a combination thereof instead of using a raw expression. Raw expressions allow passing arbitrary strings into your AQL and thus will open you to AQL injection attacks if you are passing in untrusted user input. 250 | 251 | ## AQL Operations 252 | 253 | ### Boolean And 254 | 255 | Creates an "and" operation from the given values. 256 | 257 | `qb.and(a, b)` => `(a && b)` 258 | 259 | OR: 260 | 261 | `aqlValue.and(b)` => `(a && b)` 262 | 263 | If the values are not already AQL values, they will be converted automatically. 264 | 265 | This function can take any number of arguments. 266 | 267 | **Examples** 268 | 269 | `qb.ref('x').and('y')` => `(x && y)` 270 | 271 | ### Boolean Or 272 | 273 | Creates an "or" operation from the given values. 274 | 275 | `qb.or(a, b)` => `(a || b)` 276 | 277 | OR: 278 | 279 | `aqlValue.or(b)` => `(a || b)` 280 | 281 | If the values are not already AQL values, they will be converted automatically. 282 | 283 | This function can take any number of arguments. 284 | 285 | **Examples** 286 | 287 | `qb.ref('x').or('y')` => `(x || y)` 288 | 289 | ### Addition 290 | 291 | Creates an addition operation from the given values. 292 | 293 | `qb.add(a, b)` => `(a + b)` 294 | 295 | OR: 296 | 297 | `aqlValue.add(b)` => `(a + b)` 298 | 299 | If the values are not already AQL values, they will be converted automatically. 300 | 301 | This function can take any number of arguments. 302 | 303 | *Alias:* `qb.plus(a, b)` 304 | 305 | **Examples** 306 | 307 | `qb.ref('x').plus('y')` => `(x + y)` 308 | 309 | ### Subtraction 310 | 311 | Creates a subtraction operation from the given values. 312 | 313 | `qb.sub(a, b)` => `(a - b)` 314 | 315 | OR: 316 | 317 | `aqlValue.sub(b)` => `(a - b)` 318 | 319 | If the values are not already AQL values, they will be converted automatically. 320 | 321 | This function can take any number of arguments. 322 | 323 | *Alias:* `qb.minus(a, b)` 324 | 325 | **Examples** 326 | 327 | `qb.ref('x').minus('y')` => `(x - y)` 328 | 329 | ### Multiplication 330 | 331 | Creates a multiplication operation from the given values. 332 | 333 | `qb.mul(a, b)` => `(a * b)` 334 | 335 | OR: 336 | 337 | `aqlValue.mul(b)` => `(a * b)` 338 | 339 | If the values are not already AQL values, they will be converted automatically. 340 | 341 | This function can take any number of arguments. 342 | 343 | *Alias:* `qb.times(a, b)` 344 | 345 | **Examples** 346 | 347 | `qb.ref('x').times('y')` => `(x * y)` 348 | 349 | ### Division 350 | 351 | Creates a division operation from the given values. 352 | 353 | `qb.div(a, b)` => `(a / b)` 354 | 355 | OR: 356 | 357 | `aqlValue.div(b)` => `(a / b)` 358 | 359 | If the values are not already AQL values, they will be converted automatically. 360 | 361 | This function can take any number of arguments. 362 | 363 | **Examples** 364 | 365 | `qb.ref('x').div('y')` => `(x / y)` 366 | 367 | ### Modulus 368 | 369 | Creates a modulus operation from the given values. 370 | 371 | `qb.mod(a, b)` => `(a % b)` 372 | 373 | OR: 374 | 375 | `aqlValue.mod(b)` => `(a % b)` 376 | 377 | If the values are not already AQL values, they will be converted automatically. 378 | 379 | This function can take any number of arguments. 380 | 381 | **Examples** 382 | 383 | `qb.ref('x').mod('y')` => `(x % y)` 384 | 385 | ### Equality 386 | 387 | Creates an equality comparison from the given values. 388 | 389 | `qb.eq(a, b)` => `(a == b)` 390 | 391 | OR: 392 | 393 | `qbValue.eq(b)` => `(a == b)` 394 | 395 | If the values are not already AQL values, they will be converted automatically. 396 | 397 | **Examples** 398 | 399 | `qb.ref('x').eq('y')` => `(x == y)` 400 | 401 | ### Inequality 402 | 403 | Creates an inequality comparison from the given values. 404 | 405 | `qb.neq(a, b)` => `(a != b)` 406 | 407 | OR: 408 | 409 | `qbValue.neq(b)` => `(a != b)` 410 | 411 | If the values are not already AQL values, they will be converted automatically. 412 | 413 | **Examples** 414 | 415 | `qb.ref('x').neq('y')` => `(x != y)` 416 | 417 | ### Greater Than 418 | 419 | Creates a greater-than comparison from the given values. 420 | 421 | `qb.gt(a, b)` => `(a > b)` 422 | 423 | OR 424 | 425 | `qbValue.gt(b)` => `(a > b)` 426 | 427 | If the values are not already AQL values, they will be converted automatically. 428 | 429 | **Examples** 430 | 431 | `qb.ref('x').gt('y')` => `(x > y)` 432 | 433 | ### Greater Than Or Equal To 434 | 435 | Creates a greater-than-or-equal-to comparison from the given values. 436 | 437 | `qb.gte(a, b)` => `(a >= b)` 438 | 439 | OR 440 | 441 | `qbValue.gte(b)` => `(a >= b)` 442 | 443 | If the values are not already AQL values, they will be converted automatically. 444 | 445 | **Examples** 446 | 447 | `qb.ref('x').gte('y')` => `(x >= y)` 448 | 449 | ### Less Than 450 | 451 | Creates a less-than comparison from the given values. 452 | 453 | `qb.lt(a, b)` => `(a < b)` 454 | 455 | OR 456 | 457 | `qbValue.lt(b)` => `(a < b)` 458 | 459 | If the values are not already AQL values, they will be converted automatically. 460 | 461 | **Examples** 462 | 463 | `qb.ref('x').lt('y')` => `(x < y)` 464 | 465 | ### Less Than Or Equal To 466 | 467 | Creates a less-than-or-equal-to comparison from the given values. 468 | 469 | `qb.lte(a, b)` => `(a <= b)` 470 | 471 | OR 472 | 473 | `qbValue.lte(b)` => `(a <= b)` 474 | 475 | If the values are not already AQL values, they will be converted automatically. 476 | 477 | **Examples** 478 | 479 | `qb.ref('x').lte('y')` => `(x <= y)` 480 | 481 | ### Contains 482 | 483 | Creates an "in" comparison from the given values. 484 | 485 | `qb.in(a, b)` => `(a in b)` 486 | 487 | OR: 488 | 489 | `qbValue.in(b)` => `(a in b)` 490 | 491 | If the values are not already AQL values, they will be converted automatically. 492 | 493 | 494 | **Examples** 495 | 496 | `qb.ref('x').in('y')` => `(x in y)` 497 | 498 | ### Negation 499 | 500 | Creates a negation from the given value. 501 | 502 | `qb.not(a)` => `!(a)` 503 | 504 | OR: 505 | 506 | `qbValue.not()` => `!(a)` 507 | 508 | If the value is not already an AQL value, it will be converted automatically. 509 | 510 | **Examples** 511 | 512 | `qb.not('x')` => `!(x)` 513 | 514 | ### Negated Contains 515 | 516 | Creates a "not in" comparison from the given values. 517 | 518 | `qb.notIn(a, b)` => `(a not in b)` 519 | 520 | OR: 521 | 522 | `qbValue.notIn(b)` => `(a not in b)` 523 | 524 | If the values are not already AQL values, they will be converted automatically. 525 | 526 | 527 | **Examples** 528 | 529 | `qb.ref('x').notIn('y')` => `(x not in y)` 530 | 531 | ### Negative Value 532 | 533 | Creates a negative value expression from the given value. 534 | 535 | `qb.neg(a)` => `-(a)` 536 | 537 | OR: 538 | 539 | `qbValue.neg()` => `-(a)` 540 | 541 | If the value is not already an AQL value, it will be converted automatically. 542 | 543 | **Examples** 544 | 545 | `qb.neg('x')` => `-(x)` 546 | 547 | ### Ternary (if / else) 548 | 549 | Creates a ternary expression from the given values. 550 | 551 | `qb.if(condition, thenDo, elseDo)` => `(condition ? thenDo : elseDo)` 552 | 553 | OR: 554 | 555 | `qbValue.then(thenDo).else(elseDo)` => `(condition ? thenDo : elseDo)` 556 | 557 | If the values are not already AQL values, they will be converted automatically. 558 | 559 | *Alias:* `qbValue.then(thenDo).otherwise(elseDo)` 560 | 561 | **Examples** 562 | 563 | `qb.ref('x').then('y').else('z')` => `(x ? y : z)` 564 | 565 | ### Function Call 566 | 567 | Creates a functon call for the given name and arguments. 568 | 569 | `qb.fn(name)(...args)` 570 | 571 | If the values are not already AQL values, they will be converted automatically. 572 | 573 | For built-in functions, methods with the relevant function name are already provided by the query builder. 574 | 575 | **Examples** 576 | 577 | * `qb.fn('MY::USER::FUNC')(1, 2, 3)` => `MY::USER::FUNC(1, 2, 3)` 578 | * `qb.fn('hello')()` => `hello()` 579 | * `qb.RANDOM()` => `RANDOM()` 580 | * `qb.FLOOR(qb.div(5, 2))` => `FLOOR((5 / 2))` 581 | 582 | ## AQL Statements 583 | 584 | In addition to the methods documented above, the query builder provides all methods of *PartialStatement* objects. 585 | 586 | AQL *Statement* objects have a method *toAQL()* which returns their AQL representation as a JavaScript string. 587 | 588 | **Examples** 589 | 590 | ```js 591 | qb.for('doc').in('my_collection').return('doc._key').toAQL() 592 | // => FOR doc IN my_collection RETURN doc._key 593 | ``` 594 | 595 | ### FOR expression IN collection 596 | 597 | `PartialStatement::for(expression).in(collection) : PartialStatement` 598 | 599 | 600 | **Examples** 601 | 602 | * `_.for('doc').in('my_collection')` => `FOR doc IN my_collection` 603 | 604 | ### LET varname = expression 605 | 606 | `PartialStatement::let(varname, expression) : PartialStatement` 607 | 608 | 609 | **Examples** 610 | 611 | * `_.let('foo', 23)` => `LET foo = 23` 612 | 613 | ### LET var1 = expr1, var2 = expr2, …, varn = exprn 614 | 615 | `PartialStatement::let(definitions) : PartialStatement` 616 | 617 | 618 | **Examples** 619 | 620 | * `_.let({a: 1, b: 2, c: 3})` => `LET a = 1, b = 2, c = 3` 621 | 622 | ### RETURN expression 623 | 624 | `PartialStatement::return(expression) : ReturnExpression` 625 | 626 | 627 | **Examples** 628 | 629 | * `_.return('x')` => `RETURN x` 630 | * `_.return({x: 'x'})` => `RETURN {x: x}` 631 | 632 | ### RETURN DISTINCT expression 633 | 634 | `PartialStatement::returnDistinct(expression) : ReturnExpression` 635 | 636 | **Examples** 637 | 638 | * `_.returnDistinct('x')` => `RETURN DISTINCT x` 639 | 640 | ### FILTER expression 641 | 642 | `PartialStatement::filter(expression) : PartialStatement` 643 | 644 | **Examples** 645 | 646 | * `_.filter(qb.eq('a', 'b'))` => `FILTER a == b` 647 | 648 | ### COLLECT … 649 | 650 | #### COLLECT WITH COUNT INTO varname 651 | 652 | `PartialStatement::collectWithCountInto(varname) : CollectExpression` 653 | 654 | **Examples** 655 | 656 | * `_.collectWithCountInto('x')` => `COLLECT WITH COUNT INTO x` 657 | 658 | #### COLLECT varname = expression 659 | 660 | `PartialStatement::collect(varname, expression) : CollectExpression` 661 | 662 | **Examples** 663 | 664 | * `_.collect('x', 'y')` => `COLLECT x = y` 665 | 666 | #### COLLECT var1 = expr1, var2 = expr2, …, varn = exprn 667 | 668 | `PartialStatement::collect(definitions) : CollectExpression` 669 | 670 | **Examples** 671 | 672 | * `_.collect({x: 'a', y: 'b'})` => `COLLECT x = a, y = b` 673 | 674 | #### … WITH COUNT INTO varname 675 | 676 | `CollectExpression::withCountInto(varname) : CollectExpression` 677 | 678 | **Examples** 679 | 680 | * `_.withCountInto('x')` => `WITH COUNT INTO x` 681 | 682 | #### … INTO varname 683 | 684 | `CollectExpression::into(varname) : CollectExpression` 685 | 686 | **Examples** 687 | 688 | * `_.into('z')` => `INTO z` 689 | 690 | ##### … KEEP ...vars 691 | 692 | `CollectExpression::keep(...vars) : CollectExpression` 693 | 694 | **Examples** 695 | 696 | * `_.into('z').keep('a', 'b')` => `INTO z KEEP a, b` 697 | 698 | #### … INTO varname = expression 699 | 700 | `CollectExpression::into(varname, expression) : CollectExpression` 701 | 702 | **Examples** 703 | 704 | * `_.into('x', 'y')` => `INTO x = y` 705 | 706 | #### … OPTIONS options 707 | 708 | `CollectExpression::options(options) : CollectExpression` 709 | 710 | **Examples** 711 | 712 | * `_.options('opts')` => `OPTIONS opts` 713 | 714 | ### … SORT ...args 715 | 716 | `PartialStatement::sort(...args) : PartialStatement` 717 | 718 | **Examples** 719 | 720 | * `_.sort('x', 'DESC', 'y', 'ASC')` => `SORT x DESC, y ASC` 721 | 722 | ### … LIMIT offset, count 723 | 724 | `PartialStatement::limit([offset,] count) : PartialStatement` 725 | 726 | **Examples** 727 | 728 | * `_.limit(20)` => `LIMIT 20` 729 | * `_.limit(20, 20)` => `LIMIT 20, 20` 730 | 731 | ### REMOVE … 732 | 733 | #### REMOVE expression IN collection 734 | 735 | `PartialStatement::remove(expression).in(collection) : RemoveExpression` 736 | 737 | *Alias:* `remove(expression).into(collection)` 738 | 739 | **Examples** 740 | 741 | * `_.remove('x').in('y')` => `REMOVE x IN y` 742 | 743 | #### … LET varname = OLD RETURN varname 744 | 745 | `RemoveExpression::returnOld(varname) : ReturnExpression` 746 | 747 | **Examples** 748 | 749 | * `_.returnOld('z')` => `LET z = OLD RETURN z` 750 | 751 | #### … OPTIONS options 752 | 753 | `RemoveExpression::options(options) : RemoveExpression` 754 | 755 | **Examples** 756 | 757 | * `_.options('opts')` => `OPTIONS opts` 758 | 759 | ### UPSERT … 760 | 761 | #### UPSERT expression1 INSERT expression2 REPLACE expression3 IN collection 762 | 763 | `PartialStatement::upsert(expression1).insert(expression2).replace(expression3).in(collection) : UpsertExpression` 764 | 765 | *Alias:* `….into(collection)` 766 | 767 | **Examples** 768 | 769 | * `_.upsert('x').insert('y').replace('z').in('c')` => `UPSERT x INSERT y REPLACE z IN c` 770 | 771 | #### UPSERT expression1 INSERT expression2 UPDATE expression3 IN collection 772 | 773 | `PartialStatement::upsert(expression1).insert(expression2).update(expression3).in(collection) : UpsertExpression` 774 | 775 | *Alias:* `….into(collection)` 776 | 777 | **Examples** 778 | 779 | * `_.upsert('x').insert('y').update('z').in('c')` => `UPSERT x INSERT y UPDATE z IN c` 780 | 781 | #### … OPTIONS options 782 | 783 | `UpsertExpression::options(options) : UpsertExpression` 784 | 785 | **Examples** 786 | 787 | * `_.options('opts')` => `OPTIONS opts` 788 | 789 | ### INSERT … 790 | 791 | #### INSERT expression INTO collection 792 | 793 | `PartialStatement::insert(expression).into(collection) : InsertExpression` 794 | 795 | *Alias:* `insert(expression).in(collection)` 796 | 797 | **Examples** 798 | 799 | * `_.insert('x').into('y')` => `INSERT x INTO y` 800 | 801 | #### … OPTIONS options 802 | 803 | `InsertExpression::options(options) : InsertExpression` 804 | 805 | **Examples** 806 | 807 | * `_.options('opts')` => `OPTIONS opts` 808 | 809 | #### … LET varname = NEW RETURN varname 810 | 811 | `InsertExpression::returnNew(varname) : ReturnExpression` 812 | 813 | **Examples** 814 | 815 | * `_.returnNew('z')` => `LET z = NEW RETURN z` 816 | 817 | ### UPDATE … 818 | 819 | #### UPDATE expression IN collection 820 | 821 | `PartialStatement::update(expression).in(collection) : UpdateExpression` 822 | 823 | *Alias:* `update(expression).into(collection)` 824 | 825 | **Examples** 826 | 827 | * `_.update('x').in('y')` => `UPDATE x IN y` 828 | 829 | #### UPDATE expression1 WITH expression2 IN collection 830 | 831 | `PartialStatement::update(expression1).with(expression2).in(collection) : UpdateExpression` 832 | 833 | *Alias:* `update(expression1).with(expression2).into(collection)` 834 | 835 | **Examples** 836 | 837 | * `_.update('x').with('y').in('z')` => `UPDATE x WITH y IN z` 838 | 839 | #### … OPTIONS options 840 | 841 | `UpdateExpression::options(options) : UpdateExpression` 842 | 843 | **Examples** 844 | 845 | * `_.options('opts')` => `OPTIONS opts` 846 | 847 | #### … LET varname = NEW RETURN varname 848 | 849 | `UpdateExpression::returnNew(varname) : ReturnExpression` 850 | 851 | **Examples** 852 | 853 | * `_.returnNew('z')` => `LET z = NEW RETURN z` 854 | 855 | #### … LET varname = OLD RETURN varname 856 | 857 | `UpdateExpression::returnOld(varname) : ReturnExpression` 858 | 859 | **Examples** 860 | 861 | * `_.returnOld('z')` => `LET z = OLD RETURN z` 862 | 863 | ### REPLACE … 864 | 865 | #### REPLACE expression IN collection 866 | 867 | `PartialStatement::replace(expression).in(collection) : ReplaceExpression` 868 | 869 | *Alias:* `replace(expression).into(collection)` 870 | 871 | **Examples** 872 | 873 | * `_.replace('x').in('y')` => `REPLACE x IN y` 874 | 875 | #### REPLACE expression1 WITH expression2 IN collection 876 | 877 | `PartialStatement::replace(expression1).with(expression2).in(collection) : ReplaceExpression` 878 | 879 | *Alias:* `replace(expression1).with(expression2).into(collection)` 880 | 881 | **Examples** 882 | 883 | * `_.replace('x').with('y').in('z')` => `REPLACE x WITH y IN z` 884 | 885 | #### … OPTIONS options 886 | 887 | `ReplaceExpression::options(options) : ReplaceExpression` 888 | 889 | **Examples** 890 | 891 | * `_.options('opts')` => `OPTIONS opts` 892 | 893 | #### … LET varname = NEW RETURN varname 894 | 895 | `ReplaceExpression::returnOld(varname) : ReturnExpression` 896 | 897 | **Examples** 898 | 899 | * `_.returnNew('z')` => `LET z = NEW RETURN z` 900 | 901 | #### … LET varname = OLD RETURN varname 902 | 903 | `ReplaceExpression::returnNew(varname) : ReturnExpression` 904 | 905 | **Examples** 906 | 907 | * `_.returnOld('z')` => `LET z = OLD RETURN z` 908 | 909 | # License 910 | 911 | The Apache License, Version 2.0. For more information, see the accompanying LICENSE file. 912 | -------------------------------------------------------------------------------- /assumptions.js: -------------------------------------------------------------------------------- 1 | /*jshint browserify: true */ 2 | 'use strict'; 3 | exports.keywords = [ 4 | 'asc', 5 | 'collect', 6 | 'desc', 7 | 'distinct', 8 | 'false', 9 | 'filter', 10 | 'for', 11 | 'in', 12 | 'insert', 13 | 'into', 14 | 'new', 15 | 'let', 16 | 'limit', 17 | 'old', 18 | 'null', 19 | 'remove', 20 | 'replace', 21 | 'return', 22 | 'sort', 23 | 'true', 24 | 'update', 25 | 'with' 26 | ]; 27 | 28 | exports.builtins = { 29 | // Conversion 30 | TO_BOOL: 1, TO_NUMBER: 1, TO_STRING: 1, TO_LIST: 1, 31 | // Type checks 32 | IS_NULL: 1, IS_BOOL: 1, IS_NUMBER: 1, IS_STRING: 1, IS_LIST: 1, IS_DOCUMENT: 1, 33 | // String functions 34 | CONCAT: [[1, Infinity]], CONCAT_SEPARATOR: [[2, Infinity]], 35 | CHAR_LENGTH: 1, LENGTH: 1, LOWER: 1, UPPER: 1, SUBSTRING: [2, 3], 36 | LEFT: 2, RIGHT: 2, TRIM: [1, 2], REVERSE: 1, CONTAINS: [2, 3], LIKE: 3, 37 | LTRIM: [1, 2], RTRIM: [1, 2], FIND_FIRST: [2, 3, 4], FIND_LAST: [2, 3, 4], 38 | SPLIT: [1, 2, 3], SUBSTITUTE: [2, 3, 4], MD5: 1, SHA1: 1, RANDOM_TOKEN: 1, 39 | // Numeric functions 40 | FLOOR: 1, CEIL: 1, ROUND: 1, ABS: 1, SQRT: 1, RAND: 0, 41 | // Date functions 42 | DATE_TIMESTAMP: [1, [3, 7]], DATE_ISO8601: [1, [3, 7]], 43 | DATE_DAYOFWEEK: 1, DATE_YEAR: 1, DATE_MONTH: 1, DATE_DAY: 1, 44 | DATE_HOUR: 1, DATE_MINUTE: 1, DATE_SECOND: 1, DATE_MILLISECOND: 1, 45 | DATE_NOW: 0, 46 | // List functions 47 | /*LENGTH: 1,*/ FLATTEN: [1, 2], MIN: 1, MAX: 1, AVERAGE: 1, SUM: 1, 48 | MEDIAN: 1, PERCENTILE: [2, 3], VARIANCE_POPULATION: 1, VARIANCE_SAMPLE: 1, 49 | STDDEV_POPULATION: 1, STDDEV_SAMPLE: 1, /*REVERSE: 1,*/ 50 | FIRST: 1, LAST: 1, NTH: 2, POSITION: [2, 3], SLICE: [2, 3], 51 | UNIQUE: 1, UNION: [[1, Infinity]], UNION_DISTINCT: [[1, Infinity]], 52 | MINUS: [[1, Infinity]], INTERSECTION: [[1, Infinity]], 53 | CALL: [[1, Infinity]], APPLY: [[1, Infinity]], 54 | PUSH: [2, 3], APPEND: [2, 3], POP: 1, SHIFT: 1, UNSHIFT: [2, 3], 55 | REMOVE_VALUE: [2, 3], REMOVE_VALUES: 2, REMOVE_NTH: 2, 56 | // Document functions 57 | MATCHES: [2, 3], MERGE: [[1, Infinity]], MERGE_RECURSIVE: [[1, Infinity]], 58 | TRANSLATE: [2, 3], HAS: 2, ATTRIBUTES: [[1, 3]], UNSET: [[1, Infinity]], 59 | KEEP: [[2, Infinity]], PARSE_IDENTIFIER: 1, ZIP: 2, 60 | // Geo functions 61 | NEAR: [5, 6], WITHIN: [5, 6], WITHIN_RECTANGLE: 5, IS_IN_POLYGON: [2, 3], 62 | // Fulltext functions 63 | FULLTEXT: 3, 64 | // Graph functions 65 | PATHS: [3, 4], TRAVERSAL: [5, 6], TRAVERSAL_TREE: [5, 6], 66 | SHORTEST_PATH: [5, 6], EDGES: [3, 5], NEIGHBORS: [4, 6], 67 | GRAPH_PATHS: [1, 2], GRAPH_SHORTEST_PATH: [3, 4], GRAPH_DISTANCE_TO: [3, 4], 68 | GRAPH_TRAVERSAL: [3, 4], GRAPH_TRAVERSAL_TREE: [4, 5], GRAPH_EDGES: [2, 3], 69 | GRAPH_VERTICES: [2, 3], GRAPH_NEIGHBORS: [2, 3], GRAPH_COMMON_NEIGHBORS: [3, 4, 5], 70 | GRAPH_COMMON_PROPERTIES: [3, 4], GRAPH_ECCENTRICITY: [1, 2], 71 | GRAPH_BETWEENNESS: [1, 2], GRAPH_CLOSENESS: [1, 2], 72 | GRAPH_ABSOLUTE_ECCENTRICITY: [2, 3], GRAPH_ABSOLUTE_BETWEENNESS: [2, 3], 73 | GRAPH_ABSOLUTE_CLOSENESS: [2, 3], GRAPH_DIAMETER: [1, 2], GRAPH_RADIUS: [1, 2], 74 | // Control flow functions 75 | NOT_NULL: [[1, Infinity]], FIRST_LIST: [[1, Infinity]], 76 | FIRST_DOCUMENT: [[1, Infinity]], 77 | // Miscellaneous functions 78 | COLLECTIONS: 0, CURRENT_USER: 0, DOCUMENT: [1, 2], SKIPLIST: [[2, 4]] 79 | }; 80 | 81 | exports.deprecatedBuiltins = [ 82 | 'SKIPLIST' 83 | ]; 84 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aqb", 3 | "homepage": "https://github.com/arangodb/aqbjs", 4 | "description": "ArangoDB AQL query builder.", 5 | "license": "APACHE-2.0", 6 | "ignore": [ 7 | "**", 8 | "!/dist/**", 9 | "!/bower.json", 10 | "!/README*", 11 | "!/LICENSE*" 12 | ], 13 | "main": [ 14 | "./dist/aqb.js" 15 | ] 16 | } -------------------------------------------------------------------------------- /dist/aqb.min.js: -------------------------------------------------------------------------------- 1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.aqb=e()}}(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o=arity[i][0]&&args.length<=arity[i][1]){valid=true}}if(!valid){throw new AqlError("Invalid number of arguments for function "+functionName+": "+args.length)}}return new types.FunctionCall(functionName,args)}};function deprecateAqlFunction(fn,functionName){return function(){console.warn("The AQL function "+functionName+" is deprecated!");return fn.apply(this,arguments)}}Object.keys(assumptions.builtins).forEach(function(key){QB[key]=QB.fn(key,assumptions.builtins[key]);if(assumptions.deprecatedBuiltins.indexOf(key)!==-1){QB[key]=deprecateAqlFunction(QB[key],key)}});module.exports=QB},{"./assumptions":1,"./errors":2,"./types":4,console:undefined}],4:[function(require,module,exports){"use strict";var AqlError=require("./errors").AqlError;var keywords=require("./assumptions").keywords;function toArray(self,args){var arr=Array.prototype.slice.call(args);if(self)arr.unshift(self);return arr}function isQuotedString(str){return str.length>=2&&str.charAt(0)===str.charAt(str.length-1)&&str.charAt(0)==='"'}function wrapAQL(expr){if(expr instanceof Operation||expr instanceof ReturnExpression||expr instanceof PartialStatement){return"("+expr.toAQL()+")"}return expr.toAQL()}function isValidNumber(number){return number===number&&number!==Infinity&&number!==-Infinity}function castNumber(number){if(Math.floor(number)===number){return new IntegerLiteral(number)}return new NumberLiteral(number)}function castBoolean(bool){return new BooleanLiteral(bool)}function castString(str){if(str.match(NumberLiteral.re)){return autoCastToken(Number(str))}if(isQuotedString(str)){return new StringLiteral(JSON.parse(str))}var match=str.match(RangeExpression.re);if(match){return new RangeExpression(Number(match[1]),Number(match[2]))}if(str.match(Identifier.re)){return new Identifier(str)}return new SimpleReference(str)}function castObject(obj){if(obj.constructor&&obj.constructor.name==="ArangoCollection"){return new Identifier(obj)}if(Array.isArray(obj)){return new ListLiteral(obj)}return new ObjectLiteral(obj)}function autoCastToken(token){if(token===null||token===undefined){return new NullLiteral}if(token instanceof Expression||token instanceof PartialStatement){return token}var type=typeof token;if(!autoCastToken.hasOwnProperty(type)){throw new AqlError("Invalid AQL value: ("+type+") "+token)}return autoCastToken[type](token)}autoCastToken.number=castNumber;autoCastToken["boolean"]=castBoolean;autoCastToken.string=castString;autoCastToken.object=castObject;function Definitions(dfns){if(dfns instanceof Definitions){dfns=dfns._dfns}this._dfns=[];var self=this;if(!dfns||typeof dfns!=="object"){throw new AqlError("Expected definitions to be an object")}if(Array.isArray(dfns)){dfns.forEach(function(dfn,i){if(!Array.isArray(dfn)||dfn.length!==2){throw new AqlError("Expected definitions["+i+"] to be a tuple")}self._dfns.push([new Identifier(dfn[0]),autoCastToken(dfn[1])])})}else{Object.keys(dfns).forEach(function(key){self._dfns.push([new Identifier(key),autoCastToken(dfns[key])])})}if(this._dfns.length===0){throw new AqlError("Expected definitions not to be empty")}}Definitions.prototype.toAQL=function(){return this._dfns.map(function(dfn){return dfn[0].toAQL()+" = "+wrapAQL(dfn[1])}).join(", ")};function Expression(){}Expression.prototype.range=Expression.prototype.to=function(max){return new RangeExpression(this,max)};Expression.prototype.get=function(){return new PropertyAccess(this,toArray(null,arguments))};Expression.prototype.and=function(){return new NAryOperation("&&",toArray(this,arguments))};Expression.prototype.or=function(){return new NAryOperation("||",toArray(this,arguments))};Expression.prototype.add=Expression.prototype.plus=function(){return new NAryOperation("+",toArray(this,arguments))};Expression.prototype.sub=Expression.prototype.minus=function(){return new NAryOperation("-",toArray(this,arguments))};Expression.prototype.mul=Expression.prototype.times=function(){return new NAryOperation("*",toArray(this,arguments))};Expression.prototype.div=function(){return new NAryOperation("/",toArray(this,arguments))};Expression.prototype.mod=function(){return new NAryOperation("%",toArray(this,arguments))};Expression.prototype.eq=function(x){return new BinaryOperation("==",this,x)};Expression.prototype.gt=function(x){return new BinaryOperation(">",this,x)};Expression.prototype.gte=function(x){return new BinaryOperation(">=",this,x)};Expression.prototype.lt=function(x){return new BinaryOperation("<",this,x)};Expression.prototype.lte=function(x){return new BinaryOperation("<=",this,x)};Expression.prototype.neq=function(x){return new BinaryOperation("!=",this,x)};Expression.prototype.not=function(){return new UnaryOperation("!",this)};Expression.prototype.neg=function(){return new UnaryOperation("-",this)};Expression.prototype["in"]=function(x){return new BinaryOperation("in",this,x)};Expression.prototype.notIn=function(x){return new BinaryOperation("not in",this,x)};Expression.prototype.then=function(x){var self=this;var elseFn=function(y){return new TernaryOperation("?",":",self,x,y)};return{"else":elseFn,else_:elseFn,otherwise:elseFn}};function Operation(){}Operation.prototype=new Expression;Operation.prototype.constructor=Operation;function RawExpression(value){if(value&&value instanceof RawExpression)value=value._value;this._value=value}RawExpression.prototype=new Expression;RawExpression.prototype.constructor=RawExpression;RawExpression.prototype.toAQL=function(){return String(this._value)};function NullLiteral(value){if(value&&value instanceof NullLiteral)value=value._value;if(value!==null&&value!==undefined){throw new AqlError("Expected value to be null: "+value)}this._value=value}NullLiteral.prototype=new Expression;NullLiteral.prototype.constructor=NullLiteral;NullLiteral.prototype.toAQL=function(){return"null"};function BooleanLiteral(value){if(value&&value instanceof BooleanLiteral)value=value._value;this._value=Boolean(value)}BooleanLiteral.prototype=new Expression;BooleanLiteral.prototype.constructor=BooleanLiteral;BooleanLiteral.prototype.toAQL=function(){return String(this._value)};function NumberLiteral(value){if(value&&(value instanceof NumberLiteral||value instanceof IntegerLiteral))value=value._value;this._value=Number(value);if(!isValidNumber(this._value)){throw new AqlError("Expected value to be a finite number: "+value)}}NumberLiteral.re=/^[-+]?[0-9]+(\.[0-9]+)?$/;NumberLiteral.prototype=new Expression;NumberLiteral.prototype.constructor=NumberLiteral;NumberLiteral.prototype.toAQL=function(){return String(this._value)};function IntegerLiteral(value){if(value&&(value instanceof NumberLiteral||value instanceof IntegerLiteral))value=value._value;this._value=Number(value);if(!isValidNumber(this._value)||Math.floor(this._value)!==this._value){throw new AqlError("Expected value to be a finite integer: "+value)}}IntegerLiteral.prototype=new Expression;IntegerLiteral.prototype.constructor=IntegerLiteral;IntegerLiteral.prototype.toAQL=function(){return String(this._value)};function StringLiteral(value){if(value&&value instanceof StringLiteral)value=value._value;if(value&&typeof value.toAQL==="function")value=value.toAQL();this._value=String(value)}StringLiteral.prototype=new Expression;StringLiteral.prototype.constructor=StringLiteral;StringLiteral.prototype.toAQL=function(){return JSON.stringify(this._value)};function ListLiteral(value){if(value&&value instanceof ListLiteral)value=value._value;if(!value||!Array.isArray(value)){throw new AqlError("Expected value to be an array: "+value)}this._value=value.map(autoCastToken)}ListLiteral.prototype=new Expression;ListLiteral.prototype.constructor=ListLiteral;ListLiteral.prototype.toAQL=function(){var value=this._value.map(wrapAQL);return"["+value.join(", ")+"]"};function ObjectLiteral(value){if(value&&value instanceof ObjectLiteral)value=value._value;if(!value||typeof value!=="object"){throw new AqlError("Expected value to be an object: "+value)}this._value={};var self=this;Object.keys(value).forEach(function(key){if(key.charAt(0)===":"){if(!key.slice(1).match(SimpleReference.re)){throw new AqlError("Expected key to be a well-formed dynamic property name: "+key)}self._value["["+key.slice(1)+"]"]=autoCastToken(value[key])}else if(!isQuotedString(key)&&!key.match(Identifier.re)&&key!==String(Number(key))){self._value[JSON.stringify(key)]=autoCastToken(value[key])}else{self._value[key]=autoCastToken(value[key])}})}ObjectLiteral.prototype=new Expression;ObjectLiteral.prototype.constructor=ObjectLiteral;ObjectLiteral.prototype.toAQL=function(){var value=this._value;var items=Object.keys(value).map(function(key){return key+": "+wrapAQL(value[key])});return"{"+items.join(", ")+"}"};function RangeExpression(start,end){this._start=autoCastToken(start);this._end=autoCastToken(end)}RangeExpression.re=/^([0-9]+)\.\.([0-9]+)$/;RangeExpression.prototype=new Expression;RangeExpression.prototype.constructor=RangeExpression;RangeExpression.prototype.toAQL=function(){return wrapAQL(this._start)+".."+wrapAQL(this._end)};function PropertyAccess(obj,keys){this._obj=autoCastToken(obj);this._keys=keys.map(function(key){return autoCastToken(key)})}PropertyAccess.prototype=new Expression;PropertyAccess.prototype.constructor=PropertyAccess;PropertyAccess.prototype.toAQL=function(){return wrapAQL(this._obj)+this._keys.map(function(key){return"["+wrapAQL(key)+"]"}).join("")};function Keyword(value){if(value&&value instanceof Keyword)value=value._value;if(!value||typeof value!=="string"){throw new AqlError("Expected value to be a string: "+value)}if(!value.match(Keyword.re)){throw new AqlError("Not a valid keyword: "+value)}this._value=value}Keyword.re=/^[_a-z][_0-9a-z]*$/i;Keyword.prototype=new Expression;Keyword.prototype.constructor=Keyword;Keyword.prototype.toAQL=function(){return String(this._value).toUpperCase()};function Identifier(value){if(value){if(value.constructor&&value.constructor.name==="ArangoCollection"){value=value.name()}else if(value instanceof Identifier){value=value._value}}if(!value||typeof value!=="string"){throw new AqlError("Expected value to be a string: "+value)}if(!value.match(Identifier.re)){throw new AqlError("Not a valid identifier: "+value)}this._value=value}Identifier.re=/^[_@a-z][-_@0-9a-z]*$/i;Identifier.prototype=new Expression;Identifier.prototype.constructor=Identifier;Identifier.prototype.toAQL=function(){var value=String(this._value);if(keywords.indexOf(value.toLowerCase())!==-1||value.indexOf("-")!==-1){return"`"+value+"`"}return value};function SimpleReference(value){if(value){if(value.constructor&&value.constructor.name==="ArangoCollection"){value=value.name()}else if(value instanceof SimpleReference){value=value._value}}if(!value||typeof value!=="string"){throw new AqlError("Expected value to be a string: "+value)}if(!value.match(SimpleReference.re)){throw new AqlError("Not a valid simple reference: "+value)}this._value=value}SimpleReference.re=/^([_@a-z][-_@0-9a-z]*|`[_@a-z][-_@0-9a-z]*`)(\.[_@a-z][-_@0-9a-z]*|\.`[_@a-z][-_@0-9a-z]*`|\[\*\])*$/i;SimpleReference.prototype=new Expression;SimpleReference.prototype.constructor=SimpleReference;SimpleReference.prototype.toAQL=function(){var value=String(this._value);var tokens=value.split(".").map(function(token){if(token.charAt(0)!=="`"&&(keywords.indexOf(token.toLowerCase())!==-1||token.indexOf("-")!==-1)){return"`"+token+"`"}return token});return tokens.join(".")};function UnaryOperation(operator,value){if(!operator||typeof operator!=="string"){throw new AqlError("Expected operator to be a string: "+operator)}this._operator=operator;this._value=autoCastToken(value)}UnaryOperation.prototype=new Expression;UnaryOperation.prototype.constructor=UnaryOperation;UnaryOperation.prototype.toAQL=function(){return this._operator+wrapAQL(this._value)};function BinaryOperation(operator,value1,value2){if(!operator||typeof operator!=="string"){throw new AqlError("Expected operator to be a string: "+operator)}this._operator=operator;this._value1=autoCastToken(value1);this._value2=autoCastToken(value2)}BinaryOperation.prototype=new Operation;BinaryOperation.prototype.constructor=BinaryOperation;BinaryOperation.prototype.toAQL=function(){return[wrapAQL(this._value1),this._operator,wrapAQL(this._value2)].join(" ")};function TernaryOperation(operator1,operator2,value1,value2,value3){if(!operator1||typeof operator1!=="string"){throw new AqlError("Expected operator 1 to be a string: "+operator1)}if(!operator2||typeof operator2!=="string"){throw new AqlError("Expected operator 2 to be a string: "+operator2)}this._operator1=operator1;this._operator2=operator2;this._value1=autoCastToken(value1);this._value2=autoCastToken(value2);this._value3=autoCastToken(value3)}TernaryOperation.prototype=new Operation;TernaryOperation.prototype.constructor=TernaryOperation;TernaryOperation.prototype.toAQL=function(){return[wrapAQL(this._value1),this._operator1,wrapAQL(this._value2),this._operator2,wrapAQL(this._value3)].join(" ")};function NAryOperation(operator,values){if(!operator||typeof operator!=="string"){throw new AqlError("Expected operator to be a string: "+operator)}this._operator=operator;this._values=values.map(autoCastToken)}NAryOperation.prototype=new Operation;NAryOperation.prototype.constructor=NAryOperation;NAryOperation.prototype.toAQL=function(){var values=this._values.map(wrapAQL);return values.join(" "+this._operator+" ")};function FunctionCall(functionName,args){if(!functionName||typeof functionName!=="string"){throw new AqlError("Expected function name to be a string: "+functionName)}if(!functionName.match(FunctionCall.re)){throw new AqlError("Not a valid function name: "+functionName)}if(args&&!Array.isArray(args)){throw new AqlError("Expected arguments to be an array: "+args)}this._functionName=functionName;this._args=args?args.map(autoCastToken):[]}FunctionCall.re=/^[_a-z][_0-9a-z]*(::[_a-z][_0-9a-z]*)*$/i;FunctionCall.prototype=new Expression;FunctionCall.prototype.constructor=FunctionCall;FunctionCall.prototype.toAQL=function(){var args=this._args.map(wrapAQL);return this._functionName+"("+args.join(", ")+")"};function ReturnExpression(prev,value,distinct){this._prev=prev;this._value=autoCastToken(value);this._distinct=distinct}ReturnExpression.prototype=new Expression;ReturnExpression.prototype.constructor=ReturnExpression;ReturnExpression.prototype.toAQL=function(){return(this._prev?this._prev.toAQL()+" ":"")+"RETURN"+(this._distinct?" DISTINCT":"")+" "+wrapAQL(this._value)};function PartialStatement(){}PartialStatement.prototype["for"]=function(varname){var self=this,inFn;inFn=function(expr){return new ForExpression(self,varname,expr)};return{"in":inFn,in_:inFn}};PartialStatement.prototype.filter=function(expr){return new FilterExpression(this,expr)};PartialStatement.prototype.let=function(varname,expr){var dfns=expr===undefined?varname:[[varname,expr]];return new LetExpression(this,dfns)};PartialStatement.prototype.collect=function(varname,expr){var dfns=expr===undefined?varname:[[varname,expr]];return new CollectExpression(this,dfns)};PartialStatement.prototype.collectWithCountInto=function(varname){return new CollectWithCountIntoExpression(this,undefined,varname)};PartialStatement.prototype.sort=function(){var args=Array.prototype.slice.call(arguments);return new SortExpression(this,args)};PartialStatement.prototype.limit=function(x,y){return new LimitExpression(this,x,y)};PartialStatement.prototype["return"]=function(x){return new ReturnExpression(this,x,false)};PartialStatement.prototype.returnDistinct=function(x){return new ReturnExpression(this,x,true)};PartialStatement.prototype.remove=function(expr){var self=this,inFn;inFn=function(collection){return new RemoveExpression(self,expr,collection)};return{into:inFn,"in":inFn,in_:inFn}};PartialStatement.prototype.upsert=function(upsertExpr){var self=this;function insertFn(insertExpr){function updateOrReplaceFn(replace){return function(updateOrReplaceExpr){function inFn(inCollection){return new UpsertExpression(self,upsertExpr,insertExpr,replace,updateOrReplaceExpr,inCollection)}return{into:inFn,"in":inFn,in_:inFn}}}return{update:updateOrReplaceFn(false),replace:updateOrReplaceFn(true)}}return{insert:insertFn}};PartialStatement.prototype.insert=function(expr){var self=this,inFn;inFn=function(collection){return new InsertExpression(self,expr,collection)};return{into:inFn,"in":inFn,in_:inFn}};PartialStatement.prototype.update=function(expr){var self=this,withFn,inFn;withFn=function(withExpr){var inFn=function(collection){return new UpdateExpression(self,expr,withExpr,collection)};return{into:inFn,"in":inFn,in_:inFn}};inFn=function(collection){return new UpdateExpression(self,expr,undefined,collection)};return{"with":withFn,with_:withFn,into:inFn,"in":inFn,in_:inFn}};PartialStatement.prototype.replace=function(expr){var self=this,withFn,inFn;withFn=function(withExpr){var inFn=function(collection){return new ReplaceExpression(self,expr,withExpr,collection)};return{into:inFn,"in":inFn,in_:inFn}};inFn=function(collection){return new ReplaceExpression(self,expr,undefined,collection)};return{"with":withFn,with_:withFn,into:inFn,"in":inFn,in_:inFn}};function ForExpression(prev,varname,expr){this._prev=prev;this._varname=new Identifier(varname);this._expr=autoCastToken(expr)}ForExpression.prototype=new PartialStatement;ForExpression.prototype.constructor=ForExpression;ForExpression.prototype.toAQL=function(){return(this._prev?this._prev.toAQL()+" ":"")+"FOR "+wrapAQL(this._varname)+" IN "+wrapAQL(this._expr)};function FilterExpression(prev,expr){this._prev=prev;this._expr=autoCastToken(expr)}FilterExpression.prototype=new PartialStatement;FilterExpression.prototype.constructor=FilterExpression;FilterExpression.prototype.toAQL=function(){return(this._prev?this._prev.toAQL()+" ":"")+"FILTER "+wrapAQL(this._expr)};function LetExpression(prev,dfns){this._prev=prev;this._dfns=new Definitions(dfns)}LetExpression.prototype=new PartialStatement;LetExpression.prototype.constructor=LetExpression;LetExpression.prototype.toAQL=function(){return(this._prev?this._prev.toAQL()+" ":"")+"LET "+wrapAQL(this._dfns)};function CollectExpression(prev,dfns,varname,intoExpr,keepNames,options){this._prev=prev;this._dfns=new Definitions(dfns);if(!intoExpr){if(!varname){this.into=function(newVarname,newIntoExpr){return new CollectExpression(prev,dfns,newVarname,newIntoExpr,undefined,options)}}else{this._varname=new Identifier(varname);if(!keepNames){this.keep=function(){var newKeepNames=Array.prototype.slice.call(arguments);return new CollectExpression(prev,dfns,varname,undefined,newKeepNames,options)}}else{if(!Array.isArray(keepNames)){throw new AqlError("Expected keep list to be an array: "+keepNames)}if(!keepNames.length){throw new AqlError("Expected keep list not to be empty: "+keepNames)}this._keep=keepNames.map(function(keepVar){return new Identifier(keepVar)})}}}else if(varname){this._varname=new Identifier(varname);this._intoExpr=autoCastToken(intoExpr)}if(!options){this.options=function(newOpts){return new CollectExpression(prev,dfns,varname,intoExpr,keepNames,newOpts)}}else this._options=new ObjectLiteral(options);if(!varname&&!intoExpr&&!keepNames){this.withCountInto=function(newVarname){return new CollectWithCountIntoExpression(prev,dfns,newVarname,options)}}}CollectExpression.prototype=new PartialStatement;CollectExpression.prototype.constructor=CollectExpression;CollectExpression.prototype.toAQL=function(){return(this._prev?this._prev.toAQL()+" ":"")+"COLLECT "+wrapAQL(this._dfns)+(this._varname?" INTO "+wrapAQL(this._varname)+(this._intoExpr?" = "+wrapAQL(this._intoExpr):this._keep?" KEEP "+this._keep.map(function(keep){return keep.toAQL()}).join(", "):""):"")+(this._options?" OPTIONS "+wrapAQL(this._options):"")};function CollectWithCountIntoExpression(prev,dfns,varname,options){this._prev=prev;if(dfns)this._dfns=new Definitions(dfns);this._varname=new Identifier(varname);if(!options){this.options=function(newOpts){return new CollectWithCountIntoExpression(prev,dfns,varname,newOpts)}}else this._options=new ObjectLiteral(options)}CollectWithCountIntoExpression.prototype=new PartialStatement;CollectWithCountIntoExpression.prototype.constructor=CollectWithCountIntoExpression;CollectWithCountIntoExpression.prototype.toAQL=function(){return(this._prev?this._prev.toAQL()+" ":"")+"COLLECT"+(this._dfns?" "+wrapAQL(this._dfns):"")+" WITH COUNT INTO "+wrapAQL(this._varname)+(this._options?" OPTIONS "+wrapAQL(this._options):"")};function SortExpression(prev,args){if(!args||!Array.isArray(args)){throw new AqlError("Expected sort list to be an array: "+args)}if(!args.length){throw new AqlError("Expected sort list not to be empty: "+args)}this._prev=prev;this._args=[];var allowKeyword=false;this._args=args.map(function(arg,i){if(!allowKeyword&&arg){if(arg instanceof Keyword||typeof arg==="string"&&SortExpression.keywords.indexOf(arg.toUpperCase())!==-1){throw new AqlError("Unexpected keyword "+arg.toString()+" at offset "+i)}}if(typeof arg==="string"&&SortExpression.keywords.indexOf(arg.toUpperCase())!==-1){allowKeyword=false;return new Keyword(arg)}else{allowKeyword=true;return autoCastToken(arg)}})}SortExpression.keywords=["ASC","DESC"];SortExpression.prototype=new PartialStatement;SortExpression.prototype.constructor=SortExpression;SortExpression.prototype.toAQL=function(){var args=[],j=0;this._args.forEach(function(arg){if(arg instanceof Keyword){args[j]+=" "+arg.toAQL()}else{j=args.push(wrapAQL(arg))-1}});return(this._prev?this._prev.toAQL()+" ":"")+"SORT "+args.join(", ")};function LimitExpression(prev,offset,count){if(count===undefined){count=offset;offset=undefined}this._prev=prev;this._offset=offset===undefined?undefined:autoCastToken(offset);this._count=autoCastToken(count)}LimitExpression.prototype=new PartialStatement;LimitExpression.prototype.constructor=LimitExpression;LimitExpression.prototype.toAQL=function(){return(this._prev?this._prev.toAQL()+" ":"")+"LIMIT "+(this._offset===undefined?wrapAQL(this._count):wrapAQL(this._offset)+", "+wrapAQL(this._count))};function RemoveExpression(prev,expr,collection,options){this._prev=prev;this._expr=autoCastToken(expr);this._collection=new Identifier(collection);if(!options){this.options=function(newOpts){return new RemoveExpression(prev,expr,collection,newOpts)}}else this._options=new ObjectLiteral(options)}RemoveExpression.prototype=new PartialStatement;RemoveExpression.prototype.constructor=RemoveExpression;RemoveExpression.prototype.returnOld=function(x){return this.let(x,"OLD")["return"](x)};RemoveExpression.prototype.toAQL=function(){return(this._prev?this._prev.toAQL()+" ":"")+"REMOVE "+wrapAQL(this._expr)+" IN "+wrapAQL(this._collection)+(this._options?" OPTIONS "+wrapAQL(this._options):"")};function UpsertExpression(prev,upsertExpr,insertExpr,replace,updateOrReplaceExpr,collection,options){this._prev=prev;this._upsertExpr=autoCastToken(upsertExpr);this._insertExpr=autoCastToken(insertExpr);this._updateOrReplace=replace?"REPLACE":"UPDATE";this._updateOrReplaceExpr=autoCastToken(updateOrReplaceExpr);this._collection=new Identifier(collection);if(!options){this.options=function(newOpts){return new UpsertExpression(prev,upsertExpr,insertExpr,replace,updateOrReplaceExpr,collection,newOpts)}}else this._options=new ObjectLiteral(options)}UpsertExpression.prototype=new PartialStatement;UpsertExpression.prototype.constructor=UpsertExpression;UpsertExpression.prototype.returnNew=function(x){return this.let(x,"NEW")["return"](x)};UpsertExpression.prototype.returnOld=function(x){return this.let(x,"OLD")["return"](x)};UpsertExpression.prototype.toAQL=function(){return(this._prev?this._prev.toAQL()+" ":"")+"UPSERT "+wrapAQL(this._upsertExpr)+" INSERT "+wrapAQL(this._insertExpr)+" "+this._updateOrReplace+" "+wrapAQL(this._updateOrReplaceExpr)+" IN "+wrapAQL(this._collection)+(this._options?" OPTIONS "+wrapAQL(this._options):"")};function InsertExpression(prev,expr,collection,options){this._prev=prev;this._expr=autoCastToken(expr);this._collection=new Identifier(collection);if(!options){this.options=function(newOpts){return new InsertExpression(prev,expr,collection,newOpts)}}else this._options=new ObjectLiteral(options)}InsertExpression.prototype=new PartialStatement;InsertExpression.prototype.constructor=InsertExpression;InsertExpression.prototype.returnNew=function(x){return this.let(x,"NEW")["return"](x)};InsertExpression.prototype.toAQL=function(){return(this._prev?this._prev.toAQL()+" ":"")+"INSERT "+wrapAQL(this._expr)+" INTO "+wrapAQL(this._collection)+(this._options?" OPTIONS "+wrapAQL(this._options):"")};function UpdateExpression(prev,expr,withExpr,collection,options){this._prev=prev;this._expr=autoCastToken(expr);this._withExpr=withExpr===undefined?undefined:autoCastToken(withExpr);this._collection=new Identifier(collection);if(!options){this.options=function(newOpts){return new UpdateExpression(prev,expr,withExpr,collection,newOpts)}}else this._options=new ObjectLiteral(options)}UpdateExpression.prototype=new PartialStatement;UpdateExpression.prototype.constructor=UpdateExpression;UpdateExpression.prototype.returnNew=function(x){return this.let(x,"NEW")["return"](x)};UpdateExpression.prototype.returnOld=function(x){return this.let(x,"OLD")["return"](x)};UpdateExpression.prototype.toAQL=function(){return(this._prev?this._prev.toAQL()+" ":"")+"UPDATE "+wrapAQL(this._expr)+(this._withExpr?" WITH "+wrapAQL(this._withExpr):"")+" IN "+wrapAQL(this._collection)+(this._options?" OPTIONS "+wrapAQL(this._options):"")};function ReplaceExpression(prev,expr,withExpr,collection,options){this._prev=prev;this._expr=autoCastToken(expr);this._withExpr=withExpr===undefined?undefined:autoCastToken(withExpr);this._collection=new Identifier(collection);if(!options){this.options=function(newOpts){return new ReplaceExpression(prev,expr,withExpr,collection,newOpts)}}else this._options=new ObjectLiteral(options)}ReplaceExpression.prototype=new PartialStatement;ReplaceExpression.prototype.constructor=ReplaceExpression;ReplaceExpression.prototype.returnNew=function(x){return this.let(x,"NEW")["return"](x)};ReplaceExpression.prototype.returnOld=function(x){return this.let(x,"OLD")["return"](x)};ReplaceExpression.prototype.toAQL=function(){return(this._prev?this._prev.toAQL()+" ":"")+"REPLACE "+wrapAQL(this._expr)+(this._withExpr?" WITH "+wrapAQL(this._withExpr):"")+" IN "+wrapAQL(this._collection)+(this._options?" OPTIONS "+wrapAQL(this._options):"") 2 | };exports.autoCastToken=autoCastToken;exports.RawExpression=RawExpression;exports.NullLiteral=NullLiteral;exports.BooleanLiteral=BooleanLiteral;exports.NumberLiteral=NumberLiteral;exports.IntegerLiteral=IntegerLiteral;exports.StringLiteral=StringLiteral;exports.ListLiteral=ListLiteral;exports.ObjectLiteral=ObjectLiteral;exports.RangeExpression=RangeExpression;exports.PropertyAccess=PropertyAccess;exports.Keyword=Keyword;exports.Identifier=Identifier;exports.SimpleReference=SimpleReference;exports.UnaryOperation=UnaryOperation;exports.BinaryOperation=BinaryOperation;exports.TernaryOperation=TernaryOperation;exports.NAryOperation=NAryOperation;exports.FunctionCall=FunctionCall;exports.ForExpression=ForExpression;exports.FilterExpression=FilterExpression;exports.LetExpression=LetExpression;exports.CollectExpression=CollectExpression;exports.CollectWithCountIntoExpression=CollectWithCountIntoExpression;exports.SortExpression=SortExpression;exports.LimitExpression=LimitExpression;exports.ReturnExpression=ReturnExpression;exports.RemoveExpression=RemoveExpression;exports.UpsertExpression=UpsertExpression;exports.InsertExpression=InsertExpression;exports.UpdateExpression=UpdateExpression;exports.ReplaceExpression=ReplaceExpression;exports._Expression=Expression;exports._Operation=Operation;exports._Statement=ReturnExpression;exports._PartialStatement=PartialStatement;exports._Definitions=Definitions},{"./assumptions":1,"./errors":2}]},{},[3])(3)}); -------------------------------------------------------------------------------- /errors.js: -------------------------------------------------------------------------------- 1 | /*jshint browserify: true */ 2 | 'use strict'; 3 | function AqlError(message) { 4 | this.message = message; 5 | var err = new Error(message); 6 | err.name = this.name; 7 | if (err.hasOwnProperty('stack')) this.stack = err.stack; 8 | if (err.hasOwnProperty('description')) this.description = err.description; 9 | if (err.hasOwnProperty('lineNumber')) this.lineNumber = err.lineNumber; 10 | if (err.hasOwnProperty('fileName')) this.fileName = err.fileName; 11 | } 12 | AqlError.prototype = new Error(); 13 | AqlError.prototype.constructor = AqlError; 14 | AqlError.prototype.name = 'AqlError'; 15 | 16 | exports.AqlError = AqlError; 17 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /*jshint browserify: true */ 2 | /*globals console: false */ 3 | 'use strict'; 4 | var console = require('console'); 5 | var AqlError = require('./errors').AqlError; 6 | var assumptions = require('./assumptions'); 7 | var types = require('./types'); 8 | 9 | function QB(obj) { 10 | if (typeof obj === 'string') { 11 | return QB.str(obj); 12 | } 13 | if (obj === null || obj === undefined) { 14 | return new types.NullLiteral(); 15 | } 16 | if (typeof obj === 'object') { 17 | if (obj instanceof Date) { 18 | return types.autoCastToken(JSON.stringify(obj)); 19 | } 20 | if (obj instanceof Array) { 21 | return new types.ListLiteral(obj.map(QB)); 22 | } 23 | var result = {}; 24 | for (var key in obj) { 25 | if (obj.hasOwnProperty(key)) { 26 | result[JSON.stringify(key)] = QB(obj[key]); 27 | } 28 | } 29 | return new types.ObjectLiteral(result); 30 | } 31 | return types.autoCastToken(obj); 32 | } 33 | 34 | Object.keys(types._PartialStatement.prototype).forEach(function (key) { 35 | if (key === 'constructor') return; 36 | QB[key] = types._PartialStatement.prototype[key].bind(null); 37 | }); 38 | 39 | Object.keys(types._Expression.prototype).forEach(function (key) { 40 | if (key === 'constructor' || key === 'then') return; 41 | QB[key] = function () { 42 | return types._Expression.prototype[key].apply( 43 | types.autoCastToken(arguments[0]), 44 | Array.prototype.slice.call(arguments, 1) 45 | ); 46 | }; 47 | }); 48 | 49 | QB.if = function (cond, then, otherwise) { 50 | return types.autoCastToken(cond).then(then).else(otherwise); 51 | }; 52 | QB.bool = function (value) { 53 | return new types.BooleanLiteral(value); 54 | }; 55 | QB.num = function (value) { 56 | return new types.NumberLiteral(value); 57 | }; 58 | QB.int = function (value) { 59 | return new types.IntegerLiteral(value); 60 | }; 61 | QB.str = function (value) { 62 | return new types.StringLiteral(value); 63 | }; 64 | QB.list = function (arr) { 65 | return new types.ListLiteral(arr); 66 | }; 67 | QB.obj = function (obj) { 68 | return new types.ObjectLiteral(obj); 69 | }; 70 | QB.ref = function (value) { 71 | if (types.Identifier.re.exec(value)) { 72 | return new types.Identifier(value); 73 | } 74 | return new types.SimpleReference(value); 75 | }; 76 | QB.expr = function (value) { 77 | return new types.RawExpression(value); 78 | }; 79 | QB.fn = function (functionName, arity) { 80 | if (typeof arity === 'number') { 81 | arity = [arity]; 82 | } 83 | return function () { 84 | var args = Array.prototype.slice.call(arguments), valid, i; 85 | if (arity) { 86 | valid = false; 87 | for (i = 0; !valid && i < arity.length; i++) { 88 | if (typeof arity[i] === 'number') { 89 | if (args.length === arity[i]) { 90 | valid = true; 91 | } 92 | } else if ( 93 | Object.prototype.toString.call(arity[i]) === '[object Array]' && 94 | args.length >= arity[i][0] && args.length <= arity[i][1] 95 | ) { 96 | valid = true; 97 | } 98 | } 99 | if (!valid) { 100 | throw new AqlError( 101 | 'Invalid number of arguments for function ' + 102 | functionName + ': ' + args.length 103 | ); 104 | } 105 | } 106 | return new types.FunctionCall(functionName, args); 107 | }; 108 | }; 109 | 110 | function deprecateAqlFunction(fn, functionName) { 111 | return function () { 112 | console.warn('The AQL function ' + functionName + ' is deprecated!'); 113 | return fn.apply(this, arguments); 114 | }; 115 | } 116 | 117 | Object.keys(assumptions.builtins).forEach(function (key) { 118 | QB[key] = QB.fn(key, assumptions.builtins[key]); 119 | if (assumptions.deprecatedBuiltins.indexOf(key) !== -1) { 120 | QB[key] = deprecateAqlFunction(QB[key], key); 121 | } 122 | }); 123 | 124 | module.exports = QB; 125 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aqb", 3 | "version": "2.1.0", 4 | "description": "ArangoDB AQL query builder.", 5 | "main": "index.js", 6 | "keywords": [ 7 | "arangodb", 8 | "aql", 9 | "nosql", 10 | "query" 11 | ], 12 | "files": [ 13 | "*.js", 14 | "package.json", 15 | "README.md", 16 | "LICENSE" 17 | ], 18 | "scripts": { 19 | "test": "mocha -t 30000 --growl -R spec test test/**", 20 | "cover": "istanbul cover --report lcov _mocha -- -t 30000 -R spec", 21 | "coveralls": "npm run cover && cat ./coverage/lcov.info | coveralls ; rm -rf ./coverage", 22 | "dist": "npm run bundle && npm run minify", 23 | "bundle": "browserify -t dekeywordify -u console -s aqb ./index.js > dist/aqb.js", 24 | "minify": "uglifyjs dist/aqb.js > dist/aqb.min.js" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "git://github.com/arangodb/aqbjs.git" 29 | }, 30 | "author": "Alan Plum ", 31 | "license": "APACHE-2.0", 32 | "bugs": { 33 | "url": "https://github.com/arangodb/aqbjs/issues" 34 | }, 35 | "homepage": "https://github.com/arangodb/aqbjs", 36 | "devDependencies": { 37 | "browserify": "^7.0.2", 38 | "coveralls": "^2.11.2", 39 | "dekeywordify": "^0.2.1", 40 | "expect.js": "^0.3.1", 41 | "istanbul": "^0.3.5", 42 | "mocha": "^2.1.0", 43 | "uglify-js": "^2.4.15" 44 | }, 45 | "dependencies": {} 46 | } 47 | -------------------------------------------------------------------------------- /test/auto-cast.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true, evil: true, -W014: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../types'), 6 | AqlError = require('../errors').AqlError, 7 | autoCastToken = types.autoCastToken; 8 | 9 | function each(examples, fn) { 10 | for (var i = 0; i < examples.length; i++) { 11 | fn(examples[i]); 12 | } 13 | } 14 | 15 | describe('autoCastToken', function () { 16 | it('null -> NullLiteral', function () { 17 | var result = autoCastToken(null); 18 | expect(result).to.be.a(types.NullLiteral); 19 | }); 20 | it('undefined -> NullLiteral', function () { 21 | var result = autoCastToken(undefined); 22 | expect(result).to.be.a(types.NullLiteral); 23 | }); 24 | it('int -> IntegerLiteral', function () { 25 | each([0, 1, 3, 23, '23', '+23', '-23'], function (value) { 26 | var result = autoCastToken(value); 27 | expect(result).to.be.an(types.IntegerLiteral); 28 | expect(result._value).to.equal(Number(value)); 29 | }); 30 | }); 31 | it('float -> NumberLiteral', function () { 32 | each([1.1, 3.14, '1.1', '+1.1', '-1.1'], function (value) { 33 | var result = autoCastToken(value); 34 | expect(result).to.be.a(types.NumberLiteral); 35 | expect(result).not.to.be.an(types.IntegerLiteral); 36 | expect(result._value).to.equal(Number(value)); 37 | }); 38 | }); 39 | it('boolean -> BooleanLiteral', function () { 40 | each([true, false], function (value) { 41 | var result = autoCastToken(value); 42 | expect(result).to.be.a(types.BooleanLiteral); 43 | expect(result._value).to.equal(value); 44 | }); 45 | }); 46 | it('quoted string -> StringLiteral', function () { 47 | each(['"lol"', '""', '" "'], function (value) { 48 | var result = autoCastToken(value); 49 | expect(result).to.be.a(types.StringLiteral); 50 | expect(result._value).to.equal(value.slice(1, -1)); 51 | }); 52 | }); 53 | it('range string -> RangeExpression', function () { 54 | each(['0..1', '2..3', '1000..5'], function (value) { 55 | var result = autoCastToken(value); 56 | expect(result).to.be.a(types.RangeExpression); 57 | var val = value.split('..'); 58 | expect(result._start._value).to.equal(Number(val[0])); 59 | expect(result._end._value).to.equal(Number(val[1])); 60 | }); 61 | }); 62 | it('identifier string -> Identifier', function () { 63 | each(['hi', 'o_hi_there', 'oHiMark'], function (value) { 64 | var result = autoCastToken(value); 65 | expect(result).to.be.an(types.Identifier); 66 | expect(result._value).to.equal(value); 67 | }); 68 | }); 69 | it('simple reference string -> SimpleReference', function () { 70 | each(['hi.mark', 'o.hi.there', 'oHi.Mark'], function (value) { 71 | var result = autoCastToken(value); 72 | expect(result).to.be.a(types.SimpleReference); 73 | expect(result._value).to.equal(value); 74 | }); 75 | }); 76 | it('ArangoCollection -> Identifier', function () { 77 | function ArangoCollection(name) { 78 | this.name = function () {return name;}; 79 | } 80 | each(['hi', 'o_hi_there', 'oHiMark'], function (value) { 81 | var result = autoCastToken(new ArangoCollection(value)); 82 | expect(result).to.be.an(types.Identifier); 83 | expect(result._value).to.equal(value); 84 | }); 85 | }); 86 | it('Array -> ListLiteral', function () { 87 | var result = autoCastToken([123]); 88 | expect(result).to.be.a(types.ListLiteral); 89 | expect(result._value[0]._value).to.equal(123); 90 | }); 91 | it('Object -> ObjectLiteral', function () { 92 | var result = autoCastToken({x: 123}); 93 | expect(result).to.be.a(types.ObjectLiteral); 94 | expect(result._value.x._value).to.equal(123); 95 | }); 96 | it('NaN throws an error', function () { 97 | expect(function () { 98 | autoCastToken(0 / 0); 99 | }).throwException(function (e) { 100 | expect(e).to.be.an(AqlError); 101 | }); 102 | }); 103 | it('Infinity throws an error', function () { 104 | expect(function () { 105 | autoCastToken(Infinity); 106 | }).throwException(function (e) { 107 | expect(e).to.be.an(AqlError); 108 | }); 109 | }); 110 | it('Negative Infinity throws an error', function () { 111 | expect(function () { 112 | autoCastToken(-Infinity); 113 | }).throwException(function (e) { 114 | expect(e).to.be.an(AqlError); 115 | }); 116 | }); 117 | }); 118 | -------------------------------------------------------------------------------- /test/examples.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true, evil: true, -W014: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | qb = require('../'); 6 | 7 | function example(aql, fn) { 8 | it(aql, function () { 9 | expect(fn().toAQL()).to.equal(aql); 10 | }); 11 | } 12 | 13 | describe('Query Builder examples', function () { 14 | example( 15 | 'FOR my IN mycollection RETURN my._key', 16 | function (aql) { 17 | return qb 18 | .for('my').in('mycollection') 19 | .return('my._key'); 20 | } 21 | ); 22 | 23 | example( 24 | 'RETURN "this will be returned"', 25 | function (aql) { 26 | return qb 27 | .return('"this will be returned"'); 28 | } 29 | ); 30 | 31 | example( 32 | 'FOR year IN [2011, 2012, 2013] ' 33 | + 'FOR quarter IN [1, 2, 3, 4] ' 34 | + 'RETURN {' 35 | + 'y: year, ' 36 | + 'q: quarter, ' 37 | + 'nice: CONCAT(TO_STRING(quarter), "/", TO_STRING(year))' 38 | + '}', 39 | function () { 40 | return qb 41 | .for('year').in([2011, 2012, 2013]) 42 | .for('quarter').in([1, 2, 3, 4]) 43 | .return({ 44 | y: 'year', 45 | q: 'quarter', 46 | nice: qb.CONCAT(qb.TO_STRING('quarter'), '"/"', qb.TO_STRING('year')) 47 | }); 48 | } 49 | ); 50 | 51 | example( 52 | 'FOR u IN users ' 53 | + 'UPDATE u WITH {' 54 | + 'gender: TRANSLATE(u.gender, {m: "male", f: "female"})' 55 | + '} IN users', 56 | function () { 57 | return qb.for('u').in('users') 58 | .update('u').with_({ 59 | gender: qb.TRANSLATE('u.gender', {m: '"male"', f: '"female"'}) 60 | }).in('users'); 61 | } 62 | ); 63 | 64 | example( 65 | 'FOR u IN users ' 66 | + 'FILTER (u.active == true) ' 67 | + 'UPDATE u WITH {numberOfLogins: 0} IN users', 68 | function () { 69 | return qb.for('u').in('users') 70 | .filter(qb.ref('u.active').eq(true)) 71 | .update('u').with_({numberOfLogins: 0}).in('users'); 72 | } 73 | ); 74 | 75 | example( 76 | 'FOR u IN users ' 77 | + 'FILTER (u.active == true) ' 78 | + 'UPDATE u WITH {' 79 | + 'numberOfLogins: (u.numberOfLogins + 1)' 80 | + '} IN users', 81 | function () { 82 | return qb.for('u').in('users') 83 | .filter(qb.ref('u.active').eq(true)) 84 | .update('u').with_({ 85 | numberOfLogins: qb.ref('u.numberOfLogins').add(1) 86 | }).in('users'); 87 | } 88 | ); 89 | 90 | example( 91 | 'FOR u IN users ' 92 | + 'FILTER (u.active == true) ' 93 | + 'UPDATE u WITH {' 94 | + 'lastLogin: DATE_NOW(), ' 95 | + 'numberOfLogins: (HAS(u, "numberOfLogins") ? (u.numberOfLogins + 1) : 1)' 96 | + '} IN users', 97 | function () { 98 | return qb.for('u').in('users') 99 | .filter(qb.ref('u.active').eq(true)) 100 | .update('u').with_({ 101 | lastLogin: qb.DATE_NOW(), 102 | numberOfLogins: ( 103 | qb.HAS('u', '"numberOfLogins"') 104 | .then(qb.ref('u.numberOfLogins').add(1)) 105 | .else_(1) 106 | ) 107 | }).in('users'); 108 | } 109 | ); 110 | 111 | example( 112 | 'FOR u IN users ' 113 | + 'REPLACE u IN backup', 114 | function () { 115 | return qb.for('u').in('users') 116 | .replace('u').in('backup'); 117 | } 118 | ); 119 | 120 | example( 121 | 'FOR u IN users ' 122 | + 'REPLACE u IN backup OPTIONS {ignoreErrors: true}', 123 | function () { 124 | return qb.for('u').in('users') 125 | .replace('u').in('backup').options({ignoreErrors: true}); 126 | } 127 | ); 128 | 129 | example( 130 | 'FOR u IN users ' 131 | + 'FILTER (((u.active == true) && (u.age >= 35)) && (u.age <= 37)) ' 132 | + 'REMOVE u IN users', 133 | function () { 134 | return qb.for('u').in('users') 135 | .filter( 136 | qb.ref('u.active').eq(true) 137 | .and(qb.ref('u.age').gte(35)) 138 | .and(qb.ref('u.age').lte(37)) 139 | ) 140 | .remove('u').in('users'); 141 | } 142 | ); 143 | 144 | example( 145 | 'FOR i IN 1..1000 ' 146 | + 'INSERT {' 147 | + 'id: (100000 + i), ' 148 | + 'age: (18 + FLOOR((RAND() * 25))), ' 149 | + 'name: CONCAT(test, TO_STRING(i)), ' 150 | + 'active: false, ' 151 | + 'gender: (((i % 2) == 0) ? "male" : "female")' 152 | + '} INTO users', 153 | function () { 154 | return qb.for('i').in(qb.range(1, 1000)) 155 | .insert({ 156 | id: qb(100000).add('i'), 157 | age: qb(18).add(qb.FLOOR(qb.RAND().times(25))), 158 | name: qb.CONCAT('test', qb.TO_STRING('i')), 159 | active: false, 160 | gender: ( 161 | qb.ref('i').mod(2).eq(0) 162 | .then('"male"') 163 | .else_('"female"') 164 | ) 165 | }).into('users'); 166 | } 167 | ); 168 | 169 | example( 170 | 'FOR u IN users ' 171 | + 'INSERT u INTO backup', 172 | function () { 173 | return qb.for('u').in('users') 174 | .insert('u').into('backup'); 175 | } 176 | ); 177 | 178 | example( 179 | 'FOR u IN users ' 180 | + 'LIMIT 0, 3 ' 181 | + 'RETURN {users: {' 182 | + 'isActive: (u.active ? "yes" : "no"), ' 183 | + 'name: u.name' 184 | +'}}', 185 | function () { 186 | return qb.for('u').in('users') 187 | .limit(0, 3) 188 | .return({ 189 | users: { 190 | isActive: qb.ref('u.active').then('"yes"').else_('"no"'), 191 | name: 'u.name' 192 | } 193 | }); 194 | } 195 | ); 196 | 197 | example( 198 | 'FOR u IN users ' 199 | + 'FILTER ((u.active == true) && (u.age >= 30)) ' 200 | + 'SORT u.age DESC ' 201 | + 'LIMIT 0, 5 ' 202 | + 'RETURN {age: u.age, name: u.name}', 203 | function () { 204 | return qb.for('u').in('users') 205 | .filter( 206 | qb.ref('u.active').eq(true) 207 | .and(qb.ref('u.age').gte(30)) 208 | ) 209 | .sort('u.age', 'DESC') 210 | .limit(0, 5) 211 | .return({ 212 | age: 'u.age', 213 | name: 'u.name' 214 | }); 215 | } 216 | ); 217 | 218 | example( 219 | 'FOR u IN users ' 220 | + 'FILTER (u.active == true) ' 221 | + 'LIMIT 0, 4 ' 222 | + 'FOR f IN relations ' 223 | + 'FILTER ((f.type == "friend") && (f.from == u.id)) ' 224 | + 'RETURN {user: u.name, friendId: f.to}', 225 | function () { 226 | return qb.for('u').in('users') 227 | .filter(qb.ref('u.active').eq(true)) 228 | .limit(0, 4) 229 | .for('f').in('relations') 230 | .filter( 231 | qb.ref('f.type').eq('"friend"') 232 | .and(qb.ref('f.from').eq('u.id')) 233 | ) 234 | .return({ 235 | user: 'u.name', 236 | friendId: 'f.to' 237 | }); 238 | } 239 | ); 240 | 241 | example( 242 | 'FOR u IN users ' 243 | + 'FILTER (u.active == true) ' 244 | + 'LIMIT 0, 4 ' 245 | + 'RETURN {' 246 | + 'user: u.name, ' 247 | + 'friendIds: (' 248 | + 'FOR f IN relations ' 249 | + 'FILTER ((f.from == u.id) && (f.type == "friend")) ' 250 | + 'RETURN f.to' 251 | + ')' 252 | + '}', 253 | function () { 254 | return qb.for('u').in('users') 255 | .filter(qb.ref('u.active').eq(true)) 256 | .limit(0, 4) 257 | .return({ 258 | user: 'u.name', 259 | friendIds: qb.for('f').in('relations').filter( 260 | qb.ref('f.from').eq('u.id') 261 | .and(qb.ref('f.type').eq('"friend"')) 262 | ).return('f.to') 263 | }); 264 | } 265 | ); 266 | 267 | example( 268 | 'FOR u IN users ' 269 | + 'FILTER (u.active == true) ' 270 | + 'LIMIT 0, 4 ' 271 | + 'RETURN {' 272 | + 'user: u.name, ' 273 | + 'friendIds: (' 274 | + 'FOR f IN relations ' 275 | + 'FILTER ((f.from == u.id) && (f.type == "friend")) ' 276 | + 'FOR u2 IN users ' 277 | + 'FILTER (f.to == u2.id) ' 278 | + 'RETURN u2.name' 279 | + ')' 280 | + '}', 281 | function () { 282 | return qb.for('u').in('users') 283 | .filter(qb.ref('u.active').eq(true)) 284 | .limit(0, 4) 285 | .return({ 286 | user: 'u.name', 287 | friendIds: qb.for('f').in('relations').filter( 288 | qb.ref('f.from').eq('u.id') 289 | .and(qb.ref('f.type').eq('"friend"')) 290 | ).for('u2').in('users').filter( 291 | qb.ref('f.to').eq('u2.id') 292 | ).return('u2.name') 293 | }); 294 | } 295 | ); 296 | 297 | example( 298 | 'FOR u IN users ' 299 | + 'FILTER (u.active == true) ' 300 | + 'COLLECT age = u.age INTO usersByAge ' 301 | + 'SORT age DESC ' 302 | + 'LIMIT 0, 5 ' 303 | + 'RETURN {age: age, users: usersByAge[*].u.name}', 304 | function () { 305 | return qb.for('u').in('users') 306 | .filter(qb.ref('u.active').eq(true)) 307 | .collect({age: 'u.age'}).into('usersByAge') 308 | .sort('age', 'DESC').limit(0, 5) 309 | .return({ 310 | age: 'age', 311 | users: 'usersByAge[*].u.name' 312 | }); 313 | } 314 | ); 315 | 316 | example( 317 | 'FOR u IN users ' 318 | + 'FILTER (u.active == true) ' 319 | + 'COLLECT age = u.age INTO usersByAge ' 320 | + 'SORT age DESC ' 321 | + 'LIMIT 0, 5 ' 322 | + 'RETURN {age: age, users: (' 323 | + 'FOR temp IN usersByAge ' 324 | + 'RETURN temp.u.name' 325 | + ')}', 326 | function () { 327 | return qb.for('u').in('users') 328 | .filter(qb.ref('u.active').eq(true)) 329 | .collect({age: 'u.age'}).into('usersByAge') 330 | .sort('age', 'DESC').limit(0, 5) 331 | .return({ 332 | age: 'age', 333 | users: qb.for('temp').in('usersByAge').return('temp.u.name') 334 | }); 335 | } 336 | ); 337 | 338 | example( 339 | 'FOR u IN users ' 340 | + 'FILTER (u.active == true) ' 341 | + 'COLLECT ageGroup = (FLOOR((u.age / 5)) * 5), gender = u.gender INTO group ' 342 | + 'SORT ageGroup DESC ' 343 | + 'RETURN {ageGroup: ageGroup, gender: gender}', 344 | function () { 345 | return qb.for('u').in('users') 346 | .filter(qb.ref('u.active').eq(true)) 347 | .collect({ 348 | ageGroup: qb.FLOOR(qb.ref('u.age').div(5)).times(5), 349 | gender: 'u.gender' 350 | }).into('group') 351 | .sort('ageGroup', 'DESC') 352 | .return({ 353 | ageGroup: 'ageGroup', 354 | gender: 'gender' 355 | }); 356 | } 357 | ); 358 | 359 | example( 360 | 'FOR u IN users ' 361 | + 'FILTER (u.active == true) ' 362 | + 'COLLECT ageGroup = (FLOOR((u.age / 5)) * 5), gender = u.gender INTO group ' 363 | + 'SORT ageGroup DESC ' 364 | + 'RETURN {ageGroup: ageGroup, gender: gender, numUsers: LENGTH(group)}', 365 | function () { 366 | return qb.for('u').in('users') 367 | .filter(qb.ref('u.active').eq(true)) 368 | .collect({ 369 | ageGroup: qb.FLOOR(qb.ref('u.age').div(5)).times(5), 370 | gender: 'u.gender' 371 | }).into('group') 372 | .sort('ageGroup', 'DESC') 373 | .return({ 374 | ageGroup: 'ageGroup', 375 | gender: 'gender', 376 | numUsers: qb.LENGTH('group') 377 | }); 378 | } 379 | ); 380 | 381 | example( 382 | 'FOR u IN users ' 383 | + 'FILTER (u.active == true) ' 384 | + 'COLLECT ageGroup = (FLOOR((u.age / 5)) * 5) INTO group ' 385 | + 'LET numUsers = LENGTH(group) ' 386 | + 'FILTER (numUsers > 2) ' 387 | + 'SORT numUsers DESC ' 388 | + 'LIMIT 0, 3 ' 389 | + 'RETURN {ageGroup: ageGroup, numUsers: numUsers, users: group[*].u.name}', 390 | function () { 391 | return qb.for('u').in('users') 392 | .filter(qb.ref('u.active').eq(true)) 393 | .collect({ 394 | ageGroup: qb.FLOOR(qb.ref('u.age').div(5)).times(5) 395 | }).into('group') 396 | .let('numUsers', qb.LENGTH('group')) 397 | .filter(qb.ref('numUsers').gt(2)) 398 | .sort('numUsers', 'DESC') 399 | .limit(0, 3) 400 | .return({ 401 | ageGroup: 'ageGroup', 402 | numUsers: 'numUsers', 403 | users: 'group[*].u.name' 404 | }); 405 | } 406 | ); 407 | 408 | example( 409 | 'UPSERT {ip: "192.168.173.13"} ' 410 | + 'INSERT {ip: "192.168.173.13", name: "flittard"} ' 411 | + 'UPDATE {} ' 412 | + 'IN hosts', 413 | function () { 414 | return qb.upsert({ip: qb.str('192.168.173.13')}) 415 | .insert({ip: qb.str('192.168.173.13'), name: qb.str('flittard')}) 416 | .update({}) 417 | .in('hosts'); 418 | } 419 | ); 420 | 421 | example( 422 | 'UPSERT {ip: "192.168.173.13"} ' 423 | + 'INSERT {ip: "192.168.173.13", name: "flittard"} ' 424 | + 'UPDATE {} ' 425 | + 'IN hosts ' 426 | + 'LET isNewInstance = (`OLD` ? false : true) ' 427 | + 'RETURN {doc: `NEW`, isNewInstance: isNewInstance}', 428 | function () { 429 | return qb.upsert({ip: qb.str('192.168.173.13')}) 430 | .insert({ip: qb.str('192.168.173.13'), name: qb.str('flittard')}) 431 | .update({}) 432 | .in('hosts') 433 | .let('isNewInstance', qb.ref('OLD').then(false).else(true)) 434 | .return({doc: 'NEW', isNewInstance: 'isNewInstance'}); 435 | } 436 | ); 437 | 438 | example( 439 | 'foo[1][2][3]', 440 | function () { 441 | return qb.ref('foo').get(1, 2, 3); 442 | } 443 | ); 444 | }); -------------------------------------------------------------------------------- /test/query-builder.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true, evil: true, -W014: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../types'), 6 | AqlError = require('../errors').AqlError, 7 | QB = require('../'); 8 | 9 | function each(examples, fn) { 10 | for (var i = 0; i < examples.length; i++) { 11 | fn(examples[i]); 12 | } 13 | } 14 | 15 | describe('QB', function () { 16 | it('null -> NullLiteral', function () { 17 | var result = QB(null); 18 | expect(result).to.be.a(types.NullLiteral); 19 | }); 20 | it('undefined -> NullLiteral', function () { 21 | var result = QB(undefined); 22 | expect(result).to.be.a(types.NullLiteral); 23 | }); 24 | it('int -> IntegerLiteral', function () { 25 | each([0, 1, 3, 23], function (value) { 26 | var result = QB(value); 27 | expect(result).to.be.an(types.IntegerLiteral); 28 | expect(result._value).to.equal(Number(value)); 29 | }); 30 | }); 31 | it('int-like string -> StringLiteral', function () { 32 | each(['23', '+23', '-23'], function (value) { 33 | var result = QB(value); 34 | expect(result).to.be.a(types.StringLiteral); 35 | expect(result._value).to.equal(value); 36 | }); 37 | }); 38 | it('float -> NumberLiteral', function () { 39 | each([1.1, 3.14], function (value) { 40 | var result = QB(value); 41 | expect(result).to.be.a(types.NumberLiteral); 42 | expect(result).not.to.be.an(types.IntegerLiteral); 43 | expect(result._value).to.equal(Number(value)); 44 | }); 45 | }); 46 | it('float-like string -> StringLiteral', function () { 47 | each(['1.1', '+1.1', '-1.1'], function (value) { 48 | var result = QB(value); 49 | expect(result).to.be.a(types.StringLiteral); 50 | expect(result._value).to.equal(value); 51 | }); 52 | }); 53 | it('boolean -> BooleanLiteral', function () { 54 | each([true, false], function (value) { 55 | var result = QB(value); 56 | expect(result).to.be.a(types.BooleanLiteral); 57 | expect(result._value).to.equal(value); 58 | }); 59 | }); 60 | it('quoted string -> StringLiteral', function () { 61 | each(['"lol"', '""', '" "'], function (value) { 62 | var result = QB(value); 63 | expect(result).to.be.a(types.StringLiteral); 64 | expect(result._value).to.equal(value); 65 | }); 66 | }); 67 | it('range string -> StringLiteral', function () { 68 | each(['0..1', '2..3', '1000..5'], function (value) { 69 | var result = QB(value); 70 | expect(result).to.be.a(types.StringLiteral); 71 | expect(result._value).to.equal(value); 72 | }); 73 | }); 74 | it('identifier string -> StringLiteral', function () { 75 | each(['hi', 'o_hi_there', 'oHiMark'], function (value) { 76 | var result = QB(value); 77 | expect(result).to.be.a(types.StringLiteral); 78 | expect(result._value).to.equal(value); 79 | }); 80 | }); 81 | it('simple reference string -> StringLiteral', function () { 82 | each(['hi.mark', 'o.hi.there', 'oHi.Mark'], function (value) { 83 | var result = QB(value); 84 | expect(result).to.be.a(types.StringLiteral); 85 | expect(result._value).to.equal(value); 86 | }); 87 | }); 88 | it('Array -> ListLiteral', function () { 89 | var result = QB([123, 'hello']); 90 | expect(result).to.be.a(types.ListLiteral); 91 | expect(result._value[0]).to.be.an(types.IntegerLiteral); 92 | expect(result._value[0]._value).to.equal(123); 93 | expect(result._value[1]).to.be.a(types.StringLiteral); 94 | expect(result._value[1]._value).to.equal('hello'); 95 | }); 96 | it('Object -> ObjectLiteral', function () { 97 | var result = QB({x: 123, y: 'hello'}); 98 | expect(result).to.be.a(types.ObjectLiteral); 99 | expect(result._value['"x"']).to.be.an(types.IntegerLiteral); 100 | expect(result._value['"x"']._value).to.equal(123); 101 | expect(result._value['"y"']).to.be.a(types.StringLiteral); 102 | expect(result._value['"y"']._value).to.equal('hello'); 103 | }); 104 | it('NaN throws an error', function () { 105 | expect(function () { 106 | QB(0 / 0); 107 | }).throwException(function (e) { 108 | expect(e).to.be.an(AqlError); 109 | }); 110 | }); 111 | it('Infinity throws an error', function () { 112 | expect(function () { 113 | QB(Infinity); 114 | }).throwException(function (e) { 115 | expect(e).to.be.an(AqlError); 116 | }); 117 | }); 118 | it('Negative Infinity throws an error', function () { 119 | expect(function () { 120 | QB(-Infinity); 121 | }).throwException(function (e) { 122 | expect(e).to.be.an(AqlError); 123 | }); 124 | }); 125 | }); -------------------------------------------------------------------------------- /test/types/BinaryOperation.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | BinaryOperation = types.BinaryOperation, 7 | AqlError = require('../../errors').AqlError, 8 | isAqlError = function (e) { 9 | expect(e).to.be.an(AqlError); 10 | }; 11 | 12 | describe('BinaryOperation', function () { 13 | it('returns an expression', function () { 14 | var expr = new BinaryOperation('+', 'x', 'y'); 15 | expect(expr).to.be.an(types._Expression); 16 | expect(expr.toAQL).to.be.a('function'); 17 | }); 18 | it('accepts non-empty strings as operators', function () { 19 | var values = [ 20 | '-', 21 | '~', 22 | '+', 23 | 'not', 24 | 'nöis3', 25 | '$$ $$%§-äß', 26 | 'bad:bad:bad' 27 | ]; 28 | for (var i = 0; i < values.length; i++) { 29 | expect(new BinaryOperation(values[i], 'x', 'y').toAQL()).to.equal('x ' + values[i] + ' y'); 30 | } 31 | }); 32 | it('does not accept any other values as operators', function () { 33 | var values = [ 34 | '', 35 | new types.StringLiteral('for'), 36 | new types.RawExpression('for'), 37 | new types.SimpleReference('for'), 38 | new types.Keyword('for'), 39 | new types.NullLiteral(null), 40 | 42, 41 | true, 42 | function () {}, 43 | {}, 44 | [] 45 | ]; 46 | for (var i = 0; i < values.length; i++) { 47 | expect(function () {return new BinaryOperation(values[i], 'x', 'y');}).to.throwException(isAqlError); 48 | } 49 | }); 50 | it('auto-casts values', function () { 51 | var arr = [42, 'id', 'some.ref', '"hello"', false, null]; 52 | var ctors = [ 53 | types.IntegerLiteral, 54 | types.Identifier, 55 | types.SimpleReference, 56 | types.StringLiteral, 57 | types.BooleanLiteral, 58 | types.NullLiteral 59 | ]; 60 | for (var i = 0; i < arr.length; i++) { 61 | var op = new BinaryOperation('+', arr[i], arr[i]); 62 | expect(op._value1.constructor).to.equal(ctors[i]); 63 | expect(op._value2.constructor).to.equal(ctors[i]); 64 | } 65 | }); 66 | it('wraps Operation values in parentheses', function () { 67 | var op = new types._Operation(); 68 | op.toAQL = function () {return 'x';}; 69 | expect(new BinaryOperation('+', op, op).toAQL()).to.equal('(x) + (x)'); 70 | }); 71 | it('wraps Statement values in parentheses', function () { 72 | var st = new types._Statement(); 73 | st.toAQL = function () {return 'x';}; 74 | expect(new BinaryOperation('+', st, st).toAQL()).to.equal('(x) + (x)'); 75 | }); 76 | it('wraps PartialStatement values in parentheses', function () { 77 | var ps = new types._PartialStatement(); 78 | ps.toAQL = function () {return 'x';}; 79 | expect(new BinaryOperation('+', ps, ps).toAQL()).to.equal('(x) + (x)'); 80 | }); 81 | }); -------------------------------------------------------------------------------- /test/types/BooleanLiteral.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | BooleanLiteral = types.BooleanLiteral; 7 | 8 | describe('BooleanLiteral', function () { 9 | it('returns an expression', function () { 10 | var expr = new BooleanLiteral(true); 11 | expect(expr).to.be.an(types._Expression); 12 | expect(expr.toAQL).to.be.a('function'); 13 | }); 14 | it('wraps truthy values', function () { 15 | var values = [ 16 | 'a non-empty string', 17 | 42, 18 | true, 19 | {}, 20 | [], 21 | function () {} 22 | ]; 23 | for (var i = 0; i < values.length; i++) { 24 | expect(new BooleanLiteral(values[i]).toAQL()).to.equal('true'); 25 | } 26 | }); 27 | it('wraps falsey values', function () { 28 | var values = [ 29 | '', // an empty string 30 | 0, 31 | null, 32 | false, 33 | undefined 34 | ]; 35 | for (var i = 0; i < values.length; i++) { 36 | expect(new BooleanLiteral(values[i]).toAQL()).to.equal('false'); 37 | } 38 | }); 39 | it('clones BooleanLiteral instances', function () { 40 | var src = new BooleanLiteral(false), 41 | copy = new BooleanLiteral(src); 42 | expect(src.toAQL()).to.equal(copy.toAQL()); 43 | expect(src).not.to.equal(copy); 44 | }); 45 | }); -------------------------------------------------------------------------------- /test/types/CollectExpression.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | CollectExpression = types.CollectExpression, 7 | AqlError = require('../../errors').AqlError, 8 | isAqlError = function (e) { 9 | expect(e).to.be.an(AqlError); 10 | }; 11 | 12 | describe('CollectExpression', function () { 13 | it('returns a partial statement', function () { 14 | var expr = new CollectExpression(null, {x: 'y'}); 15 | expect(expr).to.be.a(types._PartialStatement); 16 | expect(expr.toAQL).to.be.a('function'); 17 | }); 18 | it('generates a COLLECT statement', function () { 19 | expect(new CollectExpression(null, {x: 'y'}).toAQL()).to.equal('COLLECT x = y'); 20 | }); 21 | it('auto-casts assignment values', function () { 22 | expect(new CollectExpression(null, {a: 42})._dfns._dfns[0][1].constructor).to.equal(types.IntegerLiteral); 23 | var dfns = [['a', 42], ['b', 'id'], ['c', 'some.ref'], ['d', '"hello"'], ['e', false], ['f', null]]; 24 | var ctors = [ 25 | types.IntegerLiteral, 26 | types.Identifier, 27 | types.SimpleReference, 28 | types.StringLiteral, 29 | types.BooleanLiteral, 30 | types.NullLiteral 31 | ]; 32 | var expr = new CollectExpression(null, dfns); 33 | for (var i = 0; i < dfns.length; i++) { 34 | expect(expr._dfns._dfns[i][1].constructor).to.equal(ctors[i]); 35 | } 36 | }); 37 | it('accepts array assignments', function () { 38 | expect(new CollectExpression(null, [['a', 23], ['b', 42]]).toAQL()).to.equal('COLLECT a = 23, b = 42'); 39 | }); 40 | it('does not accept empty assignments', function () { 41 | expect(function () {return new CollectExpression(null, {});}).to.throwException(isAqlError); 42 | }); 43 | it('does not accept non-object assignments', function () { 44 | var values = [ 45 | undefined, 46 | null, 47 | 42, 48 | false, 49 | 'hello', 50 | function () {} 51 | ]; 52 | for (var i = 0; i < values.length; i++) { 53 | expect(function () {return new CollectExpression(null, values[i]);}).to.throwException(isAqlError); 54 | } 55 | }); 56 | it('wraps Operation values in parentheses', function () { 57 | var op = new types._Operation(); 58 | op.toAQL = function () {return 'y';}; 59 | expect(new CollectExpression(null, {x: op}).toAQL()).to.equal('COLLECT x = (y)'); 60 | }); 61 | it('wraps Statement values in parentheses', function () { 62 | var st = new types._Statement(); 63 | st.toAQL = function () {return 'y';}; 64 | expect(new CollectExpression(null, {x: st}).toAQL()).to.equal('COLLECT x = (y)'); 65 | }); 66 | it('wraps PartialStatement values in parentheses', function () { 67 | var ps = new types._PartialStatement(); 68 | ps.toAQL = function () {return 'y';}; 69 | expect(new CollectExpression(null, {x: ps}).toAQL()).to.equal('COLLECT x = (y)'); 70 | }); 71 | it('converts preceding nodes to AQL', function () { 72 | var ps = new types._PartialStatement(); 73 | ps.toAQL = function () {return '$';}; 74 | expect(new CollectExpression(ps, {x: 'y'}).toAQL()).to.equal('$ COLLECT x = y'); 75 | }); 76 | describe('into(var)', function () { 77 | var expr = new CollectExpression(null, {x: 'y'}); 78 | var expr2 = expr.into('z'); 79 | it('returns a new CollectExpression', function () { 80 | expect(expr2).to.be.a(CollectExpression); 81 | expect(expr2.toAQL()).to.equal('COLLECT x = y INTO z'); 82 | }); 83 | describe('keep', function () { 84 | var expr3 = expr2.keep('a', 'b'); 85 | it('returns a new CollectExpression', function () { 86 | expect(expr3).to.be.a(CollectExpression); 87 | expect(expr3.toAQL()).to.equal('COLLECT x = y INTO z KEEP a, b'); 88 | }); 89 | it('expects at least one variable name', function () { 90 | expect(function () {expr2.keep();}).to.throwException(isAqlError); 91 | }); 92 | describe('options', function () { 93 | it('returns a new CollectExpression', function () { 94 | var optExpr = expr3.options({c: 'd'}); 95 | expect(optExpr).to.be.a(types.CollectExpression); 96 | expect(optExpr.toAQL()).to.equal('COLLECT x = y INTO z KEEP a, b OPTIONS {c: d}'); 97 | }); 98 | }); 99 | }); 100 | describe('options', function () { 101 | it('returns a new CollectExpression', function () { 102 | var optExpr = expr2.options({c: 'd'}); 103 | expect(optExpr).to.be.a(types.CollectExpression); 104 | expect(optExpr.toAQL()).to.equal('COLLECT x = y INTO z OPTIONS {c: d}'); 105 | }); 106 | }); 107 | }); 108 | describe('into(var, expr)', function () { 109 | var expr = new CollectExpression(null, {x: 'y'}); 110 | var expr2 = expr.into('z', {a: 'b'}); 111 | it('returns a new CollectExpression', function () { 112 | expect(expr2).to.be.a(CollectExpression); 113 | expect(expr2.toAQL()).to.equal('COLLECT x = y INTO z = {a: b}'); 114 | }); 115 | describe('options', function () { 116 | it('returns a new CollectExpression', function () { 117 | var optExpr = expr2.options({c: 'd'}); 118 | expect(optExpr).to.be.a(types.CollectExpression); 119 | expect(optExpr.toAQL()).to.equal('COLLECT x = y INTO z = {a: b} OPTIONS {c: d}'); 120 | }); 121 | }); 122 | }); 123 | describe('withCountInto(var)', function () { 124 | it('returns a new CollectWithCountIntoExpression', function () { 125 | var expr = new CollectExpression(null, {x: 'y'}); 126 | var expr2 = expr.withCountInto('z'); 127 | expect(expr2).to.be.a(types.CollectWithCountIntoExpression); 128 | expect(expr2.toAQL()).to.equal('COLLECT x = y WITH COUNT INTO z'); 129 | }); 130 | }); 131 | describe('options', function () { 132 | var expr = new CollectExpression(null, {x: 'y'}); 133 | it('returns a new CollectExpression', function () { 134 | var optExpr = expr.options({c: 'd'}); 135 | expect(optExpr).to.be.a(types.CollectExpression); 136 | expect(optExpr.toAQL()).to.equal('COLLECT x = y OPTIONS {c: d}'); 137 | }); 138 | }); 139 | }); -------------------------------------------------------------------------------- /test/types/CollectWithCountIntoExpression.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | CollectWithCountIntoExpression = types.CollectWithCountIntoExpression, 7 | AqlError = require('../../errors').AqlError, 8 | isAqlError = function (e) { 9 | expect(e).to.be.an(AqlError); 10 | }; 11 | 12 | describe('CollectWithCountIntoExpression', function () { 13 | it('returns a partial statement', function () { 14 | var expr = new CollectWithCountIntoExpression(null, null, 'z'); 15 | expect(expr).to.be.a(types._PartialStatement); 16 | expect(expr.toAQL).to.be.a('function'); 17 | }); 18 | it('generates a COLLECT statement', function () { 19 | expect(new CollectWithCountIntoExpression(null, null, 'z').toAQL()).to.equal('COLLECT WITH COUNT INTO z'); 20 | expect(new CollectWithCountIntoExpression(null, {x: 'y'}, 'z').toAQL()).to.equal('COLLECT x = y WITH COUNT INTO z'); 21 | }); 22 | it('auto-casts assignment values', function () { 23 | expect(new CollectWithCountIntoExpression(null, {a: 42}, 'z')._dfns._dfns[0][1].constructor).to.equal(types.IntegerLiteral); 24 | var dfns = [['a', 42], ['b', 'id'], ['c', 'some.ref'], ['d', '"hello"'], ['e', false], ['f', null]]; 25 | var ctors = [ 26 | types.IntegerLiteral, 27 | types.Identifier, 28 | types.SimpleReference, 29 | types.StringLiteral, 30 | types.BooleanLiteral, 31 | types.NullLiteral 32 | ]; 33 | var expr = new CollectWithCountIntoExpression(null, dfns, 'z'); 34 | for (var i = 0; i < dfns.length; i++) { 35 | expect(expr._dfns._dfns[i][1].constructor).to.equal(ctors[i]); 36 | } 37 | }); 38 | it('accepts array assignments', function () { 39 | expect(new CollectWithCountIntoExpression(null, [['a', 23], ['b', 42]], 'z').toAQL()).to.equal('COLLECT a = 23, b = 42 WITH COUNT INTO z'); 40 | }); 41 | it('accepts empty assignments', function () { 42 | var values = [ 43 | undefined, 44 | null, 45 | false 46 | ]; 47 | for (var i = 0; i < values.length; i++) { 48 | expect(new CollectWithCountIntoExpression(null, values[i], 'z').toAQL()).to.equal('COLLECT WITH COUNT INTO z'); 49 | } 50 | }); 51 | it('does not accept non-object assignments', function () { 52 | var values = [ 53 | 42, 54 | 'hello', 55 | function () {} 56 | ]; 57 | for (var i = 0; i < values.length; i++) { 58 | expect(function () {return new CollectWithCountIntoExpression(null, values[i]);}).to.throwException(isAqlError); 59 | } 60 | }); 61 | it('wraps Operation values in parentheses', function () { 62 | var op = new types._Operation(); 63 | op.toAQL = function () {return 'y';}; 64 | expect(new CollectWithCountIntoExpression(null, {x: op}, 'z').toAQL()).to.equal('COLLECT x = (y) WITH COUNT INTO z'); 65 | }); 66 | it('wraps Statement values in parentheses', function () { 67 | var st = new types._Statement(); 68 | st.toAQL = function () {return 'y';}; 69 | expect(new CollectWithCountIntoExpression(null, {x: st}, 'z').toAQL()).to.equal('COLLECT x = (y) WITH COUNT INTO z'); 70 | }); 71 | it('wraps PartialStatement values in parentheses', function () { 72 | var ps = new types._PartialStatement(); 73 | ps.toAQL = function () {return 'y';}; 74 | expect(new CollectWithCountIntoExpression(null, {x: ps}, 'z').toAQL()).to.equal('COLLECT x = (y) WITH COUNT INTO z'); 75 | }); 76 | it('converts preceding nodes to AQL', function () { 77 | var ps = new types._PartialStatement(); 78 | ps.toAQL = function () {return '$';}; 79 | expect(new CollectWithCountIntoExpression(ps, {x: 'y'}, 'z').toAQL()).to.equal('$ COLLECT x = y WITH COUNT INTO z'); 80 | }); 81 | describe('options', function () { 82 | var expr = new CollectWithCountIntoExpression(null, {x: 'y'}, 'z'); 83 | it('returns a new CollectWithCountIntoExpression', function () { 84 | var optExpr = expr.options({c: 'd'}); 85 | expect(optExpr).to.be.a(types.CollectWithCountIntoExpression); 86 | expect(optExpr.toAQL()).to.equal('COLLECT x = y WITH COUNT INTO z OPTIONS {c: d}'); 87 | }); 88 | }); 89 | }); -------------------------------------------------------------------------------- /test/types/Definitions.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | Definitions = types._Definitions, 7 | AqlError = require('../../errors').AqlError, 8 | isAqlError = function (e) { 9 | expect(e).to.be.an(AqlError); 10 | }; 11 | 12 | describe('Definitions', function () { 13 | it('wraps object assignments', function () { 14 | var obj = {a: 1, b: 2, c: 3}; 15 | var expr = new Definitions(obj); 16 | expect(expr._dfns).to.be.an('array'); 17 | for (var i = 0; i < expr._dfns.length; i++) { 18 | expect(obj[expr._dfns[i][0]._value]).to.equal(expr._dfns[i][1]._value); 19 | } 20 | }); 21 | it('accepts array assignments', function () { 22 | expect(new Definitions([['a', 23], ['b', 42]]).toAQL()).to.equal('a = 23, b = 42'); 23 | }); 24 | it('does not accept empty assignments', function () { 25 | expect(function () {return new Definitions({});}).to.throwException(isAqlError); 26 | }); 27 | it('auto-casts assignment values', function () { 28 | expect(new Definitions({a: 42})._dfns[0][1].constructor).to.equal(types.IntegerLiteral); 29 | var dfns = [['a', 42], ['b', 'id'], ['c', 'some.ref'], ['d', '"hello"'], ['e', false], ['f', null]]; 30 | var ctors = [ 31 | types.IntegerLiteral, 32 | types.Identifier, 33 | types.SimpleReference, 34 | types.StringLiteral, 35 | types.BooleanLiteral, 36 | types.NullLiteral 37 | ]; 38 | var expr = new Definitions(dfns); 39 | for (var i = 0; i < dfns.length; i++) { 40 | expect(expr._dfns[i][1].constructor).to.equal(ctors[i]); 41 | } 42 | }); 43 | it('does not accept non-object assignments', function () { 44 | var values = [ 45 | undefined, 46 | null, 47 | 42, 48 | false, 49 | 'hello', 50 | function () {} 51 | ]; 52 | for (var i = 0; i < values.length; i++) { 53 | expect(function () {return new Definitions(values[i]);}).to.throwException(isAqlError); 54 | } 55 | }); 56 | it('clones Definitions instances', function () { 57 | var src = new Definitions({a: 1, b: 2, c: 3}), 58 | copy = new Definitions(src); 59 | expect(src.toAQL()).to.equal(copy.toAQL()); 60 | expect(src).not.to.equal(copy); 61 | }); 62 | it('treats keys as identifiers in AQL', function () { 63 | var expr = new Definitions({a: 'b'}); 64 | expect(expr.toAQL()).to.equal('a = b'); 65 | }); 66 | it('wraps Operation values in parentheses', function () { 67 | var op = new types._Operation(); 68 | op.toAQL = function () {return 'x';}; 69 | expect(new Definitions({an: op}).toAQL()).to.equal('an = (x)'); 70 | }); 71 | it('wraps Statement values in parentheses', function () { 72 | var st = new types._Statement(); 73 | st.toAQL = function () {return 'x';}; 74 | expect(new Definitions({an: st}).toAQL()).to.equal('an = (x)'); 75 | }); 76 | it('wraps PartialStatement values in parentheses', function () { 77 | var ps = new types._PartialStatement(); 78 | ps.toAQL = function () {return 'x';}; 79 | expect(new Definitions({an: ps}).toAQL()).to.equal('an = (x)'); 80 | }); 81 | }); -------------------------------------------------------------------------------- /test/types/FilterExpression.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | FilterExpression = types.FilterExpression; 7 | 8 | describe('FilterExpression', function () { 9 | it('returns a partial statement', function () { 10 | var expr = new FilterExpression(null, 'x'); 11 | expect(expr).to.be.a(types._PartialStatement); 12 | expect(expr.toAQL).to.be.a('function'); 13 | }); 14 | it('generates a FILTER statement', function () { 15 | expect(new FilterExpression(null, 'x').toAQL()).to.equal('FILTER x'); 16 | }); 17 | it('auto-casts values', function () { 18 | var arr = [42, 'id', 'some.ref', '"hello"', false, null]; 19 | var ctors = [ 20 | types.IntegerLiteral, 21 | types.Identifier, 22 | types.SimpleReference, 23 | types.StringLiteral, 24 | types.BooleanLiteral, 25 | types.NullLiteral 26 | ]; 27 | for (var i = 0; i < arr.length; i++) { 28 | expect(new FilterExpression(null, arr[i])._expr.constructor).to.equal(ctors[i]); 29 | } 30 | }); 31 | it('wraps Operation values in parentheses', function () { 32 | var op = new types._Operation(); 33 | op.toAQL = function () {return 'x';}; 34 | expect(new FilterExpression(null, op).toAQL()).to.equal('FILTER (x)'); 35 | }); 36 | it('wraps Statement values in parentheses', function () { 37 | var st = new types._Statement(); 38 | st.toAQL = function () {return 'x';}; 39 | expect(new FilterExpression(null, st).toAQL()).to.equal('FILTER (x)'); 40 | }); 41 | it('wraps PartialStatement values in parentheses', function () { 42 | var ps = new types._PartialStatement(); 43 | ps.toAQL = function () {return 'x';}; 44 | expect(new FilterExpression(null, ps).toAQL()).to.equal('FILTER (x)'); 45 | }); 46 | it('converts preceding nodes to AQL', function () { 47 | var ps = new types._PartialStatement(); 48 | ps.toAQL = function () {return '$';}; 49 | expect(new FilterExpression(ps, 'x').toAQL()).to.equal('$ FILTER x'); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /test/types/ForExpression.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | ForExpression = types.ForExpression, 7 | AqlError = require('../../errors').AqlError, 8 | isAqlError = function (e) { 9 | expect(e).to.be.an(AqlError); 10 | }; 11 | 12 | describe('ForExpression', function () { 13 | it('returns a statement', function () { 14 | var expr = new ForExpression(null, 'x', 'y'); 15 | expect(expr).to.be.a(types._PartialStatement); 16 | expect(expr.toAQL).to.be.a('function'); 17 | }); 18 | it('generates a FOR statement', function () { 19 | expect(new ForExpression(null, 'x', 'y').toAQL()).to.equal('FOR x IN y'); 20 | }); 21 | it('wraps well-formed strings as variable names', function () { 22 | var values = [ 23 | '_', 24 | '_x', 25 | 'all_lower_case', 26 | 'snakeCaseAlso', 27 | 'CamelCaseHere', 28 | 'ALL_UPPER_CASE', 29 | '__cRaZy__' 30 | ]; 31 | for (var i = 0; i < values.length; i++) { 32 | expect(new ForExpression(null, values[i], 'y')._varname.toAQL()).to.equal(values[i]); 33 | } 34 | }); 35 | it('does not accept malformed strings as variable names', function () { 36 | var values = [ 37 | '', 38 | '-x', 39 | 'also bad', 40 | 'überbad', 41 | 'spaß' 42 | ]; 43 | for (var i = 0; i < values.length; i++) { 44 | expect(function () {return new ForExpression(null, values[i], 'y');}).to.throwException(isAqlError); 45 | } 46 | }); 47 | it('does not accept any other values as variable names', function () { 48 | var values = [ 49 | new types.StringLiteral('for'), 50 | new types.RawExpression('for'), 51 | new types.SimpleReference('for'), 52 | new types.Keyword('for'), 53 | new types.NullLiteral(null), 54 | 42, 55 | true, 56 | function () {}, 57 | {}, 58 | [] 59 | ]; 60 | for (var i = 0; i < values.length; i++) { 61 | expect(function () {return new ForExpression(null, values[i], 'y');}).to.throwException(isAqlError); 62 | } 63 | }); 64 | it('auto-casts expressions', function () { 65 | var arr = [42, 'id', 'some.ref', '"hello"', false, null]; 66 | var ctors = [ 67 | types.IntegerLiteral, 68 | types.Identifier, 69 | types.SimpleReference, 70 | types.StringLiteral, 71 | types.BooleanLiteral, 72 | types.NullLiteral 73 | ]; 74 | for (var i = 0; i < arr.length; i++) { 75 | expect(new ForExpression(null, 'x', arr[i])._expr.constructor).to.equal(ctors[i]); 76 | } 77 | }); 78 | it('wraps Operation expressions in parentheses', function () { 79 | var op = new types._Operation(); 80 | op.toAQL = function () {return 'y';}; 81 | expect(new ForExpression(null, 'x', op).toAQL()).to.equal('FOR x IN (y)'); 82 | }); 83 | it('wraps Statement expressions in parentheses', function () { 84 | var st = new types._Statement(); 85 | st.toAQL = function () {return 'y';}; 86 | expect(new ForExpression(null, 'x', st).toAQL()).to.equal('FOR x IN (y)'); 87 | }); 88 | it('wraps PartialStatement expressions in parentheses', function () { 89 | var ps = new types._PartialStatement(); 90 | ps.toAQL = function () {return 'y';}; 91 | expect(new ForExpression(null, 'x', ps).toAQL()).to.equal('FOR x IN (y)'); 92 | }); 93 | it('converts preceding nodes to AQL', function () { 94 | var ps = new types._PartialStatement(); 95 | ps.toAQL = function () {return '$';}; 96 | expect(new ForExpression(ps, 'x', 'y').toAQL()).to.equal('$ FOR x IN y'); 97 | }); 98 | }); -------------------------------------------------------------------------------- /test/types/FunctionCall.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | FunctionCall = types.FunctionCall, 7 | AqlError = require('../../errors').AqlError, 8 | isAqlError = function (e) { 9 | expect(e).to.be.an(AqlError); 10 | }; 11 | 12 | describe('FunctionCall', function () { 13 | it('returns an expression', function () { 14 | var expr = new FunctionCall('hello'); 15 | expect(expr).to.be.an(types._Expression); 16 | expect(expr.toAQL).to.be.a('function'); 17 | }); 18 | it('wraps well-formed strings as function names', function () { 19 | var values = [ 20 | '_', 21 | '_x', 22 | 'all_lower_case', 23 | 'snakeCaseAlso', 24 | 'CamelCaseHere', 25 | 'ALL_UPPER_CASE', 26 | '__cRaZy__', 27 | 'all_lower_case::__cRaZy__', 28 | 'snakeCaseAlso::__cRaZy__', 29 | 'CamelCaseHere::__cRaZy__', 30 | 'ALL_UPPER_CASE::__cRaZy__', 31 | '__cRaZy__::__cRaZy__', 32 | '__cRaZy__::__cRaZy__::__cRaZy__::__cRaZy__' 33 | ]; 34 | for (var i = 0; i < values.length; i++) { 35 | expect(new FunctionCall(values[i]).toAQL()).to.equal(values[i] + '()'); 36 | } 37 | }); 38 | it('does not accept malformed strings as function names', function () { 39 | var values = [ 40 | '', 41 | '-x', 42 | 'in-valid', 43 | 'also bad', 44 | 'überbad', 45 | 'spaß', 46 | 'not:good:either' 47 | ]; 48 | for (var i = 0; i < values.length; i++) { 49 | expect(function () {return new FunctionCall(values[i]);}).to.throwException(isAqlError); 50 | } 51 | }); 52 | it('does not accept any other values as function names', function () { 53 | var values = [ 54 | new types.StringLiteral('for'), 55 | new types.RawExpression('for'), 56 | new types.SimpleReference('for'), 57 | new types.Keyword('for'), 58 | new types.NullLiteral(null), 59 | 42, 60 | true, 61 | function () {}, 62 | {}, 63 | [] 64 | ]; 65 | for (var i = 0; i < values.length; i++) { 66 | expect(function () {return new FunctionCall(values[i]);}).to.throwException(isAqlError); 67 | } 68 | }); 69 | it('auto-casts arguments', function () { 70 | var arr = [42, 'id', 'some.ref', '"hello"', false, null]; 71 | var ctors = [ 72 | types.IntegerLiteral, 73 | types.Identifier, 74 | types.SimpleReference, 75 | types.StringLiteral, 76 | types.BooleanLiteral, 77 | types.NullLiteral 78 | ]; 79 | var expr = new FunctionCall('hello', arr); 80 | for (var i = 0; i < arr.length; i++) { 81 | expect(expr._args[i].constructor).to.equal(ctors[i]); 82 | } 83 | }); 84 | it('does not accept truthy non-array argument arrays', function () { 85 | var values = [ 86 | (function () {return arguments;}()), 87 | {0: 'a', 1: 'b', 2: 'c'}, 88 | new types.StringLiteral('abc'), 89 | 42, 90 | 'hello', 91 | /absurd/, 92 | function () {}, 93 | {} 94 | ]; 95 | for (var i = 0; i < values.length; i++) { 96 | expect(function () {return new FunctionCall('hello', values[i]);}).to.throwException(isAqlError); 97 | } 98 | }); 99 | }); -------------------------------------------------------------------------------- /test/types/Identifier.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | Identifier = types.Identifier, 7 | AqlError = require('../../errors').AqlError, 8 | isAqlError = function (e) { 9 | expect(e).to.be.an(AqlError); 10 | }; 11 | 12 | describe('Identifier', function () { 13 | it('returns an expression', function () { 14 | var expr = new Identifier('for'); 15 | expect(expr).to.be.an(types._Expression); 16 | expect(expr.toAQL).to.be.a('function'); 17 | }); 18 | it('clones Identifier instances', function () { 19 | var src = new Identifier('for'), 20 | copy = new Identifier(src); 21 | expect(src.toAQL()).to.equal(copy.toAQL()); 22 | expect(src).not.to.equal(copy); 23 | }); 24 | it('accepts ArangoCollection instances', function () { 25 | function ArangoCollection(name) { 26 | this._name = 'lol'; 27 | } 28 | ArangoCollection.prototype.name = function () { 29 | return this._name; 30 | }; 31 | var collection = new ArangoCollection('some_collection'), 32 | id = new Identifier(collection); 33 | expect(id.toAQL()).to.equal(collection.name()); 34 | }); 35 | it('wraps well-formed strings', function () { 36 | var values = [ 37 | '_', 38 | '_x', 39 | 'all_lower_case', 40 | 'snakeCaseAlso', 41 | 'CamelCaseHere', 42 | 'ALL_UPPER_CASE', 43 | '__cRaZy__' 44 | ]; 45 | for (var i = 0; i < values.length; i++) { 46 | expect(new Identifier(values[i]).toAQL()).to.equal(values[i]); 47 | } 48 | }); 49 | it('wraps values in backticks if necessary', function () { 50 | var values = [ 51 | 'for', 52 | 'RETURN', 53 | 'totally-radical' 54 | ]; 55 | for (var i = 0; i < values.length; i++) { 56 | expect(new Identifier(values[i]).toAQL()).to.equal('`' + values[i] + '`'); 57 | } 58 | }); 59 | it('does not accept malformed strings', function () { 60 | var values = [ 61 | '', 62 | '-x', 63 | 'also bad', 64 | 'überbad', 65 | 'spaß' 66 | ]; 67 | for (var i = 0; i < values.length; i++) { 68 | expect(function () {return new Identifier(values[i]);}).to.throwException(isAqlError); 69 | } 70 | }); 71 | it('does not accept any other values', function () { 72 | var values = [ 73 | new types.StringLiteral('for'), 74 | new types.RawExpression('for'), 75 | new types.SimpleReference('for'), 76 | new types.Keyword('for'), 77 | new types.NullLiteral(null), 78 | 42, 79 | true, 80 | function () {}, 81 | {}, 82 | [] 83 | ]; 84 | for (var i = 0; i < values.length; i++) { 85 | expect(function () {return new Identifier(values[i]);}).to.throwException(isAqlError); 86 | } 87 | }); 88 | }); -------------------------------------------------------------------------------- /test/types/InsertExpression.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | InsertExpression = types.InsertExpression, 7 | AqlError = require('../../errors').AqlError, 8 | isAqlError = function (e) { 9 | expect(e).to.be.an(AqlError); 10 | }; 11 | 12 | describe('InsertExpression', function () { 13 | it('returns a statement', function () { 14 | var expr = new InsertExpression(null, 'x', 'y'); 15 | expect(expr).to.be.a(types._PartialStatement); 16 | expect(expr.toAQL).to.be.a('function'); 17 | }); 18 | it('generates an INSERT statement', function () { 19 | expect(new InsertExpression(null, 'x', 'y').toAQL()).to.equal('INSERT x INTO y'); 20 | }); 21 | it('auto-casts expressions', function () { 22 | var arr = [42, 'id', 'some.ref', '"hello"', false, null]; 23 | var ctors = [ 24 | types.IntegerLiteral, 25 | types.Identifier, 26 | types.SimpleReference, 27 | types.StringLiteral, 28 | types.BooleanLiteral, 29 | types.NullLiteral 30 | ]; 31 | for (var i = 0; i < arr.length; i++) { 32 | expect(new InsertExpression(null, arr[i], 'y')._expr.constructor).to.equal(ctors[i]); 33 | } 34 | }); 35 | it('wraps Operation expressions in parentheses', function () { 36 | var op = new types._Operation(); 37 | op.toAQL = function () {return 'x';}; 38 | expect(new InsertExpression(null, op, 'y').toAQL()).to.equal('INSERT (x) INTO y'); 39 | }); 40 | it('wraps Statement expressions in parentheses', function () { 41 | var st = new types._Statement(); 42 | st.toAQL = function () {return 'x';}; 43 | expect(new InsertExpression(null, st, 'y').toAQL()).to.equal('INSERT (x) INTO y'); 44 | }); 45 | it('wraps PartialStatement expressions in parentheses', function () { 46 | var ps = new types._PartialStatement(); 47 | ps.toAQL = function () {return 'x';}; 48 | expect(new InsertExpression(null, ps, 'y').toAQL()).to.equal('INSERT (x) INTO y'); 49 | }); 50 | it('wraps well-formed strings as collection names', function () { 51 | var values = [ 52 | '_', 53 | '_x', 54 | 'all_lower_case', 55 | 'snakeCaseAlso', 56 | 'CamelCaseHere', 57 | 'ALL_UPPER_CASE', 58 | '__cRaZy__' 59 | ]; 60 | for (var i = 0; i < values.length; i++) { 61 | expect(new InsertExpression(null, 'x', values[i])._collection.toAQL()).to.equal(values[i]); 62 | } 63 | }); 64 | it('does not accept malformed strings as collection names', function () { 65 | var values = [ 66 | '', 67 | '-x', 68 | 'also bad', 69 | 'überbad', 70 | 'spaß' 71 | ]; 72 | for (var i = 0; i < values.length; i++) { 73 | expect(function () {return new InsertExpression(null, 'x', values[i]);}).to.throwException(isAqlError); 74 | } 75 | }); 76 | it('does not accept any other values as collection names', function () { 77 | var values = [ 78 | new types.StringLiteral('for'), 79 | new types.RawExpression('for'), 80 | new types.SimpleReference('for'), 81 | new types.Keyword('for'), 82 | new types.NullLiteral(null), 83 | 42, 84 | true, 85 | function () {}, 86 | {}, 87 | [] 88 | ]; 89 | for (var i = 0; i < values.length; i++) { 90 | expect(function () {return new InsertExpression(null, 'x', values[i]);}).to.throwException(isAqlError); 91 | } 92 | }); 93 | it('converts preceding nodes to AQL', function () { 94 | var ps = new types._PartialStatement(); 95 | ps.toAQL = function () {return '$';}; 96 | expect(new InsertExpression(ps, 'x', 'y').toAQL()).to.equal('$ INSERT x INTO y'); 97 | }); 98 | describe('options', function () { 99 | var expr = new InsertExpression(null, 'x', 'y'); 100 | it('returns a new InsertExpression', function () { 101 | var optExpr = expr.options({a: 'b'}); 102 | expect(optExpr).to.be.a(types.InsertExpression); 103 | expect(optExpr.toAQL()).to.equal('INSERT x INTO y OPTIONS {a: b}'); 104 | }); 105 | }); 106 | describe('returnNew', function () { 107 | var expr = new InsertExpression(null, 'x', 'y'); 108 | it('returns a LET RETURN NEW', function () { 109 | var rtrnExpr = expr.returnNew('a'); 110 | expect(rtrnExpr).to.be.a(types.ReturnExpression); 111 | expect(rtrnExpr._value._value).to.equal('a'); 112 | expect(rtrnExpr._prev).to.be.a(types.LetExpression); 113 | rtrnExpr._prev._prev = null; 114 | expect(rtrnExpr.toAQL()).to.equal('LET a = `NEW` RETURN a'); 115 | }); 116 | }); 117 | }); 118 | -------------------------------------------------------------------------------- /test/types/IntegerLiteral.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | IntegerLiteral = types.IntegerLiteral, 7 | NumberLiteral = types.NumberLiteral, 8 | AqlError = require('../../errors').AqlError, 9 | isAqlError = function (e) { 10 | expect(e).to.be.an(AqlError); 11 | }; 12 | 13 | describe('IntegerLiteral', function () { 14 | it('returns an expression', function () { 15 | var expr = new IntegerLiteral(1); 16 | expect(expr).to.be.an(types._Expression); 17 | expect(expr.toAQL).to.be.a('function'); 18 | }); 19 | it('wraps numeric integer values', function () { 20 | var values = [ 21 | [0, '0'], 22 | [42, '42'], 23 | [-23, '-23'], 24 | [false, '0'], 25 | [true, '1'], 26 | ['', '0'], 27 | [[], '0'], 28 | ['0xabcd', String(0xabcd)], 29 | ['0', '0'], 30 | ['42', '42'], 31 | ['1.0', '1'], 32 | ['-23', '-23'] 33 | ]; 34 | for (var i = 0; i < values.length; i++) { 35 | var n = new IntegerLiteral(values[i][0]); 36 | expect(n.toAQL()).to.equal(String(values[i][1])); 37 | } 38 | }); 39 | it('does not accept numeric non-integer values', function () { 40 | var values = [1.5, '1.5', -0.01, '-0.01']; 41 | for (var i = 0; i < values.length; i++) { 42 | expect(function () {return new IntegerLiteral(values[i]);}).to.throwException(isAqlError); 43 | } 44 | }); 45 | it('clones IntegerLiteral instances', function () { 46 | var src = new IntegerLiteral(42), 47 | copy = new IntegerLiteral(src); 48 | expect(src.toAQL()).to.equal(copy.toAQL()); 49 | expect(src).not.to.equal(copy); 50 | }); 51 | it('clones integer NumberLiteral instances', function () { 52 | var src = new NumberLiteral(42), 53 | copy = new IntegerLiteral(src); 54 | expect(src.toAQL()).to.equal(copy.toAQL()); 55 | expect(src).not.to.equal(copy); 56 | }); 57 | it('does not accept non-integer NumberLiteral instances', function () { 58 | var values = [new NumberLiteral(1.5), new NumberLiteral(-0.01)]; 59 | for (var i = 0; i < values.length; i++) { 60 | expect(function () {return new IntegerLiteral(values[i]);}).to.throwException(isAqlError); 61 | } 62 | }); 63 | it('does not accept NaN', function () { 64 | expect(function () {return new IntegerLiteral(NaN);}).to.throwException(isAqlError); 65 | }); 66 | it('does not accept Infinity', function () { 67 | expect(function () {return new IntegerLiteral(Infinity);}).to.throwException(isAqlError); 68 | }); 69 | it('does not accept non-numeric values', function () { 70 | var values = [ 71 | '0xabsurd', 72 | 'hello', 73 | /absurd/, 74 | function () {}, 75 | {0: 1}, 76 | [1, 2, 3], 77 | {} 78 | ]; 79 | for (var i = 0; i < values.length; i++) { 80 | expect(function () {return new IntegerLiteral(values[i]);}).to.throwException(isAqlError); 81 | } 82 | }); 83 | }); -------------------------------------------------------------------------------- /test/types/Keyword.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | Keyword = types.Keyword, 7 | AqlError = require('../../errors').AqlError, 8 | isAqlError = function (e) { 9 | expect(e).to.be.an(AqlError); 10 | }; 11 | 12 | describe('Keyword', function () { 13 | it('returns an expression', function () { 14 | var expr = new Keyword('for'); 15 | expect(expr).to.be.an(types._Expression); 16 | expect(expr.toAQL).to.be.a('function'); 17 | }); 18 | it('clones Keyword instances', function () { 19 | var src = new Keyword('for'), 20 | copy = new Keyword(src); 21 | expect(src.toAQL()).to.equal(copy.toAQL()); 22 | expect(src).not.to.equal(copy); 23 | }); 24 | it('wraps well-formed strings', function () { 25 | var values = [ 26 | '_', 27 | '_x', 28 | 'all_lower_case', 29 | 'snakeCaseAlso', 30 | 'CamelCaseHere', 31 | 'ALL_UPPER_CASE', 32 | '__cRaZy__' 33 | ]; 34 | for (var i = 0; i < values.length; i++) { 35 | expect(new Keyword(values[i]).toAQL()).to.equal(values[i].toUpperCase()); 36 | } 37 | }); 38 | it('does not accept malformed strings', function () { 39 | var values = [ 40 | '', 41 | '-x', 42 | 'in-valid', 43 | 'also bad', 44 | 'überbad', 45 | 'spaß' 46 | ]; 47 | for (var i = 0; i < values.length; i++) { 48 | expect(function () {return new Keyword(values[i]);}).to.throwException(isAqlError); 49 | } 50 | }); 51 | it('does not accept any other values', function () { 52 | var values = [ 53 | new types.StringLiteral('for'), 54 | new types.RawExpression('for'), 55 | new types.SimpleReference('for'), 56 | new types.Identifier('for'), 57 | new types.NullLiteral(null), 58 | 42, 59 | true, 60 | function () {}, 61 | {}, 62 | [] 63 | ]; 64 | for (var i = 0; i < values.length; i++) { 65 | expect(function () {return new Keyword(values[i]);}).to.throwException(isAqlError); 66 | } 67 | }); 68 | }); -------------------------------------------------------------------------------- /test/types/LetExpression.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | LetExpression = types.LetExpression, 7 | AqlError = require('../../errors').AqlError, 8 | isAqlError = function (e) { 9 | expect(e).to.be.an(AqlError); 10 | }; 11 | 12 | describe('LetExpression', function () { 13 | it('returns a partial statement', function () { 14 | var expr = new LetExpression(null, {x: 'y'}); 15 | expect(expr).to.be.a(types._PartialStatement); 16 | expect(expr.toAQL).to.be.a('function'); 17 | }); 18 | it('generates a LET statement', function () { 19 | expect(new LetExpression(null, {x: 'y'}).toAQL()).to.equal('LET x = y'); 20 | }); 21 | it('auto-casts assignment values', function () { 22 | expect(new LetExpression(null, {a: 42})._dfns._dfns[0][1].constructor).to.equal(types.IntegerLiteral); 23 | var dfns = [['a', 42], ['b', 'id'], ['c', 'some.ref'], ['d', '"hello"'], ['e', false], ['f', null]]; 24 | var ctors = [ 25 | types.IntegerLiteral, 26 | types.Identifier, 27 | types.SimpleReference, 28 | types.StringLiteral, 29 | types.BooleanLiteral, 30 | types.NullLiteral 31 | ]; 32 | var expr = new LetExpression(null, dfns); 33 | for (var i = 0; i < dfns.length; i++) { 34 | expect(expr._dfns._dfns[i][1].constructor).to.equal(ctors[i]); 35 | } 36 | }); 37 | it('accepts array assignments', function () { 38 | expect(new LetExpression(null, [['a', 23], ['b', 42]]).toAQL()).to.equal('LET a = 23, b = 42'); 39 | }); 40 | it('does not accept empty assignments', function () { 41 | expect(function () {return new LetExpression(null, {});}).to.throwException(isAqlError); 42 | }); 43 | it('does not accept non-object assignments', function () { 44 | var values = [ 45 | undefined, 46 | null, 47 | 42, 48 | false, 49 | 'hello', 50 | function () {} 51 | ]; 52 | for (var i = 0; i < values.length; i++) { 53 | expect(function () {return new LetExpression(null, values[i]);}).to.throwException(isAqlError); 54 | } 55 | }); 56 | it('wraps Operation values in parentheses', function () { 57 | var op = new types._Operation(); 58 | op.toAQL = function () {return 'y';}; 59 | expect(new LetExpression(null, {x: op}).toAQL()).to.equal('LET x = (y)'); 60 | }); 61 | it('wraps Statement values in parentheses', function () { 62 | var st = new types._Statement(); 63 | st.toAQL = function () {return 'y';}; 64 | expect(new LetExpression(null, {x: st}).toAQL()).to.equal('LET x = (y)'); 65 | }); 66 | it('wraps PartialStatement values in parentheses', function () { 67 | var ps = new types._PartialStatement(); 68 | ps.toAQL = function () {return 'y';}; 69 | expect(new LetExpression(null, {x: ps}).toAQL()).to.equal('LET x = (y)'); 70 | }); 71 | it('converts preceding nodes to AQL', function () { 72 | var ps = new types._PartialStatement(); 73 | ps.toAQL = function () {return '$';}; 74 | expect(new LetExpression(ps, {x: 'y'}).toAQL()).to.equal('$ LET x = y'); 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /test/types/LimitExpression.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | LimitExpression = types.LimitExpression; 7 | 8 | describe('LimitExpression', function () { 9 | it('returns a partial statement', function () { 10 | var expr = new LimitExpression(null, 'x', 'y'); 11 | expect(expr).to.be.a(types._PartialStatement); 12 | expect(expr.toAQL).to.be.a('function'); 13 | }); 14 | it('generates a LIMIT statement', function () { 15 | expect(new LimitExpression(null, 'x', 'y').toAQL()).to.equal('LIMIT x, y'); 16 | }); 17 | it('treats the offset as optional', function () { 18 | expect(new LimitExpression(null, 'x', 'y')._offset._value).to.equal('x'); 19 | expect(new LimitExpression(null, 'x', 'y')._count._value).to.equal('y'); 20 | expect(new LimitExpression(null, 'y')._count._value).to.equal('y'); 21 | }); 22 | it('auto-casts offsets', function () { 23 | var arr = [42, 'id', 'some.ref', '"hello"', false, null]; 24 | var ctors = [ 25 | types.IntegerLiteral, 26 | types.Identifier, 27 | types.SimpleReference, 28 | types.StringLiteral, 29 | types.BooleanLiteral, 30 | types.NullLiteral 31 | ]; 32 | for (var i = 0; i < arr.length; i++) { 33 | expect(new LimitExpression(null, arr[i], 'y')._offset.constructor).to.equal(ctors[i]); 34 | } 35 | }); 36 | it('wraps Operation offsets in parentheses', function () { 37 | var op = new types._Operation(); 38 | op.toAQL = function () {return 'x';}; 39 | expect(new LimitExpression(null, op, 'y').toAQL()).to.equal('LIMIT (x), y'); 40 | }); 41 | it('wraps Statement offsets in parentheses', function () { 42 | var st = new types._Statement(); 43 | st.toAQL = function () {return 'x';}; 44 | expect(new LimitExpression(null, st, 'y').toAQL()).to.equal('LIMIT (x), y'); 45 | }); 46 | it('wraps PartialStatement offsets in parentheses', function () { 47 | var ps = new types._PartialStatement(); 48 | ps.toAQL = function () {return 'x';}; 49 | expect(new LimitExpression(null, ps, 'y').toAQL()).to.equal('LIMIT (x), y'); 50 | }); 51 | it('auto-casts counts', function () { 52 | var arr = [42, 'id', 'some.ref', '"hello"', false, null]; 53 | var ctors = [ 54 | types.IntegerLiteral, 55 | types.Identifier, 56 | types.SimpleReference, 57 | types.StringLiteral, 58 | types.BooleanLiteral, 59 | types.NullLiteral 60 | ]; 61 | for (var i = 0; i < arr.length; i++) { 62 | expect(new LimitExpression(null, 'x', arr[i])._count.constructor).to.equal(ctors[i]); 63 | } 64 | }); 65 | it('wraps Operation counts in parentheses', function () { 66 | var op = new types._Operation(); 67 | op.toAQL = function () {return 'y';}; 68 | expect(new LimitExpression(null, 'x', op).toAQL()).to.equal('LIMIT x, (y)'); 69 | }); 70 | it('wraps Statement counts in parentheses', function () { 71 | var st = new types._Statement(); 72 | st.toAQL = function () {return 'y';}; 73 | expect(new LimitExpression(null, 'x', st).toAQL()).to.equal('LIMIT x, (y)'); 74 | }); 75 | it('wraps PartialStatement counts in parentheses', function () { 76 | var ps = new types._PartialStatement(); 77 | ps.toAQL = function () {return 'y';}; 78 | expect(new LimitExpression(null, 'x', ps).toAQL()).to.equal('LIMIT x, (y)'); 79 | }); 80 | it('converts preceding nodes to AQL', function () { 81 | var ps = new types._PartialStatement(); 82 | ps.toAQL = function () {return '$';}; 83 | expect(new LimitExpression(ps, 'x', 'y').toAQL()).to.equal('$ LIMIT x, y'); 84 | }); 85 | }); 86 | -------------------------------------------------------------------------------- /test/types/ListLiteral.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | ListLiteral = types.ListLiteral, 7 | AqlError = require('../../errors').AqlError, 8 | isAqlError = function (e) { 9 | expect(e).to.be.an(AqlError); 10 | }; 11 | 12 | describe('ListLiteral', function () { 13 | it('returns an expression', function () { 14 | var expr = new ListLiteral([]); 15 | expect(expr).to.be.an(types._Expression); 16 | expect(expr.toAQL).to.be.a('function'); 17 | }); 18 | it('clones ListLiteral instances', function () { 19 | var src = new ListLiteral([1, 2, 3]), 20 | copy = new ListLiteral(src); 21 | expect(src.toAQL()).to.equal(copy.toAQL()); 22 | expect(src).not.to.equal(copy); 23 | }); 24 | it('wraps arrays', function () { 25 | var arr = [1, 2, 3, 4]; 26 | var expr = new ListLiteral(arr); 27 | expect(expr._value).to.be.an(Array); 28 | expect(expr._value.length).to.equal(arr.length); 29 | }); 30 | it('auto-casts array values', function () { 31 | var arr = [42, 'id', 'some.ref', '"hello"', false, null]; 32 | var ctors = [ 33 | types.IntegerLiteral, 34 | types.Identifier, 35 | types.SimpleReference, 36 | types.StringLiteral, 37 | types.BooleanLiteral, 38 | types.NullLiteral 39 | ]; 40 | var expr = new ListLiteral(arr); 41 | for (var i = 0; i < arr.length; i++) { 42 | expect(expr._value[i].constructor).to.equal(ctors[i]); 43 | } 44 | }); 45 | it('does not accept non-array values', function () { 46 | var values = [ 47 | (function () {return arguments;}()), 48 | {0: 'a', 1: 'b', 2: 'c'}, 49 | new types.StringLiteral('abc'), 50 | 42, 51 | false, 52 | 'hello', 53 | /absurd/, 54 | function () {}, 55 | {} 56 | ]; 57 | for (var i = 0; i < values.length; i++) { 58 | expect(function () {return new ListLiteral(values[i]);}).to.throwException(isAqlError); 59 | } 60 | }); 61 | it('wraps Operation values in parentheses', function () { 62 | var op = new types._Operation(); 63 | op.toAQL = function () {return 'x';}; 64 | expect(new ListLiteral([op]).toAQL()).to.equal('[(x)]'); 65 | }); 66 | it('wraps Statement values in parentheses', function () { 67 | var st = new types._Statement(); 68 | st.toAQL = function () {return 'x';}; 69 | expect(new ListLiteral([st]).toAQL()).to.equal('[(x)]'); 70 | }); 71 | it('wraps PartialStatement values in parentheses', function () { 72 | var ps = new types._PartialStatement(); 73 | ps.toAQL = function () {return 'x';}; 74 | expect(new ListLiteral([ps]).toAQL()).to.equal('[(x)]'); 75 | }); 76 | }); -------------------------------------------------------------------------------- /test/types/NAryOperation.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | NAryOperation = types.NAryOperation, 7 | AqlError = require('../../errors').AqlError, 8 | isAqlError = function (e) { 9 | expect(e).to.be.an(AqlError); 10 | }; 11 | 12 | describe('NAryOperation', function () { 13 | it('returns an expression', function () { 14 | var expr = new NAryOperation('+', ['x', 'y']); 15 | expect(expr).to.be.an(types._Expression); 16 | expect(expr.toAQL).to.be.a('function'); 17 | }); 18 | it('accepts non-empty strings as operators', function () { 19 | var values = [ 20 | '-', 21 | '~', 22 | '+', 23 | 'not', 24 | 'nöis3', 25 | '$$ $$%§-äß', 26 | 'bad:bad:bad' 27 | ]; 28 | for (var i = 0; i < values.length; i++) { 29 | expect(new NAryOperation(values[i], ['x', 'y']).toAQL()).to.equal('x ' + values[i] + ' y'); 30 | } 31 | }); 32 | it('does not accept any other values as operators', function () { 33 | var values = [ 34 | '', 35 | new types.StringLiteral('for'), 36 | new types.RawExpression('for'), 37 | new types.SimpleReference('for'), 38 | new types.Keyword('for'), 39 | new types.NullLiteral(null), 40 | 42, 41 | true, 42 | function () {}, 43 | {}, 44 | [] 45 | ]; 46 | for (var i = 0; i < values.length; i++) { 47 | expect(function () {return new NAryOperation(values[i], ['x', 'y']);}).to.throwException(isAqlError); 48 | } 49 | }); 50 | it('auto-casts values', function () { 51 | var arr = [42, 'id', 'some.ref', '"hello"', false, null]; 52 | var ctors = [ 53 | types.IntegerLiteral, 54 | types.Identifier, 55 | types.SimpleReference, 56 | types.StringLiteral, 57 | types.BooleanLiteral, 58 | types.NullLiteral 59 | ]; 60 | for (var i = 0; i < arr.length; i++) { 61 | var op = new NAryOperation('+', [arr[i], arr[i]]); 62 | expect(op._values[0].constructor).to.equal(ctors[i]); 63 | expect(op._values[1].constructor).to.equal(ctors[i]); 64 | } 65 | }); 66 | it('allows an arbitrary number of values', function () { 67 | expect(new NAryOperation('+', [1]).toAQL()).to.equal('1'); 68 | expect(new NAryOperation('+', [1, 2]).toAQL()).to.equal('1 + 2'); 69 | expect(new NAryOperation('+', [1, 2, 3]).toAQL()).to.equal('1 + 2 + 3'); 70 | expect(new NAryOperation('+', [1, 2, 3, 4]).toAQL()).to.equal('1 + 2 + 3 + 4'); 71 | }); 72 | it('wraps Operation values in parentheses', function () { 73 | var op = new types._Operation(); 74 | op.toAQL = function () {return 'x';}; 75 | expect(new NAryOperation('+', [op, op]).toAQL()).to.equal('(x) + (x)'); 76 | }); 77 | it('wraps Statement values in parentheses', function () { 78 | var st = new types._Statement(); 79 | st.toAQL = function () {return 'x';}; 80 | expect(new NAryOperation('+', [st, st]).toAQL()).to.equal('(x) + (x)'); 81 | }); 82 | it('wraps PartialStatement values in parentheses', function () { 83 | var ps = new types._PartialStatement(); 84 | ps.toAQL = function () {return 'x';}; 85 | expect(new NAryOperation('+', [ps, ps]).toAQL()).to.equal('(x) + (x)'); 86 | }); 87 | }); -------------------------------------------------------------------------------- /test/types/NullLiteral.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | NullLiteral = types.NullLiteral, 7 | AqlError = require('../../errors').AqlError, 8 | isAqlError = function (e) { 9 | expect(e).to.be.an(AqlError); 10 | }; 11 | 12 | describe('NullLiteral', function () { 13 | it('returns an expression', function () { 14 | var expr = new NullLiteral(null); 15 | expect(expr).to.be.an(types._Expression); 16 | expect(expr.toAQL).to.be.a('function'); 17 | }); 18 | it('accepts null and wraps it as "null"', function () { 19 | expect(new NullLiteral(null).toAQL()).to.equal('null'); 20 | }); 21 | it('accepts undefined and wraps it as "null"', function () { 22 | expect(new NullLiteral(undefined).toAQL()).to.equal('null'); 23 | }); 24 | it('clones NullLiteral instances', function () { 25 | var src = new NullLiteral(null), 26 | copy = new NullLiteral(src); 27 | expect(src.toAQL()).to.equal(copy.toAQL()); 28 | expect(src).not.to.equal(copy); 29 | }); 30 | it('does not accept falsey values', function () { 31 | var values = [false, 0, '']; 32 | for (var i = 0; i < values.length; i++) { 33 | expect(function () {return new NullLiteral(values[i]);}).to.throwException(isAqlError); 34 | } 35 | }); 36 | it('does not accept any other values', function () { 37 | var values = [ 38 | 'a simple string', 39 | 42, 40 | true, 41 | {an: 'object', 'with': {things: 'in it'}}, 42 | ['an', 'array', '||', 2], 43 | function also() {return this;} 44 | ]; 45 | for (var i = 0; i < values.length; i++) { 46 | expect(function () {return new NullLiteral(values[i]);}).to.throwException(isAqlError); 47 | } 48 | }); 49 | }); -------------------------------------------------------------------------------- /test/types/NumberLiteral.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | NumberLiteral = types.NumberLiteral, 7 | AqlError = require('../../errors').AqlError, 8 | isAqlError = function (e) { 9 | expect(e).to.be.an(AqlError); 10 | }; 11 | 12 | describe('NumberLiteral', function () { 13 | it('returns an expression', function () { 14 | var expr = new NumberLiteral(1); 15 | expect(expr).to.be.an(types._Expression); 16 | expect(expr.toAQL).to.be.a('function'); 17 | }); 18 | it('wraps numeric values', function () { 19 | var values = [ 20 | [0, '0'], 21 | [42, '42'], 22 | [1.5, '1.5'], 23 | [-23, '-23'], 24 | [-0.01, '-0.01'], 25 | [false, '0'], 26 | [true, '1'], 27 | ['', '0'], 28 | [[], '0'], 29 | ['0xabcd', String(0xabcd)], 30 | ['0', '0'], 31 | ['42', '42'], 32 | ['1.0', '1'], 33 | ['1.5', '1.5'], 34 | ['-23', '-23'], 35 | ['-0.01', '-0.01'] 36 | ]; 37 | for (var i = 0; i < values.length; i++) { 38 | var n = new NumberLiteral(values[i][0]); 39 | expect(n.toAQL()).to.equal(String(values[i][1])); 40 | } 41 | }); 42 | it('clones NumberLiteral instances', function () { 43 | var src = new NumberLiteral(42), 44 | copy = new NumberLiteral(src); 45 | expect(src.toAQL()).to.equal(copy.toAQL()); 46 | expect(src).not.to.equal(copy); 47 | }); 48 | it('clones IntegerLiteral instances', function () { 49 | var src = new types.IntegerLiteral(42), 50 | copy = new NumberLiteral(src); 51 | expect(src.toAQL()).to.equal(copy.toAQL()); 52 | expect(src).not.to.equal(copy); 53 | }); 54 | it('does not accept NaN', function () { 55 | expect(function () {return new NumberLiteral(NaN);}).to.throwException(isAqlError); 56 | }); 57 | it('does not accept Infinity', function () { 58 | expect(function () {return new NumberLiteral(Infinity);}).to.throwException(isAqlError); 59 | }); 60 | it('does not accept non-numeric values', function () { 61 | var values = [ 62 | '0xabsurd', 63 | 'hello', 64 | /absurd/, 65 | function () {}, 66 | {0: 1}, 67 | [1, 2, 3], 68 | {} 69 | ]; 70 | for (var i = 0; i < values.length; i++) { 71 | expect(function () {return new NumberLiteral(values[i]);}).to.throwException(isAqlError); 72 | } 73 | }); 74 | }); -------------------------------------------------------------------------------- /test/types/ObjectLiteral.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | ObjectLiteral = types.ObjectLiteral, 7 | AqlError = require('../../errors').AqlError, 8 | isAqlError = function (e) { 9 | expect(e).to.be.an(AqlError); 10 | }; 11 | 12 | describe('ObjectLiteral', function () { 13 | it('returns an expression', function () { 14 | var expr = new ObjectLiteral({}); 15 | expect(expr).to.be.an(types._Expression); 16 | expect(expr.toAQL).to.be.a('function'); 17 | }); 18 | it('clones ObjectLiteral instances', function () { 19 | var src = new ObjectLiteral({a: 1, b: 2, c: 3}), 20 | copy = new ObjectLiteral(src); 21 | expect(src.toAQL()).to.equal(copy.toAQL()); 22 | expect(src).not.to.equal(copy); 23 | }); 24 | it('wraps objects', function () { 25 | var obj = {a: 1, b: 2, c: 3}; 26 | var expr = new ObjectLiteral(obj); 27 | expect(expr._value).to.be.an(Object); 28 | expect(Object.keys(expr._value)).to.eql(Object.keys(obj)); 29 | }); 30 | it('auto-casts object values', function () { 31 | var obj = {a: 42, b: 'id', c: 'some.ref', d: '"hello"', e: false, f: null}; 32 | var ctors = { 33 | a: types.IntegerLiteral, 34 | b: types.Identifier, 35 | c: types.SimpleReference, 36 | d: types.StringLiteral, 37 | e: types.BooleanLiteral, 38 | f: types.NullLiteral 39 | }; 40 | var expr = new ObjectLiteral(obj); 41 | for (var key in obj) { 42 | if (obj.hasOwnProperty(key)) { 43 | expect(expr._value[key].constructor).to.equal(ctors[key]); 44 | } 45 | } 46 | }); 47 | it('does not accept non-object values', function () { 48 | var values = [ 49 | undefined, 50 | null, 51 | 42, 52 | false, 53 | 'hello', 54 | function () {} 55 | ]; 56 | for (var i = 0; i < values.length; i++) { 57 | expect(function () {return new ObjectLiteral(values[i]);}).to.throwException(isAqlError); 58 | } 59 | }); 60 | it('quotes unsafe keys in AQL', function () { 61 | expect(new ObjectLiteral({a: 'b'}).toAQL()).to.equal('{a: b}'); 62 | expect(new ObjectLiteral({0: 'b'}).toAQL()).to.equal('{0: b}'); 63 | expect(new ObjectLiteral({' a': 'b'}).toAQL()).to.equal('{" a": b}'); 64 | expect(new ObjectLiteral({'0a': 'b'}).toAQL()).to.equal('{"0a": b}'); 65 | expect(new ObjectLiteral({'\'a\'': 'b'}).toAQL()).to.equal('{"\'a\'": b}'); 66 | expect(new ObjectLiteral({'":a"': 'b'}).toAQL()).to.equal('{":a": b}'); 67 | }); 68 | it('handles quoted keys', function () { 69 | expect(new ObjectLiteral({'"a"': 'b'}).toAQL()).to.equal('{"a": b}'); 70 | expect(new ObjectLiteral({'" pot@to!!! "': 'b'}).toAQL()).to.equal('{" pot@to!!! ": b}'); 71 | }); 72 | it('handles dynamic keys', function () { 73 | expect(new ObjectLiteral({':a': 'b'}).toAQL()).to.equal('{[a]: b}'); 74 | expect(new ObjectLiteral({':@a': 'b'}).toAQL()).to.equal('{[@a]: b}'); 75 | expect(new ObjectLiteral({':`a`': 'b'}).toAQL()).to.equal('{[`a`]: b}'); 76 | expect(new ObjectLiteral({':`@a`': 'b'}).toAQL()).to.equal('{[`@a`]: b}'); 77 | expect(new ObjectLiteral({':a.b.c': 'b'}).toAQL()).to.equal('{[a.b.c]: b}'); 78 | expect(new ObjectLiteral({':a.`b`.c': 'b'}).toAQL()).to.equal('{[a.`b`.c]: b}'); 79 | expect(new ObjectLiteral({':a@a': 'b'}).toAQL()).to.equal('{[a@a]: b}'); 80 | expect(new ObjectLiteral({':`a@a`': 'b'}).toAQL()).to.equal('{[`a@a`]: b}'); 81 | }); 82 | it('rejects invalid dynamic keys', function () { 83 | var values = [ 84 | {'::a': 'b'}, 85 | {': a': 'b'}, 86 | {':-a': 'b'}, 87 | {':a()': 'b'}, 88 | {':[a]': 'b'} 89 | ]; 90 | for (var i = 0; i < values.length; i++) { 91 | expect(function () {return new ObjectLiteral(values[i]);}).to.throwException(isAqlError); 92 | } 93 | }); 94 | it('wraps Operation values in parentheses', function () { 95 | var op = new types._Operation(); 96 | op.toAQL = function () {return 'x';}; 97 | expect(new ObjectLiteral({an: op}).toAQL()).to.equal('{an: (x)}'); 98 | }); 99 | it('wraps Statement values in parentheses', function () { 100 | var st = new types._Statement(); 101 | st.toAQL = function () {return 'x';}; 102 | expect(new ObjectLiteral({an: st}).toAQL()).to.equal('{an: (x)}'); 103 | }); 104 | it('wraps PartialStatement values in parentheses', function () { 105 | var ps = new types._PartialStatement(); 106 | ps.toAQL = function () {return 'x';}; 107 | expect(new ObjectLiteral({an: ps}).toAQL()).to.equal('{an: (x)}'); 108 | }); 109 | }); -------------------------------------------------------------------------------- /test/types/PropertyAccess.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | PropertyAccess = types.PropertyAccess; 7 | 8 | describe('PropertyAccess', function () { 9 | it('returns an expression', function () { 10 | var expr = new PropertyAccess('a', ['b']); 11 | expect(expr).to.be.an(types._Expression); 12 | expect(expr.toAQL).to.be.a('function'); 13 | }); 14 | it('auto-casts its arguments', function () { 15 | var arr = [42, 'id', 'some.ref', '"hello"', false, null, '1..5']; 16 | var ctors = [ 17 | types.IntegerLiteral, 18 | types.Identifier, 19 | types.SimpleReference, 20 | types.StringLiteral, 21 | types.BooleanLiteral, 22 | types.NullLiteral, 23 | types.RangeExpression 24 | ]; 25 | for (var i = 0; i < arr.length; i++) { 26 | var expr = new PropertyAccess(arr[i], [arr[i]]); 27 | expect(expr._obj.constructor).to.equal(ctors[i]); 28 | expect(expr._keys[0].constructor).to.equal(ctors[i]); 29 | } 30 | }); 31 | }); -------------------------------------------------------------------------------- /test/types/RangeExpression.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | RangeExpression = types.RangeExpression; 7 | 8 | describe('RangeExpression', function () { 9 | it('returns an expression', function () { 10 | var expr = new RangeExpression(0, 1); 11 | expect(expr).to.be.an(types._Expression); 12 | expect(expr.toAQL).to.be.a('function'); 13 | }); 14 | it('auto-casts its arguments', function () { 15 | var arr = [42, 'id', 'some.ref', '"hello"', false, null, '1..5']; 16 | var ctors = [ 17 | types.IntegerLiteral, 18 | types.Identifier, 19 | types.SimpleReference, 20 | types.StringLiteral, 21 | types.BooleanLiteral, 22 | types.NullLiteral, 23 | types.RangeExpression 24 | ]; 25 | for (var i = 0; i < arr.length; i++) { 26 | var expr = new RangeExpression(arr[i], arr[i]); 27 | expect(expr._start.constructor).to.equal(ctors[i]); 28 | expect(expr._end.constructor).to.equal(ctors[i]); 29 | } 30 | }); 31 | }); -------------------------------------------------------------------------------- /test/types/RawExpression.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | RawExpression = types.RawExpression; 7 | 8 | describe('RawExpression', function () { 9 | it('returns an expression', function () { 10 | var expr = new RawExpression('foo'); 11 | expect(expr).to.be.an(types._Expression); 12 | expect(expr.toAQL).to.be.a('function'); 13 | }); 14 | it('takes any input and wraps it as a string', function () { 15 | var values = [ 16 | 'a simple string', 17 | 'ä str!ŋ w/ b@d ¢ħrź##', 18 | 0, 19 | 42, 20 | true, 21 | null, 22 | false, 23 | undefined, 24 | {an: 'object', 'with': {things: 'in it'}}, 25 | ['an', 'array', '||', 2], 26 | function also() {return this;} 27 | ]; 28 | for (var i = 0; i < values.length; i++) { 29 | expect(new RawExpression(values[i]).toAQL()).to.equal(String(values[i])); 30 | } 31 | }); 32 | }); -------------------------------------------------------------------------------- /test/types/RemoveExpression.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | RemoveExpression = types.RemoveExpression, 7 | AqlError = require('../../errors').AqlError, 8 | isAqlError = function (e) { 9 | expect(e).to.be.an(AqlError); 10 | }; 11 | 12 | describe('RemoveExpression', function () { 13 | it('returns a statement', function () { 14 | var expr = new RemoveExpression(null, 'x', 'y'); 15 | expect(expr).to.be.a(types._PartialStatement); 16 | expect(expr.toAQL).to.be.a('function'); 17 | }); 18 | it('generates a REMOVE statement', function () { 19 | expect(new RemoveExpression(null, 'x', 'y').toAQL()).to.equal('REMOVE x IN y'); 20 | }); 21 | it('auto-casts expressions', function () { 22 | var arr = [42, 'id', 'some.ref', '"hello"', false, null]; 23 | var ctors = [ 24 | types.IntegerLiteral, 25 | types.Identifier, 26 | types.SimpleReference, 27 | types.StringLiteral, 28 | types.BooleanLiteral, 29 | types.NullLiteral 30 | ]; 31 | for (var i = 0; i < arr.length; i++) { 32 | expect(new RemoveExpression(null, arr[i], 'y')._expr.constructor).to.equal(ctors[i]); 33 | } 34 | }); 35 | it('wraps Operation expressions in parentheses', function () { 36 | var op = new types._Operation(); 37 | op.toAQL = function () {return 'x';}; 38 | expect(new RemoveExpression(null, op, 'y').toAQL()).to.equal('REMOVE (x) IN y'); 39 | }); 40 | it('wraps Statement expressions in parentheses', function () { 41 | var st = new types._Statement(); 42 | st.toAQL = function () {return 'x';}; 43 | expect(new RemoveExpression(null, st, 'y').toAQL()).to.equal('REMOVE (x) IN y'); 44 | }); 45 | it('wraps PartialStatement expressions in parentheses', function () { 46 | var ps = new types._PartialStatement(); 47 | ps.toAQL = function () {return 'x';}; 48 | expect(new RemoveExpression(null, ps, 'y').toAQL()).to.equal('REMOVE (x) IN y'); 49 | }); 50 | it('wraps well-formed strings as collection names', function () { 51 | var values = [ 52 | '_', 53 | '_x', 54 | 'all_lower_case', 55 | 'snakeCaseAlso', 56 | 'CamelCaseHere', 57 | 'ALL_UPPER_CASE', 58 | '__cRaZy__' 59 | ]; 60 | for (var i = 0; i < values.length; i++) { 61 | expect(new RemoveExpression(null, 'x', values[i])._collection.toAQL()).to.equal(values[i]); 62 | } 63 | }); 64 | it('does not accept malformed strings as collection names', function () { 65 | var values = [ 66 | '', 67 | '-x', 68 | 'also bad', 69 | 'überbad', 70 | 'spaß' 71 | ]; 72 | for (var i = 0; i < values.length; i++) { 73 | expect(function () {return new RemoveExpression(null, 'x', values[i]);}).to.throwException(isAqlError); 74 | } 75 | }); 76 | it('does not accept any other values as collection names', function () { 77 | var values = [ 78 | new types.StringLiteral('for'), 79 | new types.RawExpression('for'), 80 | new types.SimpleReference('for'), 81 | new types.Keyword('for'), 82 | new types.NullLiteral(null), 83 | 42, 84 | true, 85 | function () {}, 86 | {}, 87 | [] 88 | ]; 89 | for (var i = 0; i < values.length; i++) { 90 | expect(function () {return new RemoveExpression(null, 'x', values[i]);}).to.throwException(isAqlError); 91 | } 92 | }); 93 | it('converts preceding nodes to AQL', function () { 94 | var ps = new types._PartialStatement(); 95 | ps.toAQL = function () {return '$';}; 96 | expect(new RemoveExpression(ps, 'x', 'y').toAQL()).to.equal('$ REMOVE x IN y'); 97 | }); 98 | describe('options', function () { 99 | var expr = new RemoveExpression(null, 'x', 'y'); 100 | it('returns a new RemoveExpression', function () { 101 | var optExpr = expr.options({a: 'b'}); 102 | expect(optExpr).to.be.a(types.RemoveExpression); 103 | expect(optExpr.toAQL()).to.equal('REMOVE x IN y OPTIONS {a: b}'); 104 | }); 105 | }); 106 | describe('returnOld', function () { 107 | var expr = new RemoveExpression(null, 'x', 'y'); 108 | it('returns a LET RETURN OLD', function () { 109 | var rtrnExpr = expr.returnOld('a'); 110 | expect(rtrnExpr).to.be.a(types.ReturnExpression); 111 | expect(rtrnExpr._value._value).to.equal('a'); 112 | expect(rtrnExpr._prev).to.be.a(types.LetExpression); 113 | rtrnExpr._prev._prev = null; 114 | expect(rtrnExpr.toAQL()).to.equal('LET a = `OLD` RETURN a'); 115 | }); 116 | }); 117 | }); 118 | -------------------------------------------------------------------------------- /test/types/ReplaceExpression.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | ReplaceExpression = types.ReplaceExpression, 7 | AqlError = require('../../errors').AqlError, 8 | isAqlError = function (e) { 9 | expect(e).to.be.an(AqlError); 10 | }; 11 | 12 | describe('ReplaceExpression', function () { 13 | it('returns a statement', function () { 14 | var expr = new ReplaceExpression(null, 'x', 'y', 'z'); 15 | expect(expr).to.be.a(types._PartialStatement); 16 | expect(expr.toAQL).to.be.a('function'); 17 | }); 18 | it('generates a REPLACE statement', function () { 19 | expect(new ReplaceExpression(null, 'x', 'y', 'z').toAQL()).to.equal('REPLACE x WITH y IN z'); 20 | }); 21 | it('auto-casts expressions', function () { 22 | var arr = [42, 'id', 'some.ref', '"hello"', false, null]; 23 | var ctors = [ 24 | types.IntegerLiteral, 25 | types.Identifier, 26 | types.SimpleReference, 27 | types.StringLiteral, 28 | types.BooleanLiteral, 29 | types.NullLiteral 30 | ]; 31 | for (var i = 0; i < arr.length; i++) { 32 | expect(new ReplaceExpression(null, arr[i], 'y', 'z')._expr.constructor).to.equal(ctors[i]); 33 | } 34 | }); 35 | it('wraps Operation expressions in parentheses', function () { 36 | var op = new types._Operation(); 37 | op.toAQL = function () {return 'x';}; 38 | expect(new ReplaceExpression(null, op, 'y', 'z').toAQL()).to.equal('REPLACE (x) WITH y IN z'); 39 | }); 40 | it('wraps Statement expressions in parentheses', function () { 41 | var st = new types._Statement(); 42 | st.toAQL = function () {return 'x';}; 43 | expect(new ReplaceExpression(null, st, 'y', 'z').toAQL()).to.equal('REPLACE (x) WITH y IN z'); 44 | }); 45 | it('wraps PartialStatement expressions in parentheses', function () { 46 | var ps = new types._PartialStatement(); 47 | ps.toAQL = function () {return 'x';}; 48 | expect(new ReplaceExpression(null, ps, 'y', 'z').toAQL()).to.equal('REPLACE (x) WITH y IN z'); 49 | }); 50 | it('allows omitting with-expressions', function () { 51 | expect(new ReplaceExpression(null, 'x', undefined, 'z').toAQL()).to.equal('REPLACE x IN z'); 52 | }); 53 | it('auto-casts with-expressions', function () { 54 | var arr = [42, 'id', 'some.ref', '"hello"', false, null]; 55 | var ctors = [ 56 | types.IntegerLiteral, 57 | types.Identifier, 58 | types.SimpleReference, 59 | types.StringLiteral, 60 | types.BooleanLiteral, 61 | types.NullLiteral 62 | ]; 63 | for (var i = 0; i < arr.length; i++) { 64 | expect(new ReplaceExpression(null, 'x', arr[i], 'z')._withExpr.constructor).to.equal(ctors[i]); 65 | } 66 | }); 67 | it('wraps Operation with-expressions in parentheses', function () { 68 | var op = new types._Operation(); 69 | op.toAQL = function () {return 'y';}; 70 | expect(new ReplaceExpression(null, 'x', op, 'z').toAQL()).to.equal('REPLACE x WITH (y) IN z'); 71 | }); 72 | it('wraps Statement with-expressions in parentheses', function () { 73 | var st = new types._Statement(); 74 | st.toAQL = function () {return 'y';}; 75 | expect(new ReplaceExpression(null, 'x', st, 'z').toAQL()).to.equal('REPLACE x WITH (y) IN z'); 76 | }); 77 | it('wraps PartialStatement with-expressions in parentheses', function () { 78 | var ps = new types._PartialStatement(); 79 | ps.toAQL = function () {return 'y';}; 80 | expect(new ReplaceExpression(null, 'x', ps, 'z').toAQL()).to.equal('REPLACE x WITH (y) IN z'); 81 | }); 82 | it('wraps well-formed strings as collection names', function () { 83 | var values = [ 84 | '_', 85 | '_x', 86 | 'all_lower_case', 87 | 'snakeCaseAlso', 88 | 'CamelCaseHere', 89 | 'ALL_UPPER_CASE', 90 | '__cRaZy__' 91 | ]; 92 | for (var i = 0; i < values.length; i++) { 93 | expect(new ReplaceExpression(null, 'x', 'y', values[i])._collection.toAQL()).to.equal(values[i]); 94 | } 95 | }); 96 | it('does not accept malformed strings as collection names', function () { 97 | var values = [ 98 | '', 99 | '-x', 100 | 'also bad', 101 | 'überbad', 102 | 'spaß' 103 | ]; 104 | for (var i = 0; i < values.length; i++) { 105 | expect(function () {return new ReplaceExpression(null, 'x', 'y', values[i]);}).to.throwException(isAqlError); 106 | } 107 | }); 108 | it('does not accept any other values as collection names', function () { 109 | var values = [ 110 | new types.StringLiteral('for'), 111 | new types.RawExpression('for'), 112 | new types.SimpleReference('for'), 113 | new types.Keyword('for'), 114 | new types.NullLiteral(null), 115 | 42, 116 | true, 117 | function () {}, 118 | {}, 119 | [] 120 | ]; 121 | for (var i = 0; i < values.length; i++) { 122 | expect(function () {return new ReplaceExpression(null, 'x', 'y', values[i]);}).to.throwException(isAqlError); 123 | } 124 | }); 125 | it('converts preceding nodes to AQL', function () { 126 | var ps = new types._PartialStatement(); 127 | ps.toAQL = function () {return '$';}; 128 | expect(new ReplaceExpression(ps, 'x', 'y', 'z').toAQL()).to.equal('$ REPLACE x WITH y IN z'); 129 | }); 130 | describe('options', function () { 131 | var expr = new ReplaceExpression(null, 'x', 'y', 'z'); 132 | it('returns a new ReplaceExpression', function () { 133 | var optExpr = expr.options({a: 'b'}); 134 | expect(optExpr).to.be.a(types.ReplaceExpression); 135 | expect(optExpr.toAQL()).to.equal('REPLACE x WITH y IN z OPTIONS {a: b}'); 136 | }); 137 | }); 138 | describe('returnOld', function () { 139 | var expr = new ReplaceExpression(null, 'x', 'y', 'z'); 140 | it('returns a LET RETURN OLD', function () { 141 | var rtrnExpr = expr.returnOld('a'); 142 | expect(rtrnExpr).to.be.a(types.ReturnExpression); 143 | expect(rtrnExpr._value._value).to.equal('a'); 144 | expect(rtrnExpr._prev).to.be.a(types.LetExpression); 145 | rtrnExpr._prev._prev = null; 146 | expect(rtrnExpr.toAQL()).to.equal('LET a = `OLD` RETURN a'); 147 | }); 148 | }); 149 | describe('returnNew', function () { 150 | var expr = new ReplaceExpression(null, 'x', 'y', 'z'); 151 | it('returns a LET RETURN NEW', function () { 152 | var rtrnExpr = expr.returnNew('a'); 153 | expect(rtrnExpr).to.be.a(types.ReturnExpression); 154 | expect(rtrnExpr._value._value).to.equal('a'); 155 | expect(rtrnExpr._prev).to.be.a(types.LetExpression); 156 | rtrnExpr._prev._prev = null; 157 | expect(rtrnExpr.toAQL()).to.equal('LET a = `NEW` RETURN a'); 158 | }); 159 | }); 160 | }); 161 | -------------------------------------------------------------------------------- /test/types/ReturnExpression.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | ReturnExpression = types.ReturnExpression; 7 | 8 | describe('ReturnExpression', function () { 9 | it('returns a statement', function () { 10 | var expr = new ReturnExpression(null, 'x'); 11 | expect(expr).to.be.a(types._Expression); 12 | expect(expr.toAQL).to.be.a('function'); 13 | }); 14 | it('generates a RETURN statement', function () { 15 | expect(new ReturnExpression(null, 'x').toAQL()).to.equal('RETURN x'); 16 | }); 17 | it('generates a RETURN DISTINCT statement', function () { 18 | expect(new ReturnExpression(null, 'x', true).toAQL()).to.equal('RETURN DISTINCT x'); 19 | }); 20 | it('auto-casts values', function () { 21 | var arr = [42, 'id', 'some.ref', '"hello"', false, null]; 22 | var ctors = [ 23 | types.IntegerLiteral, 24 | types.Identifier, 25 | types.SimpleReference, 26 | types.StringLiteral, 27 | types.BooleanLiteral, 28 | types.NullLiteral 29 | ]; 30 | for (var i = 0; i < arr.length; i++) { 31 | expect(new ReturnExpression(null, arr[i])._value.constructor).to.equal(ctors[i]); 32 | } 33 | }); 34 | it('wraps Operation values in parentheses', function () { 35 | var op = new types._Operation(); 36 | op.toAQL = function () {return 'x';}; 37 | expect(new ReturnExpression(null, op).toAQL()).to.equal('RETURN (x)'); 38 | }); 39 | it('wraps Statement values in parentheses', function () { 40 | var st = new types._Statement(); 41 | st.toAQL = function () {return 'x';}; 42 | expect(new ReturnExpression(null, st).toAQL()).to.equal('RETURN (x)'); 43 | }); 44 | it('wraps PartialStatement values in parentheses', function () { 45 | var ps = new types._PartialStatement(); 46 | ps.toAQL = function () {return 'x';}; 47 | expect(new ReturnExpression(null, ps).toAQL()).to.equal('RETURN (x)'); 48 | }); 49 | it('converts preceding nodes to AQL', function () { 50 | var ps = new types._PartialStatement(); 51 | ps.toAQL = function () {return '$';}; 52 | expect(new ReturnExpression(ps, 'x').toAQL()).to.equal('$ RETURN x'); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /test/types/SimpleReference.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | SimpleReference = types.SimpleReference, 7 | AqlError = require('../../errors').AqlError, 8 | isAqlError = function (e) { 9 | expect(e).to.be.an(AqlError); 10 | }; 11 | 12 | describe('SimpleReference', function () { 13 | it('returns an expression', function () { 14 | var expr = new SimpleReference('for'); 15 | expect(expr).to.be.an(types._Expression); 16 | expect(expr.toAQL).to.be.a('function'); 17 | }); 18 | it('clones SimpleReference instances', function () { 19 | var src = new SimpleReference('for'), 20 | copy = new SimpleReference(src); 21 | expect(src.toAQL()).to.equal(copy.toAQL()); 22 | expect(src).not.to.equal(copy); 23 | }); 24 | it('accepts ArangoCollection instances', function () { 25 | function ArangoCollection(name) { 26 | this._name = 'lol'; 27 | } 28 | ArangoCollection.prototype.name = function () { 29 | return this._name; 30 | }; 31 | var collection = new ArangoCollection('some_collection'), 32 | ref = new SimpleReference(collection); 33 | expect(ref.toAQL()).to.equal(collection.name()); 34 | }); 35 | it('wraps well-formed strings', function () { 36 | var values = [ 37 | '_', 38 | '_x', 39 | 'all_lower_case', 40 | 'snakeCaseAlso', 41 | 'CamelCaseHere', 42 | 'ALL_UPPER_CASE', 43 | '__cRaZy__', 44 | '_._', 45 | '_x.__cRaZy__', 46 | 'all_lower_case.__cRaZy__', 47 | 'snakeCaseAlso.__cRaZy__', 48 | 'CamelCaseHere.__cRaZy__', 49 | 'ALL_UPPER_CASE.__cRaZy__', 50 | '__cRaZy__.__cRaZy__', 51 | 'pot@to', 52 | '@chicken', 53 | '@@chicken.chicken', 54 | 'chicken.@chicken', 55 | '`@chicken`.chicken', 56 | 'chicken.`@chicken`', 57 | '`chicken`.`chicken`' 58 | ]; 59 | for (var i = 0; i < values.length; i++) { 60 | expect(new SimpleReference(values[i]).toAQL()).to.equal(values[i]); 61 | } 62 | }); 63 | it('wraps values in backticks if necessary', function () { 64 | var values = [ 65 | 'for', 66 | 'RETURN', 67 | 'totally-radical' 68 | ]; 69 | for (var i = 0; i < values.length; i++) { 70 | expect(new SimpleReference(values[i]).toAQL()).to.equal('`' + values[i] + '`'); 71 | } 72 | }); 73 | it('does not accept malformed strings', function () { 74 | var values = [ 75 | '', 76 | '-x', 77 | 'a..b', 78 | 'a.b..c', 79 | 'bad.1', 80 | 'bad[1]', 81 | 'also bad', 82 | 'überbad', 83 | 'spaß' 84 | ]; 85 | for (var i = 0; i < values.length; i++) { 86 | expect(function () {return new SimpleReference(values[i]);}).to.throwException(isAqlError); 87 | } 88 | }); 89 | it('does not accept any other values', function () { 90 | var values = [ 91 | new types.StringLiteral('for'), 92 | new types.RawExpression('for'), 93 | new types.Identifier('for'), 94 | new types.Keyword('for'), 95 | new types.NullLiteral(null), 96 | 42, 97 | true, 98 | function () {}, 99 | {}, 100 | [] 101 | ]; 102 | for (var i = 0; i < values.length; i++) { 103 | expect(function () {return new SimpleReference(values[i]);}).to.throwException(isAqlError); 104 | } 105 | }); 106 | }); -------------------------------------------------------------------------------- /test/types/SortExpression.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | SortExpression = types.SortExpression, 7 | AqlError = require('../../errors').AqlError, 8 | isAqlError = function (e) { 9 | expect(e).to.be.an(AqlError); 10 | }; 11 | 12 | describe('SortExpression', function () { 13 | it('returns a partial statement', function () { 14 | var expr = new SortExpression(null, ['x']); 15 | expect(expr).to.be.an(types._PartialStatement); 16 | expect(expr.toAQL).to.be.a('function'); 17 | }); 18 | it('generates a SORT statement', function () { 19 | expect(new SortExpression(null, ['x']).toAQL()).to.equal('SORT x'); 20 | }); 21 | it('auto-casts sort values', function () { 22 | var arr = [42, 'id', 'some.ref', '"hello"', false, null]; 23 | var ctors = [ 24 | types.IntegerLiteral, 25 | types.Identifier, 26 | types.SimpleReference, 27 | types.StringLiteral, 28 | types.BooleanLiteral, 29 | types.NullLiteral 30 | ]; 31 | var expr = new SortExpression(null, arr); 32 | for (var i = 0; i < arr.length; i++) { 33 | expect(expr._args[i].constructor).to.equal(ctors[i]); 34 | } 35 | }); 36 | it('does not accept empty values', function () { 37 | expect(function () {return new SortExpression(null, []);}).to.throwException(isAqlError); 38 | }); 39 | it('does not accept non-array values', function () { 40 | var values = [ 41 | (function () {return arguments;}()), 42 | {0: 'a', 1: 'b', 2: 'c'}, 43 | new types.StringLiteral('abc'), 44 | 42, 45 | false, 46 | 'hello', 47 | /absurd/, 48 | function () {}, 49 | {} 50 | ]; 51 | for (var i = 0; i < values.length; i++) { 52 | expect(function () {return new SortExpression(null, values[i]);}).to.throwException(isAqlError); 53 | } 54 | }); 55 | it('accepts ASC/DESC keywords', function () { 56 | var expr = new SortExpression(null, ['x', 'ASC', 'y', 'z', 'DESC']); 57 | expect(expr.toAQL()).to.equal('SORT x ASC, y, z DESC'); 58 | expect(expr._args[1].constructor).to.equal(types.Keyword); 59 | expect(expr._args[4].constructor).to.equal(types.Keyword); 60 | }); 61 | it('does not accept keywords in unexpected positions', function () { 62 | var values = [ 63 | ['ASC'], 64 | ['ASC', 'x'], 65 | ['x', 'ASC', 'DESC'], 66 | ['ASC', 'DESC'] 67 | ]; 68 | for (var i = 0; i < values.length; i++) { 69 | expect(function () {return new SortExpression(null, values[i]);}).to.throwException(isAqlError); 70 | } 71 | }); 72 | it('wraps Operation values in parentheses', function () { 73 | var op = new types._Operation(); 74 | op.toAQL = function () {return 'x';}; 75 | expect(new SortExpression(null, [op]).toAQL()).to.equal('SORT (x)'); 76 | }); 77 | it('wraps Statement values in parentheses', function () { 78 | var st = new types._Statement(); 79 | st.toAQL = function () {return 'x';}; 80 | expect(new SortExpression(null, [st]).toAQL()).to.equal('SORT (x)'); 81 | }); 82 | it('wraps PartialStatement values in parentheses', function () { 83 | var ps = new types._PartialStatement(); 84 | ps.toAQL = function () {return 'x';}; 85 | expect(new SortExpression(null, [ps]).toAQL()).to.equal('SORT (x)'); 86 | }); 87 | }); -------------------------------------------------------------------------------- /test/types/StringLiteral.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | StringLiteral = types.StringLiteral; 7 | 8 | describe('StringLiteral', function () { 9 | it('returns an expression', function () { 10 | var expr = new StringLiteral(''); 11 | expect(expr).to.be.an(types._Expression); 12 | expect(expr.toAQL).to.be.a('function'); 13 | }); 14 | it('clones StringLiteral instances', function () { 15 | var src = new StringLiteral('hello'), 16 | copy = new StringLiteral(src); 17 | expect(src.toAQL()).to.equal(copy.toAQL()); 18 | expect(src).not.to.equal(copy); 19 | }); 20 | it('wraps the AQL value of objects that have a toAQL method', function () { 21 | var values = [ 22 | new types.IntegerLiteral(23), 23 | new types.NullLiteral(null), 24 | new types.ForExpression(null, 'x', 'foo'), 25 | {toAQL: function () {return 'yes please';}} 26 | ]; 27 | for (var i = 0; i < values.length; i++) { 28 | expect(new StringLiteral(values[i])._value).to.equal(values[i].toAQL()); 29 | } 30 | }); 31 | it('wraps any other value as a string', function () { 32 | var values = [ 33 | 'a simple string', 34 | 42, 35 | true, 36 | {an: 'object', 'with': {things: 'in it'}}, 37 | ['an', 'array', '||', 2], 38 | function also() {return this;} 39 | ]; 40 | for (var i = 0; i < values.length; i++) { 41 | expect(new StringLiteral(values[i])._value).to.equal(String(values[i])); 42 | } 43 | }); 44 | }); -------------------------------------------------------------------------------- /test/types/TernaryOperation.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | TernaryOperation = types.TernaryOperation, 7 | AqlError = require('../../errors').AqlError, 8 | isAqlError = function (e) { 9 | expect(e).to.be.an(AqlError); 10 | }; 11 | 12 | describe('TernaryOperation', function () { 13 | it('returns an expression', function () { 14 | var expr = new TernaryOperation('?', ':', 'x', 'y', 'z'); 15 | expect(expr).to.be.an(types._Expression); 16 | expect(expr.toAQL).to.be.a('function'); 17 | }); 18 | it('accepts non-empty strings as operators', function () { 19 | var values = [ 20 | '-', 21 | '~', 22 | '+', 23 | 'not', 24 | 'nöis3', 25 | '$$ $$%§-äß', 26 | 'bad:bad:bad' 27 | ]; 28 | for (var i = 0; i < values.length; i++) { 29 | var op = new TernaryOperation(values[i], values[i], 'x', 'y', 'z'); 30 | expect(op.toAQL()).to.equal('x ' + values[i] + ' y ' + values[i] + ' z'); 31 | } 32 | }); 33 | it('does not accept any other values as operators', function () { 34 | var values = [ 35 | '', 36 | new types.StringLiteral('for'), 37 | new types.RawExpression('for'), 38 | new types.SimpleReference('for'), 39 | new types.Keyword('for'), 40 | new types.NullLiteral(null), 41 | 42, 42 | true, 43 | function () {}, 44 | {}, 45 | [] 46 | ]; 47 | for (var i = 0; i < values.length; i++) { 48 | expect(function () {return new TernaryOperation(values[i], values[i], 'x', 'y', 'z');}).to.throwException(isAqlError); 49 | } 50 | }); 51 | it('auto-casts values', function () { 52 | var arr = [42, 'id', 'some.ref', '"hello"', false, null]; 53 | var ctors = [ 54 | types.IntegerLiteral, 55 | types.Identifier, 56 | types.SimpleReference, 57 | types.StringLiteral, 58 | types.BooleanLiteral, 59 | types.NullLiteral 60 | ]; 61 | for (var i = 0; i < arr.length; i++) { 62 | var op = new TernaryOperation('?', ':', arr[i], arr[i], arr[i]); 63 | expect(op._value1.constructor).to.equal(ctors[i]); 64 | expect(op._value2.constructor).to.equal(ctors[i]); 65 | expect(op._value3.constructor).to.equal(ctors[i]); 66 | } 67 | }); 68 | it('wraps Operation values in parentheses', function () { 69 | var op = new types._Operation(); 70 | op.toAQL = function () {return 'x';}; 71 | expect(new TernaryOperation('?', ':', op, op, op).toAQL()).to.equal('(x) ? (x) : (x)'); 72 | }); 73 | it('wraps Statement values in parentheses', function () { 74 | var st = new types._Statement(); 75 | st.toAQL = function () {return 'x';}; 76 | expect(new TernaryOperation('?', ':', st, st, st).toAQL()).to.equal('(x) ? (x) : (x)'); 77 | }); 78 | it('wraps PartialStatement values in parentheses', function () { 79 | var ps = new types._PartialStatement(); 80 | ps.toAQL = function () {return 'x';}; 81 | expect(new TernaryOperation('?', ':', ps, ps, ps).toAQL()).to.equal('(x) ? (x) : (x)'); 82 | }); 83 | }); -------------------------------------------------------------------------------- /test/types/UnaryOperation.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | UnaryOperation = types.UnaryOperation, 7 | AqlError = require('../../errors').AqlError, 8 | isAqlError = function (e) { 9 | expect(e).to.be.an(AqlError); 10 | }; 11 | 12 | describe('UnaryOperation', function () { 13 | it('returns an expression', function () { 14 | var expr = new UnaryOperation('!', 'x'); 15 | expect(expr).to.be.an(types._Expression); 16 | expect(expr.toAQL).to.be.a('function'); 17 | }); 18 | it('accepts non-empty strings as operators', function () { 19 | var values = [ 20 | '-', 21 | '~', 22 | '!', 23 | 'not', 24 | 'nöis3', 25 | '$$ $$%§-äß', 26 | 'bad:bad:bad' 27 | ]; 28 | for (var i = 0; i < values.length; i++) { 29 | expect(new UnaryOperation(values[i], 'x').toAQL()).to.equal(values[i] + 'x'); 30 | } 31 | }); 32 | it('does not accept any other values as operators', function () { 33 | var values = [ 34 | '', 35 | new types.StringLiteral('for'), 36 | new types.RawExpression('for'), 37 | new types.SimpleReference('for'), 38 | new types.Keyword('for'), 39 | new types.NullLiteral(null), 40 | 42, 41 | true, 42 | function () {}, 43 | {}, 44 | [] 45 | ]; 46 | for (var i = 0; i < values.length; i++) { 47 | expect(function () {return new UnaryOperation(values[i], 'x');}).to.throwException(isAqlError); 48 | } 49 | }); 50 | it('auto-casts values', function () { 51 | var arr = [42, 'id', 'some.ref', '"hello"', false, null]; 52 | var ctors = [ 53 | types.IntegerLiteral, 54 | types.Identifier, 55 | types.SimpleReference, 56 | types.StringLiteral, 57 | types.BooleanLiteral, 58 | types.NullLiteral 59 | ]; 60 | for (var i = 0; i < arr.length; i++) { 61 | expect(new UnaryOperation('!', arr[i])._value.constructor).to.equal(ctors[i]); 62 | } 63 | }); 64 | it('wraps Operation values in parentheses', function () { 65 | var op = new types._Operation(); 66 | op.toAQL = function () {return 'x';}; 67 | expect(new UnaryOperation('!', op).toAQL()).to.equal('!(x)'); 68 | }); 69 | it('wraps Statement values in parentheses', function () { 70 | var st = new types._Statement(); 71 | st.toAQL = function () {return 'x';}; 72 | expect(new UnaryOperation('!', st).toAQL()).to.equal('!(x)'); 73 | }); 74 | it('wraps PartialStatement values in parentheses', function () { 75 | var ps = new types._PartialStatement(); 76 | ps.toAQL = function () {return 'x';}; 77 | expect(new UnaryOperation('!', ps).toAQL()).to.equal('!(x)'); 78 | }); 79 | }); -------------------------------------------------------------------------------- /test/types/UpdateExpression.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | UpdateExpression = types.UpdateExpression, 7 | AqlError = require('../../errors').AqlError, 8 | isAqlError = function (e) { 9 | expect(e).to.be.an(AqlError); 10 | }; 11 | 12 | describe('UpdateExpression', function () { 13 | it('returns a statement', function () { 14 | var expr = new UpdateExpression(null, 'x', 'y', 'z'); 15 | expect(expr).to.be.a(types._PartialStatement); 16 | expect(expr.toAQL).to.be.a('function'); 17 | }); 18 | it('generates an UPDATE statement', function () { 19 | expect(new UpdateExpression(null, 'x', 'y', 'z').toAQL()).to.equal('UPDATE x WITH y IN z'); 20 | }); 21 | it('auto-casts expressions', function () { 22 | var arr = [42, 'id', 'some.ref', '"hello"', false, null]; 23 | var ctors = [ 24 | types.IntegerLiteral, 25 | types.Identifier, 26 | types.SimpleReference, 27 | types.StringLiteral, 28 | types.BooleanLiteral, 29 | types.NullLiteral 30 | ]; 31 | for (var i = 0; i < arr.length; i++) { 32 | expect(new UpdateExpression(null, arr[i], 'y', 'z')._expr.constructor).to.equal(ctors[i]); 33 | } 34 | }); 35 | it('wraps Operation expressions in parentheses', function () { 36 | var op = new types._Operation(); 37 | op.toAQL = function () {return 'x';}; 38 | expect(new UpdateExpression(null, op, 'y', 'z').toAQL()).to.equal('UPDATE (x) WITH y IN z'); 39 | }); 40 | it('wraps Statement expressions in parentheses', function () { 41 | var st = new types._Statement(); 42 | st.toAQL = function () {return 'x';}; 43 | expect(new UpdateExpression(null, st, 'y', 'z').toAQL()).to.equal('UPDATE (x) WITH y IN z'); 44 | }); 45 | it('wraps PartialStatement expressions in parentheses', function () { 46 | var ps = new types._PartialStatement(); 47 | ps.toAQL = function () {return 'x';}; 48 | expect(new UpdateExpression(null, ps, 'y', 'z').toAQL()).to.equal('UPDATE (x) WITH y IN z'); 49 | }); 50 | it('allows omitting with-expressions', function () { 51 | expect(new UpdateExpression(null, 'x', undefined, 'z').toAQL()).to.equal('UPDATE x IN z'); 52 | }); 53 | it('auto-casts with-expressions', function () { 54 | var arr = [42, 'id', 'some.ref', '"hello"', false, null]; 55 | var ctors = [ 56 | types.IntegerLiteral, 57 | types.Identifier, 58 | types.SimpleReference, 59 | types.StringLiteral, 60 | types.BooleanLiteral, 61 | types.NullLiteral 62 | ]; 63 | for (var i = 0; i < arr.length; i++) { 64 | expect(new UpdateExpression(null, 'x', arr[i], 'z')._withExpr.constructor).to.equal(ctors[i]); 65 | } 66 | }); 67 | it('wraps Operation with-expressions in parentheses', function () { 68 | var op = new types._Operation(); 69 | op.toAQL = function () {return 'y';}; 70 | expect(new UpdateExpression(null, 'x', op, 'z').toAQL()).to.equal('UPDATE x WITH (y) IN z'); 71 | }); 72 | it('wraps Statement with-expressions in parentheses', function () { 73 | var st = new types._Statement(); 74 | st.toAQL = function () {return 'y';}; 75 | expect(new UpdateExpression(null, 'x', st, 'z').toAQL()).to.equal('UPDATE x WITH (y) IN z'); 76 | }); 77 | it('wraps PartialStatement with-expressions in parentheses', function () { 78 | var ps = new types._PartialStatement(); 79 | ps.toAQL = function () {return 'y';}; 80 | expect(new UpdateExpression(null, 'x', ps, 'z').toAQL()).to.equal('UPDATE x WITH (y) IN z'); 81 | }); 82 | it('wraps well-formed strings as collection names', function () { 83 | var values = [ 84 | '_', 85 | '_x', 86 | 'all_lower_case', 87 | 'snakeCaseAlso', 88 | 'CamelCaseHere', 89 | 'ALL_UPPER_CASE', 90 | '__cRaZy__' 91 | ]; 92 | for (var i = 0; i < values.length; i++) { 93 | expect(new UpdateExpression(null, 'x', 'y', values[i])._collection.toAQL()).to.equal(values[i]); 94 | } 95 | }); 96 | it('does not accept malformed strings as collection names', function () { 97 | var values = [ 98 | '', 99 | '-x', 100 | 'also bad', 101 | 'überbad', 102 | 'spaß' 103 | ]; 104 | for (var i = 0; i < values.length; i++) { 105 | expect(function () {return new UpdateExpression(null, 'x', 'y', values[i]);}).to.throwException(isAqlError); 106 | } 107 | }); 108 | it('does not accept any other values as collection names', function () { 109 | var values = [ 110 | new types.StringLiteral('for'), 111 | new types.RawExpression('for'), 112 | new types.SimpleReference('for'), 113 | new types.Keyword('for'), 114 | new types.NullLiteral(null), 115 | 42, 116 | true, 117 | function () {}, 118 | {}, 119 | [] 120 | ]; 121 | for (var i = 0; i < values.length; i++) { 122 | expect(function () {return new UpdateExpression(null, 'x', 'y', values[i]);}).to.throwException(isAqlError); 123 | } 124 | }); 125 | it('converts preceding nodes to AQL', function () { 126 | var ps = new types._PartialStatement(); 127 | ps.toAQL = function () {return '$';}; 128 | expect(new UpdateExpression(ps, 'x', 'y', 'z').toAQL()).to.equal('$ UPDATE x WITH y IN z'); 129 | }); 130 | describe('options', function () { 131 | var expr = new UpdateExpression(null, 'x', 'y', 'z'); 132 | it('returns a new UpdateExpression', function () { 133 | var optExpr = expr.options({a: 'b'}); 134 | expect(optExpr).to.be.a(types.UpdateExpression); 135 | expect(optExpr.toAQL()).to.equal('UPDATE x WITH y IN z OPTIONS {a: b}'); 136 | }); 137 | }); 138 | describe('returnOld', function () { 139 | var expr = new UpdateExpression(null, 'x', 'y', 'z'); 140 | it('returns a LET RETURN OLD', function () { 141 | var rtrnExpr = expr.returnOld('a'); 142 | expect(rtrnExpr).to.be.a(types.ReturnExpression); 143 | expect(rtrnExpr._value._value).to.equal('a'); 144 | expect(rtrnExpr._prev).to.be.a(types.LetExpression); 145 | rtrnExpr._prev._prev = null; 146 | expect(rtrnExpr.toAQL()).to.equal('LET a = `OLD` RETURN a'); 147 | }); 148 | }); 149 | describe('returnNew', function () { 150 | var expr = new UpdateExpression(null, 'x', 'y', 'z'); 151 | it('returns a LET RETURN NEW', function () { 152 | var rtrnExpr = expr.returnNew('a'); 153 | expect(rtrnExpr).to.be.a(types.ReturnExpression); 154 | expect(rtrnExpr._value._value).to.equal('a'); 155 | expect(rtrnExpr._prev).to.be.a(types.LetExpression); 156 | rtrnExpr._prev._prev = null; 157 | expect(rtrnExpr.toAQL()).to.equal('LET a = `NEW` RETURN a'); 158 | }); 159 | }); 160 | }); 161 | -------------------------------------------------------------------------------- /test/types/UpsertExpression.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true, loopfunc: true */ 2 | /*globals describe: false, it: false */ 3 | 'use strict'; 4 | var expect = require('expect.js'), 5 | types = require('../../types'), 6 | UpsertExpression = types.UpsertExpression; 7 | 8 | describe('UpsertExpression', function () { 9 | it('returns a statement', function () { 10 | var expr = new UpsertExpression(null, 'x', 'y', false, 'z', 'c'); 11 | expect(expr).to.be.a(types._PartialStatement); 12 | expect(expr.toAQL).to.be.a('function'); 13 | }); 14 | it('generates an UPSERT statement', function () { 15 | expect(new UpsertExpression(null, 'x', 'y', false, 'z', 'c').toAQL()).to.equal('UPSERT x INSERT y UPDATE z IN c'); 16 | expect(new UpsertExpression(null, 'x', 'y', true, 'z', 'c').toAQL()).to.equal('UPSERT x INSERT y REPLACE z IN c'); 17 | }); 18 | it('auto-casts expressions', function () { 19 | var arr = [42, 'id', 'some.ref', '"hello"', false, null]; 20 | var ctors = [ 21 | types.IntegerLiteral, 22 | types.Identifier, 23 | types.SimpleReference, 24 | types.StringLiteral, 25 | types.BooleanLiteral, 26 | types.NullLiteral 27 | ]; 28 | for (var i = 0; i < arr.length; i++) { 29 | expect(new UpsertExpression(null, arr[i], 'y', false, 'z', 'c')._upsertExpr.constructor).to.equal(ctors[i]); 30 | expect(new UpsertExpression(null, 'x', arr[i], false, 'z', 'c')._insertExpr.constructor).to.equal(ctors[i]); 31 | expect(new UpsertExpression(null, 'x', 'y', false, arr[i], 'c')._updateOrReplaceExpr.constructor).to.equal(ctors[i]); 32 | } 33 | }); 34 | it('wraps Operation expressions in parentheses', function () { 35 | var op = new types._Operation(); 36 | op.toAQL = function () {return 'x';}; 37 | expect(new UpsertExpression(null, op, 'y', false, 'z', 'c').toAQL()).to.equal('UPSERT (x) INSERT y UPDATE z IN c'); 38 | }); 39 | it('wraps PartialStatement expressions in parentheses', function () { 40 | var ps = new types._PartialStatement(); 41 | ps.toAQL = function () {return 'x';}; 42 | expect(new UpsertExpression(null, ps, 'y', false, 'z', 'c').toAQL()).to.equal('UPSERT (x) INSERT y UPDATE z IN c'); 43 | }); 44 | it('converts preceding nodes to AQL', function () { 45 | var ps = new types._PartialStatement(); 46 | ps.toAQL = function () {return '$';}; 47 | expect(new UpsertExpression(ps, 'x', 'y', false, 'z', 'c').toAQL()).to.equal('$ UPSERT x INSERT y UPDATE z IN c'); 48 | }); 49 | describe('options', function () { 50 | var expr = new UpsertExpression(null, 'x', 'y', false, 'z', 'c'); 51 | it('returns a new UpsertExpression', function () { 52 | var optExpr = expr.options({a: 'b'}); 53 | expect(optExpr).to.be.a(types.UpsertExpression); 54 | expect(optExpr.toAQL()).to.equal('UPSERT x INSERT y UPDATE z IN c OPTIONS {a: b}'); 55 | }); 56 | }); 57 | describe('returnOld', function () { 58 | var expr = new UpsertExpression(null, 'x', 'y', false, 'z', 'c'); 59 | it('returns a LET RETURN OLD', function () { 60 | var rtrnExpr = expr.returnOld('a'); 61 | expect(rtrnExpr).to.be.a(types.ReturnExpression); 62 | expect(rtrnExpr._value._value).to.equal('a'); 63 | expect(rtrnExpr._prev).to.be.a(types.LetExpression); 64 | rtrnExpr._prev._prev = null; 65 | expect(rtrnExpr.toAQL()).to.equal('LET a = `OLD` RETURN a'); 66 | }); 67 | }); 68 | describe('returnNew', function () { 69 | var expr = new UpsertExpression(null, 'x', 'y', false, 'z', 'c'); 70 | it('returns a LET RETURN NEW', function () { 71 | var rtrnExpr = expr.returnNew('a'); 72 | expect(rtrnExpr).to.be.a(types.ReturnExpression); 73 | expect(rtrnExpr._value._value).to.equal('a'); 74 | expect(rtrnExpr._prev).to.be.a(types.LetExpression); 75 | rtrnExpr._prev._prev = null; 76 | expect(rtrnExpr.toAQL()).to.equal('LET a = `NEW` RETURN a'); 77 | }); 78 | }); 79 | }); 80 | --------------------------------------------------------------------------------