UNPKG

arangojs

Version:

The official ArangoDB JavaScript driver.

780 lines 25.2 kB
/** * ```ts * import type { * Graph, * GraphVertexCollection, * GraphEdgeCollection, * } from "arangojs/graph.js"; * ``` * * The "graph" module provides graph related types and interfaces * for TypeScript. * * @packageDocumentation */ import { collectionToString, } from "./collection.js"; import { _documentHandle, } from "./documents.js"; import { isArangoError } from "./error.js"; import { DOCUMENT_NOT_FOUND, GRAPH_NOT_FOUND } from "./lib/codes.js"; /** * Indicates whether the given value represents a {@link graph.Graph}. * * @param graph - A value that might be a Graph. */ export function isArangoGraph(graph) { return Boolean(graph && graph.isArangoGraph); } /** * @internal */ function mungeGharialResponse(body, prop) { const { new: newDoc, old: oldDoc, [prop]: doc, ...meta } = body; const result = { ...meta, ...doc }; if (typeof newDoc !== "undefined") result.new = newDoc; if (typeof oldDoc !== "undefined") result.old = oldDoc; return result; } /** * @internal */ function coerceEdgeDefinition(options) { const edgeDefinition = {}; edgeDefinition.collection = collectionToString(options.collection); edgeDefinition.from = Array.isArray(options.from) ? options.from.map(collectionToString) : [collectionToString(options.from)]; edgeDefinition.to = Array.isArray(options.to) ? options.to.map(collectionToString) : [collectionToString(options.to)]; return edgeDefinition; } /** * Represents a {@link collection.DocumentCollection} of vertices in a {@link graph.Graph}. * * @param T - Type to use for document data. Defaults to `any`. */ export class GraphVertexCollection { _db; _name; _graph; _collection; /** * @internal */ constructor(db, name, graph) { this._db = db; this._collection = db.collection(name); this._name = this._collection.name; this._graph = graph; } /** * @internal * * Indicates that this object represents an ArangoDB collection. */ get isArangoCollection() { return true; } /** * Name of the collection. */ get name() { return this._name; } /** * A {@link collection.DocumentCollection} instance for this vertex collection. */ get collection() { return this._collection; } /** * The {@link graph.Graph} instance this vertex collection is bound to. */ get graph() { return this._graph; } /** * Checks whether a vertex matching the given key or id exists in this * collection. * * Throws an exception when passed a vertex or `_id` from a different * collection. * * @param selector - Document `_key`, `_id` or object with either of those * properties (e.g. a vertex from this collection). * * @example * ```js * const graph = db.graph("some-graph"); * const collection = graph.vertexCollection("vertices"); * const exists = await collection.vertexExists("abc123"); * if (!exists) { * console.log("Vertex does not exist"); * } * ``` */ async vertexExists(selector) { try { return await this._db.request({ method: "HEAD", path: `/_api/gharial/${encodeURIComponent(this.graph.name)}/vertex/${encodeURI(_documentHandle(selector, this._name))}`, }, () => true); } catch (err) { if (err.code === 404) { return false; } throw err; } } async vertex(selector, options = {}) { if (typeof options === "boolean") { options = { graceful: options }; } const { allowDirtyRead = undefined, graceful = false, rev, ...search } = options; const headers = {}; if (rev) headers["if-match"] = rev; const result = this._db.request({ path: `/_api/gharial/${encodeURIComponent(this.graph.name)}/vertex/${encodeURI(_documentHandle(selector, this._name))}`, headers, search, allowDirtyRead, }, (res) => res.parsedBody.vertex); if (!graceful) return result; try { return await result; } catch (err) { if (isArangoError(err) && err.errorNum === DOCUMENT_NOT_FOUND) { return null; } throw err; } } save(data, options) { return this._db.request({ method: "POST", path: `/_api/gharial/${encodeURIComponent(this.graph.name)}/vertex/${encodeURIComponent(this._name)}`, body: data, search: options, }, (res) => mungeGharialResponse(res.parsedBody, "vertex")); } replace(selector, newValue, options = {}) { if (typeof options === "string") { options = { rev: options }; } const { rev, ...search } = options; const headers = {}; if (rev) headers["if-match"] = rev; return this._db.request({ method: "PUT", path: `/_api/gharial/${encodeURIComponent(this.graph.name)}/vertex/${encodeURI(_documentHandle(selector, this._name))}`, body: newValue, search, headers, }, (res) => mungeGharialResponse(res.parsedBody, "vertex")); } update(selector, newValue, options = {}) { if (typeof options === "string") { options = { rev: options }; } const headers = {}; const { rev, ...search } = options; if (rev) headers["if-match"] = rev; return this._db.request({ method: "PATCH", path: `/_api/gharial/${encodeURIComponent(this.graph.name)}/vertex/${encodeURI(_documentHandle(selector, this._name))}`, body: newValue, search, headers, }, (res) => mungeGharialResponse(res.parsedBody, "vertex")); } remove(selector, options = {}) { if (typeof options === "string") { options = { rev: options }; } const headers = {}; const { rev, ...search } = options; if (rev) headers["if-match"] = rev; return this._db.request({ method: "DELETE", path: `/_api/gharial/${encodeURIComponent(this.graph.name)}/vertex/${encodeURI(_documentHandle(selector, this._name))}`, search, headers, }, (res) => mungeGharialResponse(res.parsedBody, "removed")); } } /** * Represents a {@link collection.EdgeCollection} of edges in a {@link graph.Graph}. * * @param T - Type to use for document data. Defaults to `any`. */ export class GraphEdgeCollection { _db; _name; _graph; _collection; /** * @internal */ constructor(db, name, graph) { this._db = db; this._collection = db.collection(name); this._name = this._collection.name; this._graph = graph; } /** * @internal * * Indicates that this object represents an ArangoDB collection. */ get isArangoCollection() { return true; } /** * Name of the collection. */ get name() { return this._name; } /** * A {@link collection.EdgeCollection} instance for this edge collection. */ get collection() { return this._collection; } /** * The {@link graph.Graph} instance this edge collection is bound to. */ get graph() { return this._graph; } /** * Checks whether a edge matching the given key or id exists in this * collection. * * Throws an exception when passed a edge or `_id` from a different * collection. * * @param selector - Document `_key`, `_id` or object with either of those * properties (e.g. a edge from this collection). * * @example * ```js * const graph = db.graph("some-graph"); * const collection = graph.edgeCollection("friends") * const exists = await collection.edgeExists("abc123"); * if (!exists) { * console.log("Edge does not exist"); * } * ``` */ async edgeExists(selector) { try { return await this._db.request({ method: "HEAD", path: `/_api/gharial/${encodeURIComponent(this.graph.name)}/edge/${encodeURI(_documentHandle(selector, this._name))}`, }, () => true); } catch (err) { if (err.code === 404) { return false; } throw err; } } async edge(selector, options = {}) { if (typeof options === "boolean") { options = { graceful: options }; } const { allowDirtyRead = undefined, graceful = false, rev, ...search } = options; const headers = {}; if (rev) headers["if-match"] = rev; const result = this._db.request({ path: `/_api/gharial/${encodeURIComponent(this.graph.name)}/edge/${encodeURI(_documentHandle(selector, this._name))}`, search, allowDirtyRead, }, (res) => res.parsedBody.edge); if (!graceful) return result; try { return await result; } catch (err) { if (isArangoError(err) && err.errorNum === DOCUMENT_NOT_FOUND) { return null; } throw err; } } save(data, options) { return this._db.request({ method: "POST", path: `/_api/gharial/${encodeURIComponent(this.graph.name)}/edge/${encodeURIComponent(this._name)}`, body: data, search: options, }, (res) => mungeGharialResponse(res.parsedBody, "edge")); } replace(selector, newValue, options = {}) { if (typeof options === "string") { options = { rev: options }; } const { rev, ...search } = options; const headers = {}; if (rev) headers["if-match"] = rev; return this._db.request({ method: "PUT", path: `/_api/gharial/${encodeURIComponent(this.graph.name)}/edge/${encodeURI(_documentHandle(selector, this._name))}`, body: newValue, search, headers, }, (res) => mungeGharialResponse(res.parsedBody, "edge")); } update(selector, newValue, options = {}) { if (typeof options === "string") { options = { rev: options }; } const { rev, ...search } = options; const headers = {}; if (rev) headers["if-match"] = rev; return this._db.request({ method: "PATCH", path: `/_api/gharial/${encodeURIComponent(this.graph.name)}/edge/${encodeURI(_documentHandle(selector, this._name))}`, body: newValue, search, headers, }, (res) => mungeGharialResponse(res.parsedBody, "edge")); } remove(selector, options = {}) { if (typeof options === "string") { options = { rev: options }; } const { rev, ...search } = options; const headers = {}; if (rev) headers["if-match"] = rev; return this._db.request({ method: "DELETE", path: `/_api/gharial/${encodeURIComponent(this.graph.name)}/edge/${encodeURI(_documentHandle(selector, this._name))}`, search, headers, }, (res) => mungeGharialResponse(res.parsedBody, "removed")); } } /** * Represents a graph in a {@link database.Database}. */ export class Graph { _name; _db; /** * @internal */ constructor(db, name) { this._name = name; this._db = db; } /** * @internal * * Indicates that this object represents an ArangoDB Graph. */ get isArangoGraph() { return true; } /** * Name of the graph. */ get name() { return this._name; } /** * Checks whether the graph exists. * * @example * ```js * const db = new Database(); * const graph = db.graph("some-graph"); * const result = await graph.exists(); * // result indicates whether the graph exists * ``` */ async exists() { try { await this.get(); return true; } catch (err) { if (isArangoError(err) && err.errorNum === GRAPH_NOT_FOUND) { return false; } throw err; } } /** * Retrieves general information about the graph. * * @example * ```js * const db = new Database(); * const graph = db.graph("some-graph"); * const data = await graph.get(); * // data contains general information about the graph * ``` */ get() { return this._db.request({ path: `/_api/gharial/${encodeURIComponent(this._name)}` }, (res) => res.parsedBody.graph); } /** * Creates a graph with the given `edgeDefinitions` and `options` for this * graph's name. * * @param edgeDefinitions - Definitions for the relations of the graph. * @param options - Options for creating the graph. * * @example * ```js * const db = new Database(); * const graph = db.graph("some-graph"); * const info = await graph.create([ * { * collection: "edges", * from: ["start-vertices"], * to: ["end-vertices"], * }, * ]); * // graph now exists * ``` */ create(edgeDefinitions, options = {}) { const { orphanCollections, satellites, waitForSync, isSmart, ...opts } = options; return this._db.request({ method: "POST", path: "/_api/gharial", body: { orphanCollections: orphanCollections && (Array.isArray(orphanCollections) ? orphanCollections.map(collectionToString) : [collectionToString(orphanCollections)]), edgeDefinitions: edgeDefinitions.map(coerceEdgeDefinition), isSmart, name: this._name, options: { ...opts, satellites: satellites?.map(collectionToString) }, }, search: { waitForSync }, }, (res) => res.parsedBody.graph); } /** * Deletes the graph from the database. * * @param dropCollections - If set to `true`, the collections associated with * the graph will also be deleted. * * @example * ```js * const db = new Database(); * const graph = db.graph("some-graph"); * await graph.drop(); * // the graph "some-graph" no longer exists * ``` */ drop(dropCollections = false) { return this._db.request({ method: "DELETE", path: `/_api/gharial/${encodeURIComponent(this._name)}`, search: { dropCollections }, }, (res) => res.parsedBody.removed); } /** * Returns a {@link graph.GraphVertexCollection} instance for the given collection * name representing the collection in this graph. * * @param T - Type to use for document data. Defaults to `any`. * @param collection - Name of the vertex collection. */ vertexCollection(collection) { return new GraphVertexCollection(this._db, collectionToString(collection), this); } /** * Fetches all vertex collections of this graph from the database and returns * an array of their names. * * See also {@link graph.Graph#vertexCollections}. * * @example * ```js * const db = new Database(); * const graph = db.graph("some-graph"); * const info = await graph.create([ * { * collection: "edges", * from: ["start-vertices"], * to: ["end-vertices"], * }, * ]); * const vertexCollectionNames = await graph.listVertexCollections(); * // ["start-vertices", "end-vertices"] * ``` */ listVertexCollections() { return this._db.request({ path: `/_api/gharial/${encodeURIComponent(this._name)}/vertex` }, (res) => res.parsedBody.collections); } /** * Fetches all vertex collections of this graph from the database and returns * an array of {@link graph.GraphVertexCollection} instances. * * See also {@link graph.Graph#listVertexCollections}. * * @example * ```js * const db = new Database(); * const graph = db.graph("some-graph"); * const info = await graph.create([ * { * collection: "edges", * from: ["start-vertices"], * to: ["end-vertices"], * }, * ]); * const vertexCollections = await graph.vertexCollections(); * for (const vertexCollection of vertexCollections) { * console.log(vertexCollection.name); * // "start-vertices" * // "end-vertices" * } * ``` */ async vertexCollections() { const names = await this.listVertexCollections(); return names.map((name) => new GraphVertexCollection(this._db, name, this)); } /** * Adds the given collection to this graph as a vertex collection. * * @param collection - Collection to add to the graph. * * @example * ```js * const db = new Database(); * const graph = db.graph("some-graph"); * await graph.addVertexCollection("more-vertices"); * // The collection "more-vertices" has been added to the graph * const extra = db.collection("extra-vertices"); * await graph.addVertexCollection(extra); * // The collection "extra-vertices" has been added to the graph * ``` */ addVertexCollection(collection, options = {}) { const { satellites, ...opts } = options; return this._db.request({ method: "POST", path: `/_api/gharial/${encodeURIComponent(this._name)}/vertex`, body: { collection: collectionToString(collection), options: { ...opts, satellites: satellites?.map(collectionToString) }, }, }, (res) => res.parsedBody.graph); } /** * Removes the given collection from this graph as a vertex collection. * * @param collection - Collection to remove from the graph. * @param dropCollection - If set to `true`, the collection will also be * deleted from the database. * * @example * ```js * const db = new Database(); * const graph = db.graph("some-graph"); * const info = await graph.create([ * { * collection: "edges", * from: ["start-vertices"], * to: ["end-vertices"], * }, * ]); * await graph.removeVertexCollection("start-vertices"); * // The collection "start-vertices" is no longer part of the graph. * ``` */ removeVertexCollection(collection, dropCollection = false) { return this._db.request({ method: "DELETE", path: `/_api/gharial/${encodeURIComponent(this._name)}/vertex/${encodeURIComponent(collectionToString(collection))}`, search: { dropCollection, }, }, (res) => res.parsedBody.graph); } /** * Returns a {@link graph.GraphEdgeCollection} instance for the given collection * name representing the collection in this graph. * * @param T - Type to use for document data. Defaults to `any`. * @param collection - Name of the edge collection. * * @example * ```js * const db = new Database(); * const graph = db.graph("some-graph"); * const info = await graph.create([ * { * collection: "edges", * from: ["start-vertices"], * to: ["end-vertices"], * }, * ]); * const graphEdgeCollection = graph.edgeCollection("edges"); * // Access the underlying EdgeCollection API: * const edgeCollection = graphEdgeCollection.collection; * ``` */ edgeCollection(collection) { return new GraphEdgeCollection(this._db, collectionToString(collection), this); } /** * Fetches all edge collections of this graph from the database and returns * an array of their names. * * See also {@link graph.Graph#edgeCollections}. * * @example * ```js * const db = new Database(); * const graph = db.graph("some-graph"); * const info = await graph.create([ * { * collection: "edges", * from: ["start-vertices"], * to: ["end-vertices"], * }, * ]); * const edgeCollectionNames = await graph.listEdgeCollections(); * // ["edges"] * ``` */ listEdgeCollections() { return this._db.request({ path: `/_api/gharial/${encodeURIComponent(this._name)}/edge` }, (res) => res.parsedBody.collections); } /** * Fetches all edge collections of this graph from the database and returns * an array of {@link graph.GraphEdgeCollection} instances. * * See also {@link graph.Graph#listEdgeCollections}. * * @example * ```js * const db = new Database(); * const graph = db.graph("some-graph"); * const info = await graph.create([ * { * collection: "edges", * from: ["start-vertices"], * to: ["end-vertices"], * }, * ]); * const graphEdgeCollections = await graph.edgeCollections(); * for (const collection of graphEdgeCollection) { * console.log(collection.name); * // "edges" * } * ``` */ async edgeCollections() { const names = await this.listEdgeCollections(); return names.map((name) => new GraphEdgeCollection(this._db, name, this)); } /** * Adds an edge definition to this graph. * * @param edgeDefinition - Definition of a relation in this graph. * * @example * ```js * const db = new Database(); * const graph = db.graph("some-graph"); * await graph.addEdgeDefinition({ * collection: "edges", * from: ["start-vertices"], * to: ["end-vertices"], * }); * // The edge definition has been added to the graph * ``` */ addEdgeDefinition(edgeDefinition, options = {}) { const { satellites, ...opts } = options; return this._db.request({ method: "POST", path: `/_api/gharial/${encodeURIComponent(this._name)}/edge`, body: { ...coerceEdgeDefinition(edgeDefinition), options: { ...opts, satellites: satellites?.map(collectionToString) }, }, }, (res) => res.parsedBody.graph); } replaceEdgeDefinition(collectionOrEdgeDefinitionOptions, edgeDefinitionOrOptions, options = {}) { let collection = collectionOrEdgeDefinitionOptions; let edgeDefinition = edgeDefinitionOrOptions; if (edgeDefinitionOrOptions && !edgeDefinitionOrOptions.hasOwnProperty("collection")) { options = edgeDefinitionOrOptions; edgeDefinitionOrOptions = undefined; } if (!edgeDefinitionOrOptions) { edgeDefinition = collectionOrEdgeDefinitionOptions; collection = edgeDefinition.collection; } const { satellites, ...opts } = options; return this._db.request({ method: "PUT", path: `/_api/gharial/${encodeURIComponent(this._name)}/edge/${encodeURIComponent(collectionToString(collection))}`, body: { ...coerceEdgeDefinition(edgeDefinition), options: { ...opts, satellites: satellites?.map(collectionToString) }, }, }, (res) => res.parsedBody.graph); } /** * Removes the edge definition for the given edge collection from this graph. * * @param collection - Edge collection for which to remove the definition. * @param dropCollection - If set to `true`, the collection will also be * deleted from the database. * * @example * ```js * const db = new Database(); * const graph = db.graph("some-graph"); * const info = await graph.create([ * { * collection: "edges", * from: ["start-vertices"], * to: ["end-vertices"], * }, * ]); * await graph.removeEdgeDefinition("edges"); * // The edge definition for "edges" has been replaced * ``` */ removeEdgeDefinition(collection, dropCollection = false) { return this._db.request({ method: "DELETE", path: `/_api/gharial/${encodeURIComponent(this._name)}/edge/${encodeURIComponent(collectionToString(collection))}`, search: { dropCollection, }, }, (res) => res.parsedBody.graph); } } //# sourceMappingURL=graph.js.map