1 | # Migrating
|
2 |
|
3 | ## v8 to v9
|
4 |
|
5 | Version 9 reverts the automatic NFC normalization introduced in v7.7.0. This
|
6 | means that arangojs will no longer automatically normalize unicode names and
|
7 | identifiers of collections, graphs, indexes, views, users, databases and so on.
|
8 |
|
9 | If you want to continue using NFC normalization, you can use the `normalize`
|
10 | method available on all JavaScript strings:
|
11 |
|
12 | ```diff
|
13 | import { Database } from "arangojs";
|
14 |
|
15 | const db = new Database();
|
16 | -const collection = db.collection(myUnicodeName);
|
17 | +const collection = db.collection(myUnicodeName.normalize("NFC"));
|
18 | ```
|
19 |
|
20 | Note that ArangoDB may reject non-normalized unicode names and identifiers.
|
21 | This change is intended to make it easier to recognize normalization issues in
|
22 | code interacting with ArangoDB that were previously masked by arangojs.
|
23 |
|
24 | ### Simple queries
|
25 |
|
26 | Simple queries like the `removeByExample` and `firstExample` methods have been
|
27 | removed from the collections API. These methods were deprecated in ArangoDB 3.4
|
28 | and can be replaced with AQL queries. For examples for replicating each
|
29 | method's behavior in AQL, see the documentation for these methods in ArangoJS 8.
|
30 |
|
31 | ### Request and Response changes
|
32 |
|
33 | Version 9 now uses native `fetch` in all environments. This means that the
|
34 | request and response objects exposed by ArangoJS now extend the fetch API's
|
35 | `Request` and `Response` objects rather than those from Node's `http` module
|
36 | and ArangoJS no longer provides the `agentOptions` or `agent` config options.
|
37 |
|
38 | #### Config changes
|
39 |
|
40 | The relevant `agentOptions` have been moved up into the `config` type and
|
41 | in most cases renamed:
|
42 |
|
43 | ```diff
|
44 | const db = new Database({
|
45 | url: "http://localhost:8529",
|
46 | - agentOptions: {
|
47 | - maxSockets: 10,
|
48 | - keepAlive: true,
|
49 | - before: (req) => console.log(String(new Date()), 'requesting', req.url),
|
50 | - after: (res) => console.log(String(new Date()), 'received', res.request.url)
|
51 | - }
|
52 | + poolSize: 10,
|
53 | + keepalive: true,
|
54 | + beforeRequest: (req) => console.log(String(new Date()), 'requesting', req.url),
|
55 | + afterResponse: (res) => console.log(String(new Date()), 'received', res.request.url)
|
56 | });
|
57 | ```
|
58 |
|
59 | If you need to modify the request agent beyond what is possible using the fetch
|
60 | API, you can override Node's default `fetch` Agent using the `undici` module:
|
61 |
|
62 | ```js
|
63 | const { Agent, setGlobalDispatcher } = require("undici");
|
64 |
|
65 | setGlobalDispatcher(
|
66 | new Agent({
|
67 | // your agent options here
|
68 | })
|
69 | );
|
70 | ```
|
71 |
|
72 | Note that you will have to add `undici` as a dependency to your project. There
|
73 | is currently no built-in way to override these options in Node.js without this
|
74 | module.
|
75 |
|
76 | #### Request and Response objects
|
77 |
|
78 | This change mostly affects code that uses the `db.route` API to perform
|
79 | arbitrary requests to the ArangoDB HTTP API.
|
80 |
|
81 | The fetch API `Request` and `Response` objects are a bit different from the
|
82 | equivalent objects previously exposed by these methods. Note that while this
|
83 | means response objects still provide a `body` property, its semantics are very
|
84 | different as the fetch API expects the `blob`, `json` and `text` methods to be
|
85 | used instead. ArangoJS will use the relevant method during response handling
|
86 | and store the result in the `parsedBody` method:
|
87 |
|
88 | ```diff
|
89 | const myFoxxApi = db.route('my/foxx');
|
90 | const res = await myFoxxApi.get();
|
91 | - const token = res.headers['x-auth-token'];
|
92 | - if (res.statusCode === 200) console.log(res.body);
|
93 | + const token = res.headers.get('x-auth-token');
|
94 | + if (res.status === 200) console.log(res.parsedBody);
|
95 | ```
|
96 |
|
97 | ## v7 to v8
|
98 |
|
99 | Version 8 drops support for Internet Explorer 11 and Node.js 10 and 12. If you
|
100 | need to continue supporting Internet Explorer, you can try transpiling arangojs
|
101 | as a dependency using Babel with the relevant polyfills.
|
102 |
|
103 | ### General
|
104 |
|
105 | In TypeScript the type `Dict<T>` has been removed from the `connection` module.
|
106 | The built-in type `Record<string, T>` can be used as a replacement:
|
107 |
|
108 | ```diff
|
109 | import { Database } from "arangojs";
|
110 | -import type { Dict } from "arangojs/connection";
|
111 |
|
112 | const db = new Database();
|
113 | -let deps: Dict<string | string[] | undefined>;
|
114 | +let deps: Record<string, string | string[] | undefined>;
|
115 | deps = await db.getServiceDependencies("/my-foxx-service", true);
|
116 | ```
|
117 |
|
118 | ### Default URL
|
119 |
|
120 | The default URL has been changed to `http://127.0.0.1:8529` to match the ArangoDB
|
121 | default. Previously the default URL was `http://localhost:8529`, which on some
|
122 | systems would resolve to the IPv6 address `::1` instead.
|
123 |
|
124 | If you don't want to use the IPv4 address `127.0.0.1` and instead want to continue
|
125 | letting the operating system resolve `localhost`, you can pass the URL explicitly:
|
126 |
|
127 | ```diff
|
128 | import { Database } from "arangojs";
|
129 |
|
130 | const db = new Database({
|
131 | + url: "http://localhost:8529"
|
132 | });
|
133 | ```
|
134 |
|
135 | ### Databases
|
136 |
|
137 | Previously arangojs allowed changing the database using the deprecated
|
138 | `db.useDatabase` method. This could make it difficult to remember which
|
139 | database you were interacting with. Instead, you should create a new `Database`
|
140 | instance for each database you want to interact with using the `db.database`
|
141 | method:
|
142 |
|
143 | ```diff
|
144 | import { Database } from "arangojs";
|
145 |
|
146 | const db = new Database();
|
147 | -db.useDatabase("database2");
|
148 | +const db2 = db.database("database2");
|
149 | ```
|
150 |
|
151 | ### Queries
|
152 |
|
153 | The functions `aql.literal` and `aql.join` are no longer available as methods
|
154 | on the `aql` template handler and need to be imported separately:
|
155 |
|
156 | ```diff
|
157 | import { aql } from "arangojs";
|
158 | +import { join } from "arangojs/aql";
|
159 |
|
160 | -const filters = aql.join([
|
161 | +const filters = join([
|
162 | aql`FILTER size == 'big'`,
|
163 | aql`FILTER color == 'yellow'`
|
164 | ]);
|
165 | ```
|
166 |
|
167 | ### Users
|
168 |
|
169 | The return values of `db.getUserDatabases` and `db.getUserAccessLevel` have
|
170 | been changed to match the documented return types:
|
171 |
|
172 | ```diff
|
173 | import { Database } from "arangojs";
|
174 |
|
175 | const db = new Database();
|
176 | -const dbs = (await db.getUserDatabases("ash")).result;
|
177 | +const dbs = await db.getUserDatabases("ash");
|
178 | for (const [db, obj] of Object.entries(dbs)) {
|
179 | console.log(`${db}: ${obj.permission}`);
|
180 | for (const [col, access] of Object.entries(obj.collections)) {
|
181 | console.log(`${db}/${col}: ${access}`);
|
182 | }
|
183 | }
|
184 |
|
185 | -const access = (await db.getUserAccessLevel("ash", "pokemons")).result;
|
186 | +const access = await db.getUserAccessLevel("ash", "pokemons");
|
187 | if (access === "rw") {
|
188 | db.collection("pokemons").save({ name: "Pikachu" });
|
189 | }
|
190 | ```
|
191 |
|
192 | ### Graphs
|
193 |
|
194 | In TypeScript the type `GraphCreateOptions` has been renamed to
|
195 | `CreateGraphOptions`:
|
196 |
|
197 | ```diff
|
198 | -import type { GraphCreateOptions } from "arangojs/graph";
|
199 | +import type { CreateGraphOptions } from "arangojs/graph";
|
200 | ```
|
201 |
|
202 | ### Enum re-exports
|
203 |
|
204 | Previously the `CollectionStatus`, `CollectionType` and `ViewType` enums
|
205 | were re-exported by the arangojs main module and could be imported from the
|
206 | `arangojs` package:
|
207 |
|
208 | ```diff
|
209 | -import { CollectionStatus, CollectionType } from "arangojs";
|
210 | +import { CollectionStatus, CollectionType } from "arangojs/collection";
|
211 | ```
|
212 |
|
213 | Note that the `ViewType` enum has been removed completely:
|
214 |
|
215 | ````diff
|
216 | -import { ViewType } from "arangojs";
|
217 | -
|
218 | -const ArangoSearchViewType = ViewType.ARANGOSEARCH_VIEW;
|
219 | +const ArangoSearchViewType = "arangosearch";
|
220 |
|
221 | ## v6 to v7
|
222 |
|
223 | ### Configuration changes
|
224 |
|
225 | The `db.useDatabase` method has been deprecated in v7.
|
226 |
|
227 | Previously the primary use of this method was to set the database name of the
|
228 | arangojs instance. The database name can now be specified using the
|
229 | `databaseName` option in the arangojs configuration:
|
230 |
|
231 | ```diff
|
232 | const db = new Database({
|
233 | url: "http://127.0.0.1:8529",
|
234 | + databaseName: "my_database",
|
235 | });
|
236 | -db.useDatabase("my_database");
|
237 | ````
|
238 |
|
239 | ### Shared connection pool
|
240 |
|
241 | It is now possible to have multiple `Database` objects using the same
|
242 | underlying connection pool:
|
243 |
|
244 | ```diff
|
245 | -const db1 = new Database();
|
246 | -db1.useDatabase("database1");
|
247 | -const db2 = new Database();
|
248 | -db2.useDatabase("database2");
|
249 | +const db1 = new Database({ databaseName: "database1" });
|
250 | +const db2 = db1.database("database2");
|
251 | ```
|
252 |
|
253 | ### Indexes
|
254 |
|
255 | The helper methods for creating specific index types, e.g. `createHashIndex`,
|
256 | have been removed and replaced with the generic `ensureIndex` method (which
|
257 | was previously called `createIndex`):
|
258 |
|
259 | ```diff
|
260 | -await collection.createGeoIndex(["lat", "lng"]);
|
261 | +await collection.ensureIndex({ type: "geo", fields: ["lat", "lng"] });
|
262 | ```
|
263 |
|
264 | ### Document and edge collections
|
265 |
|
266 | Version 7 no longer provides different methods for accessing document and edge
|
267 | collections as both types are now implemented using the same underlying class:
|
268 |
|
269 | ```diff
|
270 | const myDocumentCollection = db.collection("documents");
|
271 | -const myEdgeCollection = db.edgeCollection("edges");
|
272 | +const myEdgeCollection = db.collection("edges");
|
273 | ```
|
274 |
|
275 | When using TypeScript the collection instances can still be cast to the more
|
276 | specific `DocumentCollection` and `EdgeCollection` interfaces:
|
277 |
|
278 | ```ts
|
279 | interface EdgeType {
|
280 | color: string;
|
281 | }
|
282 | const myEdgeCollection = db.collection("edges") as EdgeCollection<EdgeType>;
|
283 | ```
|
284 |
|
285 | ### Saving edge documents
|
286 |
|
287 | The `save` method no longer supports positional arguments for `_from` and `_to`
|
288 | values. These now need to be supplied as part of the document data:
|
289 |
|
290 | ```diff
|
291 | await edges.save(
|
292 | - "vertices/start",
|
293 | - "vertices/end",
|
294 | - { color: "red" }
|
295 | + { _from: "vertices/start", _to: "vertices/end", color: "red" }
|
296 | );
|
297 | ```
|
298 |
|
299 | ### Accessing documents
|
300 |
|
301 | The `edge` method has been removed from the low-level collection API as it was
|
302 | an alias for the `document` method, which still exists:
|
303 |
|
304 | ```diff
|
305 | -const edges = db.edgeCollection("edges");
|
306 | -const edge = await edges.edge("my-edge");
|
307 | +const edges = db.collection("edges");
|
308 | +const edge = await edges.document("my-edge");
|
309 | ```
|
310 |
|
311 | Graph vertex and edge collections instead only retain their specific `vertex`
|
312 | and `edge` methods which access the collection using the high-level graph API:
|
313 |
|
314 | ```diff
|
315 | const vertices = graph.vertexCollection("vertices");
|
316 | -const vertex = await vertices.document("my-vertex");
|
317 | +const vertex = await vertices.vertex("my-vertex");
|
318 |
|
319 | const edges = graph.edgeCollection("edges");
|
320 | -const edge = await edges.document("my-edge");
|
321 | +const edge = await edges.edge("my-edge");
|
322 | ```
|
323 |
|
324 | ### Graph collections
|
325 |
|
326 | Graph vertex and edge collections no longer implement the generic collection
|
327 | API methods to avoid confusion between operations that are aware of the graph
|
328 | definition (and can trigger graph-related side-effects) and those that directly
|
329 | access low-level operations.
|
330 |
|
331 | As a convenience both graph collection types still provide access to the
|
332 | low-level collection interface via the `collection` property:
|
333 |
|
334 | ```diff
|
335 | const graphEdges = graph.edgeCollection("edges");
|
336 | -const outEdges = graphEdges.outEdges("vertices/start");
|
337 | +const outEdges = graphEdges.collection.outEdges("vertices/start");
|
338 | ```
|
339 |
|
340 | ### Cursor methods
|
341 |
|
342 | The method `each` is now called `forEach`. The method `hasNext` has been
|
343 | replaced with a getter.
|
344 |
|
345 | The methods `some` and `every` have been removed. These methods previously
|
346 | allowed iterating over cursor results in order to derive a boolean value by
|
347 | applying a callback function to each value in the result.
|
348 |
|
349 | In most cases these methods can be avoided by writing a more efficient AQL
|
350 | query:
|
351 |
|
352 | ```diff
|
353 | -const cursor = await db.query(aql`
|
354 | - FOR bowl IN porridges
|
355 | - RETURN bowl
|
356 | -`);
|
357 | -const someJustRight = await cursor.some(
|
358 | - (bowl) => bowl.temperature < TOO_HOT && bowl.temperature > TOO_COLD
|
359 | -);
|
360 | +const cursor = await db.query(aql`
|
361 | + FOR bowl IN porridges
|
362 | + FILTER bowl.temperature < ${TOO_HOT}
|
363 | + FILTER bowl.temperature > ${TOO_COLD}
|
364 | + LIMIT 1
|
365 | + RETURN 1
|
366 | +`);
|
367 | +const someJustRight = Boolean(await cursor.next());
|
368 | ```
|
369 |
|
370 | If this is not an option, the old behavior can be emulated using the `forEach`
|
371 | method (previously called `each`) instead:
|
372 |
|
373 | ```diff
|
374 | -const someJustRight = await cursor.some(
|
375 | - (bowl) => bowl.temperature < TOO_HOT && bowl.temperature > TOO_COLD
|
376 | -);
|
377 | +const someJustRight = !(await cursor.forEach(
|
378 | + (bowl) => bowl.temperature === TOO_HOT || bowl.temperature === TOO_COLD
|
379 | +));
|
380 | ```
|
381 |
|
382 | ### Batch cursor API
|
383 |
|
384 | Cursors now provide a low-level API for iterating over the result batches
|
385 | instead of individual items, which is exposed via the `batches` property.
|
386 |
|
387 | The methods `hasMore` and `nextBatch` have been replaced with the getter
|
388 | `batches.hasMore` and the method `batches.next`:
|
389 |
|
390 | ```diff
|
391 | -if (cursor.hasMore()) {
|
392 | - return await cursor.nextBatch();
|
393 | +if (cursor.batches.hasMore) {
|
394 | + return await cursor.batches.next();
|
395 | }
|
396 | ```
|
397 |
|
398 | ### Simple queries
|
399 |
|
400 | Collection methods for using simple queries (e.g. `all`, `any` and `list`)
|
401 | have been deprecated in ArangoDB 3.0 and are now also deprecated in arangojs.
|
402 |
|
403 | See the documentation of each method for an example for how to perform the same
|
404 | query using an AQL query instead.
|
405 |
|
406 | Additionally the `list` method now returns a cursor instead of an array.
|
407 |
|
408 | ### ArangoSearch Views
|
409 |
|
410 | The database methods `arangoSearchView` and `createArangoSearchView` have been
|
411 | renamed to `view` and `createView` respectively as there currently is no other
|
412 | view type available in ArangoDB:
|
413 |
|
414 | ```diff
|
415 | -await db.createArangoSearchView("my-view");
|
416 | -const view = db.arangoSearchView("my-view");
|
417 | +await db.createView("my-view");
|
418 | +const view = db.view("my-view");
|
419 | ```
|
420 |
|
421 | ### Query options
|
422 |
|
423 | The `options` argument of `db.query` has been flattened. Options that were
|
424 | previously nested in an `options` property of that argument are now specified
|
425 | directly on the argument itself:
|
426 |
|
427 | ```diff
|
428 | const cursor = await db.query(
|
429 | aql`
|
430 | FOR doc IN ${collection}
|
431 | RETURN doc
|
432 | `,
|
433 | {
|
434 | cache: false,
|
435 | - options: { fullCount: true },
|
436 | + fullCount: true,
|
437 | }
|
438 | );
|
439 | ```
|
440 |
|
441 | ### Bulk imports
|
442 |
|
443 | The default value of the `type` option now depends on the input type instead
|
444 | of always defaulting to `"auto"`. If you previously relied on the default
|
445 | value being set to `"auto"`, you may now need to explicitly set this option:
|
446 |
|
447 | ```diff
|
448 | -await collection.import(data);
|
449 | +await collection.import(data, { type: "auto" });
|
450 | ```
|
451 |
|
452 | ### Bulk operations
|
453 |
|
454 | The collection method `bulkUpdate` has been removed and the methods
|
455 | `save`, `update`, `replace` and `remove` no longer accept arrays as input.
|
456 |
|
457 | Bulk operations can now be performed using the dedicated methods
|
458 | `saveAll`, `updateAll`, `replaceAll` and `removeAll`:
|
459 |
|
460 | ```diff
|
461 | -await collection.save([{ _key: "a" }, { _key: "b" }]);
|
462 | +await collection.saveAll([{ _key: "a" }, { _key: "b" }]);
|
463 | ```
|
464 |
|
465 | ### Cross-collection operations
|
466 |
|
467 | Collection methods no longer accept document IDs from other collections.
|
468 | Previously passing a document ID referring to a different collection would
|
469 | result in the collection performing a request to that collection instead. Now
|
470 | mismatching IDs will result in an error instead:
|
471 |
|
472 | ```js
|
473 | const collection1 = db.collection("collection1");
|
474 | const doc = await collection1.document("collection2/xyz"); // ERROR
|
475 | ```
|
476 |
|
477 | ### Creating graphs
|
478 |
|
479 | The signatures of `db.createGraph` and `graph.create` have changed to always
|
480 | take an array of edge definitions as the first argument instead of taking the
|
481 | edge definitions as a property of the `properties` argument.
|
482 |
|
483 | Additionally the `properties` and `options` arguments have been merged:
|
484 |
|
485 | ```diff
|
486 | await graph.create(
|
487 | + [{ collection: "edges", from: ["a"], to: ["b"] }],
|
488 | {
|
489 | - edgeDefinitions: [{ collection: "edges", from: ["a"], to: ["b"] }],
|
490 | isSmart: true,
|
491 | - },
|
492 | - {
|
493 | waitForSync: true,
|
494 | }
|
495 | );
|
496 | ```
|
497 |
|
498 | ### Transactions
|
499 |
|
500 | The transaction method `run` has been renamed to `step` to make it more obvious
|
501 | that it is intended to only perform a single "step" of the transaction.
|
502 |
|
503 | See the method's documentation for examples of how to use the method correctly.
|
504 |
|
505 | Additionally the method `transaction` no longer acts as an alias for
|
506 | `executeTransaction`:
|
507 |
|
508 | ```diff
|
509 | -const result = await db.transaction(collections, action);
|
510 | +const result = await db.executeTransaction(collections, action);
|
511 | ```
|
512 |
|
513 | ### Service development mode
|
514 |
|
515 | The methods `enableServiceDevelopmentMode` and `disableServiceDevelopmentMode`
|
516 | have been replaced with the method `setServiceDevelopmentMode`:
|
517 |
|
518 | ```diff
|
519 | -await db.enableServiceDevelopmentMode("/my-foxx");
|
520 | +await db.setServiceDevelopmentMode("/my-foxx", true);
|
521 | ```
|
522 |
|
523 | ### System services
|
524 |
|
525 | The default value of the method `listServices` option `excludeSystem` has been
|
526 | changed from `false` to `true`:
|
527 |
|
528 | ```diff
|
529 | -const services = await db.listServices(true);
|
530 | +const services = await db.listServices();
|
531 | ```
|
532 |
|
533 | ### Query tracking
|
534 |
|
535 | The method `setQueryTracking` has been merged into `queryTracking`:
|
536 |
|
537 | ```diff
|
538 | -await db.setQueryTracking({ enabled: true });
|
539 | +await db.queryTracking({ enabled: true });
|
540 | ```
|
541 |
|
542 | ### Collection properties
|
543 |
|
544 | The method `setProperties` has been merged into `properties`:
|
545 |
|
546 | ```diff
|
547 | -await collection.setProperties({ waitForSync: true });
|
548 | +await collection.properties({ waitForSync: true });
|
549 | ```
|
550 |
|
551 | ### View properties
|
552 |
|
553 | The View method `setProperties` has been renamed to `updateProperties`:
|
554 |
|
555 | ```diff
|
556 | -await view.setProperties({ consolidationIntervalMsec: 234 });
|
557 | +await view.updateProperties({ consolidationIntervalMsec: 234 });
|
558 | ```
|
559 |
|
560 | ### Truncating collections
|
561 |
|
562 | The `db.truncate` method has been removed. The behavior can still be mimicked
|
563 | using the `db.collections` and `collection.truncate` methods:
|
564 |
|
565 | ```diff
|
566 | -await db.truncate();
|
567 | +await Promise.all(
|
568 | + db.collections().map((collection) => collection.truncate())
|
569 | +);
|
570 | ```
|