mirror of https://gitee.com/bigwinds/arangodb
673 lines
21 KiB
JavaScript
673 lines
21 KiB
JavaScript
/*jshint strict: false */
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief querying and managing collections
|
|
///
|
|
/// @file
|
|
///
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright 2014 ArangoDB 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 ArangoDB GmbH, Cologne, Germany
|
|
///
|
|
/// @author Achim Brandt
|
|
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
|
|
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
var arangodb = require("@arangodb");
|
|
var actions = require("@arangodb/actions");
|
|
var cluster = require("@arangodb/cluster");
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief return a prefixed URL
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function databasePrefix (req, url) {
|
|
// location response (e.g. /_db/dbname/_api/collection/xyz)
|
|
return "/_db/" + encodeURIComponent(arangodb.db._name()) + url;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief collection representation
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function collectionRepresentation (collection, showProperties, showCount, showFigures) {
|
|
var result = {};
|
|
|
|
result.id = collection._id;
|
|
result.name = collection.name();
|
|
result.isSystem = (result.name.charAt(0) === '_');
|
|
|
|
if (showProperties) {
|
|
var properties = collection.properties();
|
|
|
|
result.doCompact = properties.doCompact;
|
|
result.isVolatile = properties.isVolatile;
|
|
result.journalSize = properties.journalSize;
|
|
result.keyOptions = properties.keyOptions;
|
|
result.waitForSync = properties.waitForSync;
|
|
result.indexBuckets = properties.indexBuckets;
|
|
|
|
if (cluster.isCoordinator()) {
|
|
result.shardKeys = properties.shardKeys;
|
|
result.numberOfShards = properties.numberOfShards;
|
|
result.replicationFactor = properties.replicationFactor;
|
|
}
|
|
}
|
|
|
|
if (showCount) {
|
|
result.count = collection.count();
|
|
}
|
|
|
|
if (showFigures) {
|
|
var figures = collection.figures();
|
|
|
|
if (figures) {
|
|
result.figures = figures;
|
|
}
|
|
}
|
|
|
|
result.status = collection.status();
|
|
result.type = collection.type();
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief helper to parse arguments for creating collections
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function parseBodyForCreateCollection (req, res) {
|
|
var body = actions.getJsonBody(req, res);
|
|
|
|
if (body === undefined) {
|
|
return { bodyIsEmpty: true };
|
|
}
|
|
|
|
var r = {};
|
|
|
|
if (! body.hasOwnProperty("name")) {
|
|
r.name = "";
|
|
}
|
|
else {
|
|
r.name = body.name;
|
|
}
|
|
r.parameters = { waitForSync : require("internal").options()["database.wait-for-sync"] };
|
|
r.type = arangodb.ArangoCollection.TYPE_DOCUMENT;
|
|
|
|
if (body.hasOwnProperty("doCompact")) {
|
|
r.parameters.doCompact = body.doCompact;
|
|
}
|
|
|
|
if (body.hasOwnProperty("isSystem")) {
|
|
r.parameters.isSystem = (body.isSystem && r.name[0] === '_');
|
|
}
|
|
|
|
if (body.hasOwnProperty("id")) {
|
|
r.parameters.id = body.id;
|
|
}
|
|
|
|
if (body.hasOwnProperty("isVolatile")) {
|
|
r.parameters.isVolatile = body.isVolatile;
|
|
}
|
|
|
|
if (body.hasOwnProperty("journalSize")) {
|
|
r.parameters.journalSize = body.journalSize;
|
|
}
|
|
|
|
if (body.hasOwnProperty("indexBuckets")) {
|
|
r.parameters.indexBuckets = body.indexBuckets;
|
|
}
|
|
|
|
if (body.hasOwnProperty("keyOptions")) {
|
|
r.parameters.keyOptions = body.keyOptions;
|
|
}
|
|
|
|
if (body.hasOwnProperty("type")) {
|
|
r.type = body.type;
|
|
}
|
|
|
|
if (body.hasOwnProperty("waitForSync")) {
|
|
r.parameters.waitForSync = body.waitForSync;
|
|
}
|
|
|
|
if (cluster.isCoordinator()) {
|
|
if (body.hasOwnProperty("shardKeys")) {
|
|
r.parameters.shardKeys = body.shardKeys || { };
|
|
}
|
|
|
|
if (body.hasOwnProperty("numberOfShards")) {
|
|
r.parameters.numberOfShards = body.numberOfShards || 0;
|
|
}
|
|
|
|
if (body.hasOwnProperty("distributeShardsLike")) {
|
|
r.parameters.distributeShardsLike = body.distributeShardsLike || "";
|
|
}
|
|
|
|
if (body.hasOwnProperty("replicationFactor")) {
|
|
r.parameters.replicationFactor = body.replicationFactor || "";
|
|
}
|
|
|
|
if (body.hasOwnProperty("servers")) {
|
|
r.parameters.servers = body.servers || "";
|
|
}
|
|
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief was docuBlock JSF_post_api_collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function post_api_collection (req, res) {
|
|
var r = parseBodyForCreateCollection(req, res);
|
|
|
|
if (r.bodyIsEmpty) {
|
|
return; // error in JSON, is already reported
|
|
}
|
|
|
|
if (r.name === "") {
|
|
actions.resultBad(req, res, arangodb.ERROR_ARANGO_ILLEGAL_NAME,
|
|
"name must be non-empty");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
var collection;
|
|
if (typeof(r.type) === "string") {
|
|
if (r.type.toLowerCase() === "edge" || r.type === "3") {
|
|
r.type = arangodb.ArangoCollection.TYPE_EDGE;
|
|
}
|
|
}
|
|
if (r.type === arangodb.ArangoCollection.TYPE_EDGE) {
|
|
collection = arangodb.db._createEdgeCollection(r.name, r.parameters);
|
|
}
|
|
else {
|
|
collection = arangodb.db._createDocumentCollection(r.name, r.parameters);
|
|
}
|
|
|
|
var result = {};
|
|
|
|
result.id = collection._id;
|
|
result.name = collection.name();
|
|
result.waitForSync = r.parameters.waitForSync || false;
|
|
result.isVolatile = r.parameters.isVolatile || false;
|
|
result.isSystem = r.parameters.isSystem || false;
|
|
result.status = collection.status();
|
|
result.type = collection.type();
|
|
result.keyOptions = collection.keyOptions;
|
|
|
|
if (cluster.isCoordinator()) {
|
|
result.shardKeys = collection.shardKeys;
|
|
result.numberOfShards = collection.numberOfShards;
|
|
result.distributeShardsLike = collection.distributeShardsLike || "";
|
|
}
|
|
|
|
var headers = {
|
|
location: databasePrefix(req, "/_api/collection/" + result.name)
|
|
};
|
|
|
|
actions.resultOk(req, res, actions.HTTP_OK, result, headers);
|
|
}
|
|
catch (err) {
|
|
actions.resultException(req, res, err, undefined, false);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief was docuBlock JSF_get_api_collections
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function get_api_collections (req, res) {
|
|
var excludeSystem;
|
|
var collections = arangodb.db._collections();
|
|
|
|
excludeSystem = false;
|
|
if (req.parameters.hasOwnProperty('excludeSystem')) {
|
|
var value = req.parameters.excludeSystem.toLowerCase();
|
|
if (value === 'true' || value === 'yes' || value === 'on' || value === 'y' || value === '1') {
|
|
excludeSystem = true;
|
|
}
|
|
}
|
|
|
|
var list = [];
|
|
for (var i = 0; i < collections.length; ++i) {
|
|
var collection = collections[i];
|
|
var rep = collectionRepresentation(collection);
|
|
|
|
// include system collections or exclude them?
|
|
if (! excludeSystem || rep.name.substr(0, 1) !== '_') {
|
|
list.push(rep);
|
|
}
|
|
}
|
|
|
|
actions.resultOk(req, res, actions.HTTP_OK, { result : list });
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief was docuBlock JSA_get_api_collection_name
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief was docuBlock JSA_get_api_collection_properties
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief was docuBlock JSA_get_api_collection_count
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief was docuBlock JSA_get_api_collection_figures
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief was docuBlock JSA_get_api_collection_revision
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief was docuBlock JSA_get_api_collection_checksum
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function get_api_collection (req, res) {
|
|
var name;
|
|
var result;
|
|
var sub;
|
|
|
|
// .............................................................................
|
|
// /_api/collection
|
|
// .............................................................................
|
|
|
|
if (req.suffix.length === 0 && req.parameters.id === undefined) {
|
|
get_api_collections(req, res);
|
|
return;
|
|
}
|
|
|
|
// .............................................................................
|
|
// /_api/collection/<name>
|
|
// .............................................................................
|
|
|
|
name = decodeURIComponent(req.suffix[0]);
|
|
|
|
var collection = arangodb.db._collection(name);
|
|
|
|
if (collection === null) {
|
|
actions.collectionNotFound(req, res, name);
|
|
return;
|
|
}
|
|
|
|
var headers;
|
|
|
|
// .............................................................................
|
|
// /_api/collection/<name>
|
|
// .............................................................................
|
|
|
|
if (req.suffix.length === 1) {
|
|
result = collectionRepresentation(collection, false, false, false);
|
|
headers = {
|
|
location : databasePrefix(req, "/_api/collection/" + collection.name())
|
|
};
|
|
actions.resultOk(req, res, actions.HTTP_OK, result, headers);
|
|
return;
|
|
}
|
|
|
|
if (req.suffix.length === 2) {
|
|
sub = decodeURIComponent(req.suffix[1]);
|
|
|
|
// .............................................................................
|
|
// /_api/collection/<identifier>/checksum
|
|
// .............................................................................
|
|
|
|
if (sub === "checksum") {
|
|
var withRevisions = false;
|
|
var withData = false;
|
|
var value;
|
|
if (req.parameters.hasOwnProperty('withRevisions')) {
|
|
value = req.parameters.withRevisions.toLowerCase();
|
|
if (value === 'true' || value === 'yes' || value === 'on' || value === 'y' || value === '1') {
|
|
withRevisions = true;
|
|
}
|
|
}
|
|
|
|
if (req.parameters.hasOwnProperty('withData')) {
|
|
value = req.parameters.withData.toLowerCase();
|
|
if (value === 'true' || value === 'yes' || value === 'on' || value === 'y' || value === '1') {
|
|
withData = true;
|
|
}
|
|
}
|
|
|
|
result = collectionRepresentation(collection, false, false, false);
|
|
var checksum = collection.checksum(withRevisions, withData);
|
|
result.checksum = checksum.checksum;
|
|
result.revision = checksum.revision;
|
|
actions.resultOk(req, res, actions.HTTP_OK, result);
|
|
}
|
|
|
|
// .............................................................................
|
|
// /_api/collection/<identifier>/figures
|
|
// .............................................................................
|
|
|
|
else if (sub === "figures") {
|
|
result = collectionRepresentation(collection, true, true, true);
|
|
headers = {
|
|
location : databasePrefix(req, "/_api/collection/" + collection.name() + "/figures")
|
|
};
|
|
actions.resultOk(req, res, actions.HTTP_OK, result, headers);
|
|
}
|
|
|
|
// .............................................................................
|
|
// /_api/collection/<identifier>/count
|
|
// .............................................................................
|
|
|
|
else if (sub === "count") {
|
|
result = collectionRepresentation(collection, true, true, false);
|
|
headers = {
|
|
location : databasePrefix(req, "/_api/collection/" + collection.name() + "/count")
|
|
};
|
|
actions.resultOk(req, res, actions.HTTP_OK, result, headers);
|
|
}
|
|
|
|
// .............................................................................
|
|
// /_api/collection/<identifier>/properties
|
|
// .............................................................................
|
|
|
|
else if (sub === "properties") {
|
|
result = collectionRepresentation(collection, true, false, false);
|
|
headers = {
|
|
location : databasePrefix(req, "/_api/collection/" + collection.name() + "/properties")
|
|
};
|
|
actions.resultOk(req, res, actions.HTTP_OK, result, headers);
|
|
}
|
|
|
|
// .............................................................................
|
|
// /_api/collection/<identifier>/revision
|
|
// .............................................................................
|
|
|
|
else if (sub === "revision") {
|
|
result = collectionRepresentation(collection, false, false, false);
|
|
result.revision = collection.revision();
|
|
actions.resultOk(req, res, actions.HTTP_OK, result);
|
|
}
|
|
|
|
else {
|
|
actions.resultNotFound(req, res, arangodb.ERROR_HTTP_NOT_FOUND,
|
|
"expecting one of the resources 'count',"
|
|
+" 'figures', 'properties', 'parameter'");
|
|
}
|
|
}
|
|
else {
|
|
actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER,
|
|
"expect GET /_api/collection/<collection-name>/<method>");
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief was docuBlock JSF_put_api_collection_load
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function put_api_collection_load (req, res, collection) {
|
|
try {
|
|
collection.load();
|
|
|
|
var showCount = true;
|
|
var body = actions.getJsonBody(req, res);
|
|
|
|
if (body && body.hasOwnProperty("count")) {
|
|
showCount = body.count;
|
|
}
|
|
|
|
var result = collectionRepresentation(collection, false, showCount, false);
|
|
|
|
actions.resultOk(req, res, actions.HTTP_OK, result);
|
|
}
|
|
catch (err) {
|
|
actions.resultException(req, res, err, undefined, false);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief was docuBlock JSF_put_api_collection_unload
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function put_api_collection_unload (req, res, collection) {
|
|
try {
|
|
if (req.parameters.hasOwnProperty('flush')) {
|
|
var value = req.parameters.flush.toLowerCase();
|
|
if (value === 'true' || value === 'yes' || value === 'on' || value === 'y' || value === '1') {
|
|
if (collection.status() === 3 /* loaded */ &&
|
|
collection.figures().uncollectedLogfileEntries > 0) {
|
|
// flush WAL so uncollected logfile entries can get collected
|
|
require("internal").wal.flush();
|
|
}
|
|
}
|
|
}
|
|
|
|
// then unload
|
|
collection.unload();
|
|
|
|
var result = collectionRepresentation(collection);
|
|
|
|
actions.resultOk(req, res, actions.HTTP_OK, result);
|
|
}
|
|
catch (err) {
|
|
actions.resultException(req, res, err, undefined, false);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief was docuBlock JSF_put_api_collection_truncate
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function put_api_collection_truncate (req, res, collection) {
|
|
try {
|
|
collection.truncate();
|
|
|
|
var result = collectionRepresentation(collection);
|
|
|
|
actions.resultOk(req, res, actions.HTTP_OK, result);
|
|
}
|
|
catch (err) {
|
|
actions.resultException(req, res, err, undefined, false);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief was docuBlock JSF_put_api_collection_properties
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function put_api_collection_properties (req, res, collection) {
|
|
var body = actions.getJsonBody(req, res);
|
|
|
|
if (body === undefined) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
collection.properties(body);
|
|
|
|
var result = collectionRepresentation(collection, true);
|
|
|
|
actions.resultOk(req, res, actions.HTTP_OK, result);
|
|
}
|
|
catch (err) {
|
|
actions.resultException(req, res, err, undefined, false);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief was docuBlock JSF_put_api_collection_rename
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function put_api_collection_rename (req, res, collection) {
|
|
var body = actions.getJsonBody(req, res);
|
|
|
|
if (body === undefined) {
|
|
return;
|
|
}
|
|
|
|
if (! body.hasOwnProperty("name")) {
|
|
actions.resultBad(req, res, arangodb.ERROR_ARANGO_ILLEGAL_NAME,
|
|
"name must be non-empty");
|
|
return;
|
|
}
|
|
|
|
var name = body.name;
|
|
|
|
try {
|
|
collection.rename(name);
|
|
|
|
var result = collectionRepresentation(collection);
|
|
|
|
actions.resultOk(req, res, actions.HTTP_OK, result);
|
|
}
|
|
catch (err) {
|
|
actions.resultException(req, res, err, undefined, false);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief was docuBlock JSF_put_api_collection_rotate
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function put_api_collection_rotate (req, res, collection) {
|
|
try {
|
|
collection.rotate();
|
|
|
|
actions.resultOk(req, res, actions.HTTP_OK, { result: true });
|
|
}
|
|
catch (err) {
|
|
actions.resultException(req, res, err, undefined, false);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief changes a collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function put_api_collection (req, res) {
|
|
if (req.suffix.length !== 2) {
|
|
actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER,
|
|
"expected PUT /_api/collection/<collection-name>/<action>");
|
|
return;
|
|
}
|
|
|
|
var name = decodeURIComponent(req.suffix[0]);
|
|
var collection = arangodb.db._collection(name);
|
|
|
|
if (collection === null) {
|
|
actions.collectionNotFound(req, res, name);
|
|
return;
|
|
}
|
|
|
|
var sub = decodeURIComponent(req.suffix[1]);
|
|
|
|
if (sub === "load") {
|
|
put_api_collection_load(req, res, collection);
|
|
}
|
|
else if (sub === "unload") {
|
|
put_api_collection_unload(req, res, collection);
|
|
collection = null;
|
|
// run garbage collection once in all threads
|
|
require("internal").executeGlobalContextFunction("collectGarbage");
|
|
}
|
|
else if (sub === "truncate") {
|
|
put_api_collection_truncate(req, res, collection);
|
|
}
|
|
else if (sub === "properties") {
|
|
put_api_collection_properties(req, res, collection);
|
|
}
|
|
else if (sub === "rename") {
|
|
put_api_collection_rename(req, res, collection);
|
|
}
|
|
else if (sub === "rotate") {
|
|
put_api_collection_rotate(req, res, collection);
|
|
}
|
|
else {
|
|
actions.resultNotFound(req, res, arangodb.ERROR_HTTP_NOT_FOUND,
|
|
"expecting one of the actions 'load', 'unload',"
|
|
+ " 'truncate', 'properties', 'rename'");
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief was docuBlock JSF_delete_api_collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function delete_api_collection (req, res) {
|
|
if (req.suffix.length !== 1) {
|
|
actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER,
|
|
"expected DELETE /_api/collection/<collection-name>");
|
|
}
|
|
else {
|
|
var name = decodeURIComponent(req.suffix[0]);
|
|
var collection = arangodb.db._collection(name);
|
|
|
|
if (collection === null) {
|
|
actions.collectionNotFound(req, res, name);
|
|
}
|
|
else {
|
|
try {
|
|
var result = {
|
|
id : collection._id
|
|
};
|
|
|
|
collection.drop();
|
|
|
|
actions.resultOk(req, res, actions.HTTP_OK, result);
|
|
}
|
|
catch (err) {
|
|
actions.resultException(req, res, err, undefined, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief handles a collection request
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
actions.defineHttp({
|
|
url : "_api/collection",
|
|
|
|
callback : function (req, res) {
|
|
try {
|
|
if (req.requestType === actions.GET) {
|
|
get_api_collection(req, res);
|
|
}
|
|
else if (req.requestType === actions.DELETE) {
|
|
delete_api_collection(req, res);
|
|
}
|
|
else if (req.requestType === actions.POST) {
|
|
post_api_collection(req, res);
|
|
}
|
|
else if (req.requestType === actions.PUT) {
|
|
put_api_collection(req, res);
|
|
}
|
|
else {
|
|
actions.resultUnsupported(req, res);
|
|
}
|
|
}
|
|
catch (err) {
|
|
actions.resultException(req, res, err, undefined, false);
|
|
}
|
|
}
|
|
});
|
|
|
|
|