1
0
Fork 0
arangodb/js/actions/_api/collection/app.js

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);
}
}
});