mirror of https://gitee.com/bigwinds/arangodb
463 lines
15 KiB
JavaScript
463 lines
15 KiB
JavaScript
/*jshint strict: false */
|
|
/*global ArangoClusterInfo, require, exports, module */
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief ArangoCollection
|
|
// /
|
|
// / @file
|
|
// /
|
|
// / DISCLAIMER
|
|
// /
|
|
// / Copyright 2011-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 Dr. Frank Celler
|
|
// / @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
module.isSystem = true;
|
|
|
|
var internal = require('internal');
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief builds an example query
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
function buildExampleQuery (collection, example, limit) {
|
|
var parts = [];
|
|
var bindVars = { '@collection': collection.name() };
|
|
var keys = Object.keys(example);
|
|
|
|
for (var i = 0; i < keys.length; ++i) {
|
|
var key = keys[i];
|
|
parts.push('doc.@att' + i + ' == @value' + i);
|
|
bindVars['att' + i] = key;
|
|
bindVars['value' + i] = example[key];
|
|
}
|
|
|
|
var query = 'FOR doc IN @@collection';
|
|
if (parts.length > 0) {
|
|
query += ' FILTER ' + parts.join(' && ', parts);
|
|
}
|
|
if (limit > 0) {
|
|
query += ' LIMIT ' + parseInt(limit, 10);
|
|
}
|
|
|
|
return { query: query, bindVars: bindVars };
|
|
}
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief constructor
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
var ArangoCollection = internal.ArangoCollection;
|
|
exports.ArangoCollection = ArangoCollection;
|
|
|
|
// must be called after exporting ArangoCollection
|
|
require('@arangodb/arango-collection-common');
|
|
|
|
var simple = require('@arangodb/simple-query');
|
|
var ArangoError = require('@arangodb').ArangoError;
|
|
var ArangoDatabase = require('@arangodb/arango-database').ArangoDatabase;
|
|
|
|
|
|
ArangoCollection.prototype.shards = function (detailed) {
|
|
let base = ArangoClusterInfo.getCollectionInfo(require('internal').db._name(), this.name());
|
|
if (detailed) {
|
|
return base.shards;
|
|
}
|
|
return Object.keys(base.shardShorts);
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief was docuBlock collectionToArray
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
ArangoCollection.prototype.toArray = function () {
|
|
var cluster = require('@arangodb/cluster');
|
|
|
|
if (cluster.isCoordinator()) {
|
|
return this.all().toArray();
|
|
}
|
|
|
|
return this.ALL().documents;
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief was docuBlock collectionTruncate
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
ArangoCollection.prototype.truncate = function () {
|
|
var cluster = require('@arangodb/cluster');
|
|
if (cluster.isCoordinator()) {
|
|
if (this.status() === ArangoCollection.STATUS_UNLOADED) {
|
|
this.load();
|
|
}
|
|
}
|
|
var ret = this.TRUNCATE();
|
|
// manually trigger compaction, otherwise the SST files stay large
|
|
if (internal.db._engine().name === "rocksdb" && this.compact) {
|
|
this.compact();
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief finds an index of a collection
|
|
// /
|
|
// / @FUN{@FA{collection}.index(@FA{index-handle})}
|
|
// /
|
|
// / Returns the index with @FA{index-handle} or null if no such index exists.
|
|
// /
|
|
// / *Examples*
|
|
// /
|
|
// / @code
|
|
// / arango> db.example.getIndexes().map(function(x) { return x.id; })
|
|
// / ["example/0"]
|
|
// / arango> db.example.index("93013/0")
|
|
// / { "id" : "example/0", "type" : "primary", "fields" : ["_id"] }
|
|
// / @endcode
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
ArangoCollection.prototype.index = function (id) {
|
|
var indexes = this.getIndexes();
|
|
var i;
|
|
|
|
if (typeof id === 'object' && id.hasOwnProperty('id')) {
|
|
id = id.id;
|
|
} else if (typeof id === 'object' && id.hasOwnProperty('name')) {
|
|
id = id.name;
|
|
}
|
|
|
|
if (typeof id === 'string') {
|
|
var pa = ArangoDatabase.indexRegex.exec(id);
|
|
|
|
if (pa === null && !isNaN(Number(id)) && Number(id) === Math.floor(Number(id))) {
|
|
id = this.name() + '/' + id;
|
|
}
|
|
} else if (typeof id === 'number') {
|
|
// stringify the id
|
|
id = this.name() + '/' + id;
|
|
}
|
|
|
|
for (i = 0; i < indexes.length; ++i) {
|
|
var index = indexes[i];
|
|
|
|
if (index.id === id || index.name === id || this.name() + '/' + index.name === id) {
|
|
return index;
|
|
}
|
|
}
|
|
|
|
var err = new ArangoError();
|
|
err.errorNum = internal.errors.ERROR_ARANGO_INDEX_NOT_FOUND.code;
|
|
err.errorMessage = internal.errors.ERROR_ARANGO_INDEX_NOT_FOUND.message;
|
|
|
|
throw err;
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief was docuBlock collectionEdgesAll
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
ArangoCollection.prototype.edges = function (vertex) {
|
|
return this.EDGES(vertex);
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief was docuBlock collectionEdgesInbound
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
ArangoCollection.prototype.inEdges = function (vertex) {
|
|
return this.INEDGES(vertex);
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief was docuBlock collectionEdgesOutbound
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
ArangoCollection.prototype.outEdges = function (vertex) {
|
|
return this.OUTEDGES(vertex);
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief was docuBlock documentsCollectionAny
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
ArangoCollection.prototype.any = function () {
|
|
var cluster = require('@arangodb/cluster');
|
|
|
|
if (cluster.isCoordinator()) {
|
|
const db = require('internal').db;
|
|
let document = null;
|
|
let query = "FOR doc IN @@coll SORT RAND() LIMIT 1 RETURN doc";
|
|
let cursor = db._query(query, {"@coll": this.name()});
|
|
if (cursor.hasNext()) {
|
|
document = cursor.next();
|
|
}
|
|
|
|
return document;
|
|
}
|
|
|
|
return this.ANY();
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief was docuBlock collectionFirstExample
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
ArangoCollection.prototype.firstExample = function (example) {
|
|
var e;
|
|
var i;
|
|
|
|
// example is given as only argument
|
|
if (arguments.length === 1) {
|
|
e = example;
|
|
}
|
|
|
|
// example is given as list
|
|
else {
|
|
e = {};
|
|
|
|
for (i = 0; i < arguments.length; i += 2) {
|
|
e[arguments[i]] = arguments[i + 1];
|
|
}
|
|
}
|
|
|
|
var documents = (new simple.SimpleQueryByExample(this, e)).limit(1).toArray();
|
|
if (documents.length > 0) {
|
|
return documents[0];
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief removes documents matching an example
|
|
// / @param example a json object which is used as sample for finding docs that
|
|
// / this example matches
|
|
// / @param waitForSync (optional) a boolean value or a json object
|
|
// / @param limit (optional) an integer value, the max number of to deleting docs
|
|
// / @example remove("{a : 1 }")
|
|
// / @example remove("{a : 1 }", true)
|
|
// / @example remove("{a : 1 }", false)
|
|
// / @example remove("{a : 1 }", {waitForSync: false, limit: 12})
|
|
// / @throws "too many parameters" when waiitForSyn is a Json object and limit
|
|
// / is given
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
ArangoCollection.prototype.removeByExample = function (example,
|
|
waitForSync,
|
|
limit) {
|
|
if (limit === 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (typeof waitForSync === 'object') {
|
|
if (typeof limit !== 'undefined') {
|
|
throw 'too many parameters';
|
|
}
|
|
var tmp_options = waitForSync === null ? {} : waitForSync;
|
|
// avoiding jslint error
|
|
// see: http://jslinterrors.com/unexpected-sync-method-a/
|
|
waitForSync = tmp_options.waitForSync;
|
|
limit = tmp_options.limit;
|
|
}
|
|
|
|
var i;
|
|
var cluster = require('@arangodb/cluster');
|
|
|
|
var query = buildExampleQuery(this, example, limit);
|
|
var opts = { waitForSync };
|
|
query.query += ' REMOVE doc IN @@collection OPTIONS ' + JSON.stringify(opts);
|
|
|
|
return require('internal').db._query(query).getExtra().stats.writesExecuted;
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief replaces documents matching an example
|
|
// / @param example the json object for finding documents which matches this
|
|
// / example
|
|
// / @param newValue the json object which replaces the matched documents
|
|
// / @param waitForSync (optional) a boolean value or a json object which
|
|
// / contains the options
|
|
// / @param limit (optional) an integer value, the max number of to deleting docs
|
|
// / @throws "too many parameters" when waitForSync is a json object and limit
|
|
// / was given
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
ArangoCollection.prototype.replaceByExample = function (example,
|
|
newValue,
|
|
waitForSync,
|
|
limit) {
|
|
if (limit === 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (typeof newValue !== 'object' || Array.isArray(newValue)) {
|
|
var err1 = new ArangoError();
|
|
err1.errorNum = internal.errors.ERROR_BAD_PARAMETER.code;
|
|
err1.errorMessage = "invalid value for parameter 'newValue'";
|
|
|
|
throw err1;
|
|
}
|
|
|
|
if (typeof waitForSync === 'object') {
|
|
if (typeof limit !== 'undefined') {
|
|
throw 'too many parameters';
|
|
}
|
|
var tmp_options = waitForSync === null ? {} : waitForSync;
|
|
// avoiding jslint error
|
|
// see: http://jslinterrors.com/unexpected-sync-method-a/
|
|
waitForSync = tmp_options.waitForSync;
|
|
limit = tmp_options.limit;
|
|
}
|
|
|
|
var query = buildExampleQuery(this, example, limit);
|
|
var opts = { waitForSync };
|
|
query.query += ' REPLACE doc WITH @newValue IN @@collection OPTIONS ' + JSON.stringify(opts);
|
|
query.bindVars.newValue = newValue;
|
|
|
|
return require('internal').db._query(query).getExtra().stats.writesExecuted;
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief partially updates documents matching an example
|
|
// / @param example the json object for finding documents which matches this
|
|
// / example
|
|
// / @param newValue the json object which replaces the matched documents
|
|
// / @param keepNull (optional) true or a JSON object which
|
|
// / contains the options
|
|
// / @param waitForSync (optional) a boolean value
|
|
// / @param limit (optional) an integer value, the max number of to deleting docs
|
|
// / @throws "too many parameters" when keepNull is a JSON object and limit
|
|
// / was given
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
ArangoCollection.prototype.updateByExample = function (example,
|
|
newValue,
|
|
keepNull,
|
|
waitForSync,
|
|
limit) {
|
|
if (limit === 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (typeof newValue !== 'object' || Array.isArray(newValue)) {
|
|
var err1 = new ArangoError();
|
|
err1.errorNum = internal.errors.ERROR_BAD_PARAMETER.code;
|
|
err1.errorMessage = "invalid value for parameter 'newValue'";
|
|
|
|
throw err1;
|
|
}
|
|
|
|
var mergeObjects = true;
|
|
|
|
if (typeof keepNull === 'object') {
|
|
if (typeof waitForSync !== 'undefined') {
|
|
throw 'too many parameters';
|
|
}
|
|
var tmp_options = keepNull === null ? {} : keepNull;
|
|
|
|
// avoiding jslint error
|
|
// see: http://jslinterrors.com/unexpected-sync-method-a/
|
|
keepNull = tmp_options.keepNull;
|
|
waitForSync = tmp_options.waitForSync;
|
|
limit = tmp_options.limit;
|
|
if (tmp_options.hasOwnProperty('mergeObjects')) {
|
|
mergeObjects = tmp_options.mergeObjects || false;
|
|
}
|
|
}
|
|
|
|
var query = buildExampleQuery(this, example, limit);
|
|
var opts = { waitForSync, keepNull, mergeObjects };
|
|
query.query += ' UPDATE doc WITH @newValue IN @@collection OPTIONS ' + JSON.stringify(opts);
|
|
query.bindVars.newValue = newValue;
|
|
|
|
return require('internal').db._query(query).getExtra().stats.writesExecuted;
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief looks up a unique constraint
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
ArangoCollection.prototype.lookupUniqueConstraint = function () {
|
|
'use strict';
|
|
|
|
return this.lookupIndex({
|
|
type: 'hash',
|
|
fields: Array.prototype.slice.call(arguments),
|
|
unique: true
|
|
});
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief looks up a hash index
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
ArangoCollection.prototype.lookupHashIndex = function () {
|
|
'use strict';
|
|
|
|
return this.lookupIndex({
|
|
type: 'hash',
|
|
fields: Array.prototype.slice.call(arguments)
|
|
});
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief looks up a unique skiplist index
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
ArangoCollection.prototype.lookupUniqueSkiplist = function () {
|
|
'use strict';
|
|
|
|
return this.lookupIndex({
|
|
type: 'skiplist',
|
|
fields: Array.prototype.slice.call(arguments),
|
|
unique: true
|
|
});
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief looks up a skiplist index
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
ArangoCollection.prototype.lookupSkiplist = function () {
|
|
'use strict';
|
|
|
|
return this.lookupIndex({
|
|
type: 'skiplist',
|
|
fields: Array.prototype.slice.call(arguments)
|
|
});
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief was docuBlock lookUpFulltextIndex
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
ArangoCollection.prototype.lookupFulltextIndex = function (field, minLength) {
|
|
'use strict';
|
|
|
|
if (! Array.isArray(field)) {
|
|
field = [ field ];
|
|
}
|
|
|
|
return this.lookupIndex({
|
|
type: 'fulltext',
|
|
fields: field,
|
|
minLength: minLength || undefined
|
|
});
|
|
};
|