arangojs
Version:
The official ArangoDB JavaScript driver.
780 lines • 25.2 kB
JavaScript
/**
* ```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