/* jshint strict: false */ // ////////////////////////////////////////////////////////////////////////////// // / @brief ArangoDatabase // / // / @file // / // / DISCLAIMER // / // / Copyright 2013 triagens GmbH, Cologne, Germany // / // / Licensed under the Apache License, Version 2.0 (the "License") // / you may not use this file except in compliance with the License. // / You may obtain a copy of the License at // / // / http://www.apache.org/licenses/LICENSE-2.0 // / // / Unless required by applicable law or agreed to in writing, software // / distributed under the License is distributed on an "AS IS" BASIS, // / WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // / See the License for the specific language governing permissions and // / limitations under the License. // / // / Copyright holder is triAGENS GmbH, Cologne, Germany // / // / @author Achim Brandt // / @author Dr. Frank Celler // / @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany // ////////////////////////////////////////////////////////////////////////////// var internal = require('internal'); var arangosh = require('@arangodb/arangosh'); // ////////////////////////////////////////////////////////////////////////////// // / @brief constructor // ////////////////////////////////////////////////////////////////////////////// var ArangoCollection; var ArangoView; function ArangoDatabase (connection) { this._connection = connection; this._collectionConstructor = ArangoCollection; this._viewConstructor = ArangoView; this._properties = null; this._registerCollection = function (name, obj) { // store the collection in our own list this[name] = obj; }; this._viewList = {}; this._registerView = function (name, obj) { // store the view in our own list this._viewList[name] = obj; }; this._unregisterView = function(name) { if (this._viewList[name] !== undefined) { delete this._viewList[name]; } }; this._renameView = function (from, to) { // store the view in our own list this._viewList[to] = this._viewList[from]; delete this._viewList[from]; }; } exports.ArangoDatabase = ArangoDatabase; // load after exporting ArangoDatabase ArangoCollection = require('@arangodb/arango-collection').ArangoCollection; ArangoView = require('@arangodb/arango-view').ArangoView; var ArangoError = require('@arangodb').ArangoError; var ArangoStatement = require('@arangodb/arango-statement').ArangoStatement; // ////////////////////////////////////////////////////////////////////////////// // / @brief index id regex // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.indexRegex = /^([a-zA-Z0-9\-_]+)\/([0-9]+)$/; // ////////////////////////////////////////////////////////////////////////////// // / @brief key regex // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.keyRegex = /^([a-zA-Z0-9_:\-@\.\(\)\+,=;\$!\*'%])+$/; // ////////////////////////////////////////////////////////////////////////////// // / @brief append the waitForSync parameter to a URL // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._appendSyncParameter = function (url, waitForSync) { if (waitForSync) { if (url.indexOf('?') === -1) { url += '?'; } else { url += '&'; } url += 'waitForSync=true'; } return url; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief append some boolean parameter to a URL // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._appendBoolParameter = function (url, name, val) { if (url.indexOf('?') === -1) { url += '?'; } else { url += '&'; } url += name + (val ? '=true' : '=false'); return url; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief return the base url for collection usage // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._collectionurl = function (id) { if (id === undefined) { return '/_api/collection'; } return '/_api/collection/' + encodeURIComponent(id); }; // ////////////////////////////////////////////////////////////////////////////// // / @brief return the base url for view usage // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._viewurl = function (id) { if (id === undefined) { return '/_api/view'; } return '/_api/view/' + encodeURIComponent(id); }; // ////////////////////////////////////////////////////////////////////////////// // / @brief return the base url for document usage // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._documenturl = function (id, expectedName) { var s = id.split('/'); if (s.length !== 2) { throw new ArangoError({ error: true, code: internal.errors.ERROR_HTTP_BAD_PARAMETER.code, errorNum: internal.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code, errorMessage: internal.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.message }); } else if (expectedName !== undefined && expectedName !== '' && s[0] !== expectedName) { throw new ArangoError({ error: true, code: internal.errors.ERROR_HTTP_BAD_PARAMETER.code, errorNum: internal.errors.ERROR_ARANGO_CROSS_COLLECTION_REQUEST.code, errorMessage: internal.errors.ERROR_ARANGO_CROSS_COLLECTION_REQUEST.message }); } if (ArangoDatabase.keyRegex.exec(s[1]) === null) { throw new ArangoError({ error: true, code: internal.errors.ERROR_HTTP_BAD_PARAMETER.code, errorNum: internal.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code, errorMessage: internal.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.message }); } return '/_api/document/' + encodeURIComponent(s[0]) + '/' + encodeURIComponent(s[1]); }; // ////////////////////////////////////////////////////////////////////////////// // / @brief return the base url for index usage // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._indexurl = function (id, expectedName) { if (typeof id === 'string') { var pa = ArangoDatabase.indexRegex.exec(id); if (pa === null && expectedName !== undefined) { id = expectedName + '/' + id; } } else if (typeof id === 'number' && expectedName !== undefined) { // stringify a numeric id id = expectedName + '/' + id; } var s = id.split('/'); if (s.length !== 2) { // invalid index handle throw new ArangoError({ error: true, code: internal.errors.ERROR_HTTP_BAD_PARAMETER.code, errorNum: internal.errors.ERROR_ARANGO_INDEX_HANDLE_BAD.code, errorMessage: internal.errors.ERROR_ARANGO_INDEX_HANDLE_BAD.message }); } else if (expectedName !== undefined && expectedName !== '' && s[0] !== expectedName) { // index handle does not match collection name throw new ArangoError({ error: true, code: internal.errors.ERROR_HTTP_BAD_PARAMETER.code, errorNum: internal.errors.ERROR_ARANGO_CROSS_COLLECTION_REQUEST.code, errorMessage: internal.errors.ERROR_ARANGO_CROSS_COLLECTION_REQUEST.message }); } return '/_api/index/' + encodeURIComponent(s[0]) + '/' + encodeURIComponent(s[1]); }; // ////////////////////////////////////////////////////////////////////////////// // / @brief prints the help for ArangoDatabase // ////////////////////////////////////////////////////////////////////////////// var helpArangoDatabase = arangosh.createHelpHeadline('ArangoDatabase (db) help') + 'Administration Functions: ' + '\n' + ' _help() this help ' + '\n' + ' _flushCache() flush and refill collection cache ' + '\n' + ' ' + '\n' + 'Collection Functions: ' + '\n' + ' _collections() list all collections ' + '\n' + ' _collection() get collection by identifier/name ' + '\n' + ' _create(, ) creates a new collection ' + '\n' + ' _createEdgeCollection() creates a new edge collection ' + '\n' + ' _drop() delete a collection ' + '\n' + ' ' + '\n' + 'Document Functions: ' + '\n' + ' _document() get document by handle (_id) ' + '\n' + ' _replace(, , ) overwrite document ' + '\n' + ' _update(, , , partially update document ' + '\n' + ' ) ' + '\n' + ' _remove() delete document ' + '\n' + ' _exists() checks whether a document exists ' + '\n' + ' _truncate() delete all documents ' + '\n' + ' ' + '\n' + 'Database Management Functions: ' + '\n' + ' _createDatabase() creates a new database ' + '\n' + ' _dropDatabase() drops an existing database ' + '\n' + ' _useDatabase() switches into an existing database' + '\n' + ' _drop() delete a collection ' + '\n' + ' _name() name of the current database ' + '\n' + ' ' + '\n' + 'Query / Transaction Functions: ' + '\n' + ' _executeTransaction() execute transaction ' + '\n' + ' _query() execute AQL query ' + '\n' + ' _createStatement() create and return AQL query ' + '\n' + ' ' + '\n' + 'View Functions: ' + '\n' + ' _views() list all views ' + '\n' + ' _view() get view by name ' + '\n' + ' _createView(, , creates a new view ' + '\n' + ' ) ' + '\n' + ' _dropView() delete a view '; ArangoDatabase.prototype._help = function () { internal.print(helpArangoDatabase); }; // ////////////////////////////////////////////////////////////////////////////// // / @brief return a string representation of the database object // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype.toString = function () { return '[object ArangoDatabase "' + this._name() + '"]'; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief return all collections from the database // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._collections = function () { var requestResult = this._connection.GET(this._collectionurl()); arangosh.checkRequestResult(requestResult); if (requestResult.result !== undefined) { var collections = requestResult.result; var result = []; var i; // add all collections to object for (i = 0; i < collections.length; ++i) { var collection = new this._collectionConstructor(this, collections[i]); this._registerCollection(collection._name, collection); result.push(collection); } return result.sort(function (l, r) { // we assume no two collections have the same name if (l.name().toLowerCase() < r.name().toLowerCase()) { return -1; } return 1; }); } return undefined; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief return a single collection, identified by its id or name // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._collection = function (id) { if (typeof id !== 'number' && this.hasOwnProperty(id) && this[id] && this[id] instanceof this._collectionConstructor) { return this[id]; } var url; if (typeof id === 'number') { url = this._collectionurl(id) + '?useId=true'; } else { url = this._collectionurl(id); } var requestResult = this._connection.GET(url); // return null in case of not found if (requestResult !== null && requestResult.error === true && requestResult.errorNum === internal.errors.ERROR_ARANGO_DATA_SOURCE_NOT_FOUND.code) { return null; } // check all other errors and throw them arangosh.checkRequestResult(requestResult); var name = requestResult.name; if (name !== undefined) { this._registerCollection(name, new this._collectionConstructor(this, requestResult)); return this[name]; } return null; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief creates a new collection // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._create = function (name, properties, type, options) { var body = { 'name': name, 'type': ArangoCollection.TYPE_DOCUMENT }; if (properties !== undefined) { [ 'waitForSync', 'journalSize', 'isSystem', 'isVolatile', 'doCompact', 'keyOptions', 'shardKeys', 'numberOfShards', 'distributeShardsLike', 'indexBuckets', 'id', 'isSmart', 'replicationFactor', 'smartGraphAttribute', 'avoidServers', 'cacheEnabled'].forEach(function (p) { if (properties.hasOwnProperty(p)) { body[p] = properties[p]; } }); } if (typeof type === 'object') { options = type; type = undefined; } let urlAddons = []; if (typeof options === "object" && options !== null) { if (options.hasOwnProperty('waitForSyncReplication')) { if (options.waitForSyncReplication) { urlAddons.push('waitForSyncReplication=1'); } else { urlAddons.push('waitForSyncReplication=0'); } } if (options.hasOwnProperty('enforceReplicationFactor')) { if (options.enforceReplicationFactor) { urlAddons.push('enforceReplicationFactor=1'); } else { urlAddons.push('enforceReplicationFactor=0'); } } } let urlAddon = ''; if (urlAddons.length > 0) { urlAddon += '?' + urlAddons.join('&'); } if (type !== undefined) { body.type = type; } var requestResult = this._connection.POST(this._collectionurl() + urlAddon, JSON.stringify(body)); arangosh.checkRequestResult(requestResult); var nname = requestResult.name; if (nname !== undefined) { this._registerCollection(nname, new this._collectionConstructor(this, requestResult)); return this[nname]; } return undefined; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief creates a new document collection // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._createDocumentCollection = function (name, properties) { return this._create(name, properties, ArangoCollection.TYPE_DOCUMENT); }; // ////////////////////////////////////////////////////////////////////////////// // / @brief creates a new edges collection // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._createEdgeCollection = function (name, properties) { return this._create(name, properties, ArangoCollection.TYPE_EDGE); }; // ////////////////////////////////////////////////////////////////////////////// // / @brief truncates a collection // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._truncate = function (id) { var name; if (typeof id !== 'string') { id = id._id; } for (name in this) { if (this.hasOwnProperty(name)) { var collection = this[name]; if (collection instanceof this._collectionConstructor) { if (collection._id === id || collection._name === id) { return collection.truncate(); } } } } return undefined; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief drops a collection // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._drop = function (id, options) { var name; for (name in this) { if (this.hasOwnProperty(name)) { var collection = this[name]; if (collection instanceof this._collectionConstructor) { if (collection._id === id || collection._name === id) { return collection.drop(options); } } } } var c = this._collection(id); if (c) { return c.drop(options); } return undefined; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief flush the local cache // / this is called by connection.reconnect() // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._flushCache = function () { var name; for (name in this) { if (this.hasOwnProperty(name)) { var collection = this[name]; if (collection instanceof this._collectionConstructor) { // reset the collection status collection._status = null; this[name] = undefined; } } } try { // repopulate cache this._collections(); } catch (err) {} this._properties = null; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief query the database properties // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._queryProperties = function (force) { if (force || this._properties === null) { var url = '/_api/database/current'; var requestResult = this._connection.GET(url); arangosh.checkRequestResult(requestResult); this._properties = requestResult.result; } return this._properties; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief return the database id // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._id = function () { return this._queryProperties().id; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief return whether or not the current database is the system database // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._isSystem = function () { return this._queryProperties().isSystem; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief get the name of the current database // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._name = function () { return this._queryProperties().name; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief get the path of the current database // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._path = function () { return this._queryProperties().path; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief returns one index // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._index = function (id) { if (id.hasOwnProperty('id')) { id = id.id; } var requestResult = this._connection.GET(this._indexurl(id)); arangosh.checkRequestResult(requestResult); return requestResult; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief deletes one index // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._dropIndex = function (id) { if (id.hasOwnProperty('id')) { id = id.id; } var requestResult = this._connection.DELETE(this._indexurl(id)); if (requestResult !== null && requestResult.error === true && requestResult.errorNum === internal.errors.ERROR_ARANGO_INDEX_NOT_FOUND.code) { return false; } arangosh.checkRequestResult(requestResult); return true; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief returns the engine name // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._engine = function () { var requestResult = this._connection.GET('/_api/engine'); arangosh.checkRequestResult(requestResult); return requestResult; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief returns the engine statistics // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._engineStats = function () { var requestResult = this._connection.GET('/_api/engine/stats'); arangosh.checkRequestResult(requestResult); return requestResult; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief returns the database version // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._version = function (details) { var requestResult = this._connection.GET('/_api/version' + (details ? '?details=true' : '')); arangosh.checkRequestResult(requestResult); return details ? requestResult : requestResult.version; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief return a single document from the collection, identified by its id // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._document = function (id) { var rev = null; var requestResult; if (typeof id === 'object') { if (id.hasOwnProperty('_rev')) { rev = id._rev; } if (id.hasOwnProperty('_id')) { id = id._id; } } if (rev === null) { requestResult = this._connection.GET(this._documenturl(id)); } else { requestResult = this._connection.GET(this._documenturl(id), {'if-match': JSON.stringify(rev) }); } if (requestResult !== null && requestResult.error === true) { if (requestResult.errorNum === internal.errors.ERROR_HTTP_PRECONDITION_FAILED.code) { requestResult.errorNum = internal.errors.ERROR_ARANGO_CONFLICT.code; } } arangosh.checkRequestResult(requestResult); return requestResult; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief checks whether a document exists, identified by its id // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._exists = function (id) { var rev = null; var requestResult; if (typeof id === 'object') { if (id.hasOwnProperty('_rev')) { rev = id._rev; } if (id.hasOwnProperty('_id')) { id = id._id; } } if (rev === null) { requestResult = this._connection.HEAD(this._documenturl(id)); } else { requestResult = this._connection.HEAD(this._documenturl(id), {'if-match': JSON.stringify(rev) }); } if (requestResult !== null && requestResult.error === true) { if (requestResult.errorNum === internal.errors.ERROR_HTTP_NOT_FOUND.code) { return false; } if (requestResult.errorNum === internal.errors.ERROR_HTTP_PRECONDITION_FAILED.code) { requestResult.errorNum = internal.errors.ERROR_ARANGO_CONFLICT.code; } } arangosh.checkRequestResult(requestResult); return true; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief delete a document in the collection, identified by its id // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._remove = function (id, overwrite, waitForSync) { var rev = null; var requestResult; if (typeof id === 'object') { if (Array.isArray(id)) { throw new ArangoError({ error: true, code: internal.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code, errorNum: internal.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code, errorMessage: internal.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.message }); } if (id.hasOwnProperty('_rev')) { rev = id._rev; } if (id.hasOwnProperty('_id')) { id = id._id; } } var params = ''; var ignoreRevs = false; var options; if (typeof overwrite === 'object') { if (typeof waitForSync !== 'undefined') { throw 'too many arguments'; } // we assume the caller uses new signature (id, data, options) options = overwrite; if (options.hasOwnProperty('overwrite') && options.overwrite) { ignoreRevs = true; } if (options.hasOwnProperty('waitForSync')) { waitForSync = options.waitForSync; } } else { if (overwrite) { ignoreRevs = true; } options = {}; } var url = this._documenturl(id) + params; url = this._appendSyncParameter(url, waitForSync); url = this._appendBoolParameter(url, 'ignoreRevs', ignoreRevs); url = this._appendBoolParameter(url, 'returnOld', options.returnOld); if (rev === null || ignoreRevs) { requestResult = this._connection.DELETE(url); } else { requestResult = this._connection.DELETE(url, {'if-match': JSON.stringify(rev) }); } if (requestResult !== null && requestResult.error === true) { if (requestResult.errorNum === internal.errors.ERROR_HTTP_PRECONDITION_FAILED.code) { requestResult.errorNum = internal.errors.ERROR_ARANGO_CONFLICT.code; } } arangosh.checkRequestResult(requestResult); return options.silent ? true : requestResult; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief replace a document in the collection, identified by its id // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._replace = function (id, data, overwrite, waitForSync) { var rev = null; var requestResult; if (typeof id === 'object') { if (Array.isArray(id)) { throw new ArangoError({ error: true, code: internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.code, errorNum: internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.code, errorMessage: internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.message }); } if (id.hasOwnProperty('_rev')) { rev = id._rev; } if (id.hasOwnProperty('_id')) { id = id._id; } } var params = ''; var ignoreRevs = false; var options; if (typeof overwrite === 'object') { if (typeof waitForSync !== 'undefined') { throw 'too many arguments'; } // we assume the caller uses new signature (id, data, options) options = overwrite; if (options.hasOwnProperty('overwrite') && options.overwrite) { ignoreRevs = true; } if (options.hasOwnProperty('waitForSync')) { waitForSync = options.waitForSync; } } else { if (overwrite) { ignoreRevs = true; } options = {}; } var url = this._documenturl(id) + params; url = this._appendSyncParameter(url, waitForSync); url = this._appendBoolParameter(url, 'ignoreRevs', true); url = this._appendBoolParameter(url, 'returnOld', options.returnOld); url = this._appendBoolParameter(url, 'returnNew', options.returnNew); if (rev === null || ignoreRevs) { requestResult = this._connection.PUT(url, JSON.stringify(data)); } else { requestResult = this._connection.PUT(url, JSON.stringify(data), {'if-match': JSON.stringify(rev) }); } if (requestResult !== null && requestResult.error === true) { if (requestResult.errorNum === internal.errors.ERROR_HTTP_PRECONDITION_FAILED.code) { requestResult.errorNum = internal.errors.ERROR_ARANGO_CONFLICT.code; } } arangosh.checkRequestResult(requestResult); return options.silent ? true : requestResult; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief update a document in the collection, identified by its id // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._update = function (id, data, overwrite, keepNull, waitForSync) { var rev = null; var requestResult; if (typeof id === 'object') { if (Array.isArray(id)) { throw new ArangoError({ error: true, code: internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.code, errorNum: internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.code, errorMessage: internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.message }); } if (id.hasOwnProperty('_rev')) { rev = id._rev; } if (id.hasOwnProperty('_id')) { id = id._id; } } var params = ''; var ignoreRevs = false; var options; if (typeof overwrite === 'object') { if (typeof keepNull !== 'undefined') { throw 'too many arguments'; } // we assume the caller uses new signature (id, data, options) options = overwrite; if (!options.hasOwnProperty('keepNull')) { options.keepNull = true; } params = '?keepNull=' + options.keepNull; if (!options.hasOwnProperty('mergeObjects')) { options.mergeObjects = true; } params += '&mergeObjects=' + options.mergeObjects; if (options.hasOwnProperty('overwrite') && options.overwrite) { ignoreRevs = true; } } else { // set default value for keepNull var keepNullValue = ((typeof keepNull === 'undefined') ? true : keepNull); params = '?keepNull=' + (keepNullValue ? 'true' : 'false'); if (overwrite) { ignoreRevs = true; } options = {}; } var url = this._documenturl(id) + params; url = this._appendSyncParameter(url, waitForSync); url = this._appendBoolParameter(url, 'ignoreRevs', true); url = this._appendBoolParameter(url, 'returnOld', options.returnOld); url = this._appendBoolParameter(url, 'returnNew', options.returnNew); if (rev === null || ignoreRevs) { requestResult = this._connection.PATCH(url, JSON.stringify(data)); } else { requestResult = this._connection.PATCH(url, JSON.stringify(data), {'if-match': JSON.stringify(rev) }); } if (requestResult !== null && requestResult.error === true) { if (requestResult.errorNum === internal.errors.ERROR_HTTP_PRECONDITION_FAILED.code) { requestResult.errorNum = internal.errors.ERROR_ARANGO_CONFLICT.code; } } arangosh.checkRequestResult(requestResult); return options.silent ? true : requestResult; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief factory method to create a new statement // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._createStatement = function (data) { return new ArangoStatement(this, data); }; // ////////////////////////////////////////////////////////////////////////////// // / @brief factory method to create and execute a new statement // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._query = function (query, bindVars, cursorOptions, options) { if (typeof query === 'object' && query !== null && arguments.length === 1) { return new ArangoStatement(this, query).execute(); } if (options === undefined && cursorOptions !== undefined) { options = cursorOptions; } var data = { query: query, bindVars: bindVars || undefined, count: (cursorOptions && cursorOptions.count) || false, batchSize: (cursorOptions && cursorOptions.batchSize) || undefined, options: options || undefined, cache: (options && options.cache) || undefined }; return new ArangoStatement(this, data).execute(); }; // ////////////////////////////////////////////////////////////////////////////// // / @brief queryProfile execute a query with profiling information // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._profileQuery = function (query, bindVars, options) { options = options || {}; options.profile = 2; query = { query: query, bindVars: bindVars, options: options }; require('@arangodb/aql/explainer').profileQuery(query); }; // ////////////////////////////////////////////////////////////////////////////// // / @brief explains a query // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._explain = function (query, bindVars, options) { if (typeof query === 'object' && typeof query.toAQL === 'function') { query = { query: query.toAQL() }; } if (arguments.length > 1) { query = { query: query, bindVars: bindVars, options: options }; } require('@arangodb/aql/explainer').explain(query); }; // ////////////////////////////////////////////////////////////////////////////// // / @brief parses a query // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._parse = function (query) { if (typeof query === 'object' && typeof query.toAQL === 'function') { query = { query: query.toAQL() }; } else { query = { query: query }; } const requestResult = this._connection.POST('/_api/query', JSON.stringify(query)); if (requestResult && requestResult.error === true) { throw new ArangoError(requestResult); } arangosh.checkRequestResult(requestResult); return requestResult; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief create a new database // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._createDatabase = function (name, options, users) { var data = { name: name, options: options || { }, users: users || [] }; var requestResult = this._connection.POST('/_api/database', JSON.stringify(data)); if (requestResult !== null && requestResult.error === true) { throw new ArangoError(requestResult); } arangosh.checkRequestResult(requestResult); return requestResult.result; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief drop an existing database // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._dropDatabase = function (name) { var requestResult = this._connection.DELETE('/_api/database/' + encodeURIComponent(name)); if (requestResult !== null && requestResult.error === true) { throw new ArangoError(requestResult); } arangosh.checkRequestResult(requestResult); return requestResult.result; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief list all existing databases // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._databases = function () { var requestResult = this._connection.GET('/_api/database'); if (requestResult !== null && requestResult.error === true) { throw new ArangoError(requestResult); } arangosh.checkRequestResult(requestResult); return requestResult.result; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief uses a database // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._useDatabase = function (name) { var old = this._connection.getDatabaseName(); // no change if (name === old) { return true; } this._connection.setDatabaseName(name); try { // re-query properties this._queryProperties(true); this._flushCache(); } catch (err) { this._connection.setDatabaseName(old); if (err.hasOwnProperty('errorNum')) { throw err; } throw new ArangoError({ error: true, code: internal.errors.ERROR_BAD_PARAMETER.code, errorNum: internal.errors.ERROR_BAD_PARAMETER.code, errorMessage: "cannot use database '" + name + "'" }); } return true; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief lists all endpoints // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._endpoints = function () { var requestResult = this._connection.GET('/_api/endpoint'); if (requestResult !== null && requestResult.error === true) { throw new ArangoError(requestResult); } arangosh.checkRequestResult(requestResult); return requestResult; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief execute a transaction // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._executeTransaction = function (data) { if (!data || typeof (data) !== 'object') { throw new ArangoError({ error: true, code: internal.errors.ERROR_HTTP_BAD_PARAMETER.code, errorNum: internal.errors.ERROR_BAD_PARAMETER.code, errorMessage: 'usage: _executeTransaction()' }); } data = Object.assign({}, data); if (!data.collections || typeof data.collections !== 'object') { throw new ArangoError({ error: true, code: internal.errors.ERROR_HTTP_BAD_PARAMETER.code, errorNum: internal.errors.ERROR_BAD_PARAMETER.code, errorMessage: 'missing/invalid collections definition for transaction' }); } data.collections = Object.assign({}, data.collections); if (data.collections.read) { if (!Array.isArray(data.collections.read)) { data.collections.read = [data.collections.read]; } data.collections.read = data.collections.read.map( col => col.isArangoCollection ? col.name() : col ); } if (data.collections.write) { if (!Array.isArray(data.collections.write)) { data.collections.write = [data.collections.write]; } data.collections.write = data.collections.write.map( col => col.isArangoCollection ? col.name() : col ); } if (!data.action || (typeof (data.action) !== 'string' && typeof (data.action) !== 'function')) { throw new ArangoError({ error: true, code: internal.errors.ERROR_HTTP_BAD_PARAMETER.code, errorNum: internal.errors.ERROR_BAD_PARAMETER.code, errorMessage: 'missing/invalid action definition for transaction' }); } if (typeof (data.action) === 'function') { data.action = String(data.action); } var requestResult = this._connection.POST('/_api/transaction', JSON.stringify(data)); if (requestResult !== null && requestResult.error === true) { throw new ArangoError(requestResult); } arangosh.checkRequestResult(requestResult); return requestResult.result; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief creates a new view // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._createView = function (name, type, properties) { var body = { 'name': name, 'type': type, 'properties': properties }; if (properties === undefined) { body['properties'] = {}; } var requestResult = this._connection.POST(this._viewurl(), JSON.stringify(body)); arangosh.checkRequestResult(requestResult); var nname = requestResult.name; if (nname !== undefined) { this._registerView(nname, new this._viewConstructor(this, requestResult)); return this._viewList[nname]; } return undefined; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief deletes a view // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._dropView = function (id) { var name; for (name in this._viewList) { if (this._viewList.hasOwnProperty(name)) { var view = this._viewList[name]; if (view instanceof this._viewConstructor) { if (view._id === id || view._name === id) { return view.drop(); } } } } var v = this._view(id); if (v) { return v.drop(); } return undefined; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief return all views from the database // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._views = function () { var requestResult = this._connection.GET(this._viewurl()); arangosh.checkRequestResult(requestResult); var result = []; if (requestResult !== undefined) { var views = requestResult; var i; // add all views to object for (i = 0; i < views.length; ++i) { var view = new this._viewConstructor(this, views[i]); this._registerView(view._name, view); result.push(view); } result = result.sort(function (l, r) { // we assume no two views have the same name if (l.name().toLowerCase() < r.name().toLowerCase()) { return -1; } return 1; }); } return result; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief return a single view, identified by its name // ////////////////////////////////////////////////////////////////////////////// ArangoDatabase.prototype._view = function (id) { if (this._viewList[id] && this._viewList[id] instanceof this._viewConstructor) { return this._viewList[id]; } var url = this._viewurl(id); var requestResult = this._connection.GET(url); // return null in case of not found if (requestResult !== null && requestResult.error === true && requestResult.errorNum === internal.errors.ERROR_ARANGO_DATA_SOURCE_NOT_FOUND.code) { return null; } // check all other errors and throw them arangosh.checkRequestResult(requestResult); var name = requestResult.name; if (name !== undefined) { this._registerView(name, new this._viewConstructor(this, requestResult)); return this._viewList[name]; } return null; };