<commit_before>////////////////////////////////////////////////////////////////////////////////
/// @brief querying and managing collections
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2012 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 <NAME>
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

var actions = require("actions");
var API = "_api/collection";

// -----------------------------------------------------------------------------
// --SECTION--                                                 private functions
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup AvocadoAPI
/// @{
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/// @brief collection representation
////////////////////////////////////////////////////////////////////////////////

function CollectionRepresentation (collection, showParameter, showCount, showFigures) {
  var result = {};

  result.id = collection._id;
  result.name = collection.name();

  if (showParameter) {
    var parameter = collection.parameter();

    result.waitForSync = parameter.waitForSync;
    result.journalSize = parameter.journalSize;
  }

  if (showCount) {
    result.count = collection.count();
  }

  if (showFigures) {
    var figures = collection.figures();

    if (figures) {
      result.figures = {
        alive : {
          count : figures.numberAlive,
          size : figures.sizeAlive
        },
        dead : {
          count : figures.numberDead,
          size : figures.sizeDead
        },
        datafiles : {
          count : figures.numberDatafiles
        }
      };
    }
  }

  result.status = collection.status();

  return result;
}

////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

// -----------------------------------------------------------------------------
// --SECTION--                                                  public functions
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup AvocadoAPI
/// @{
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/// @brief creates a collection
///
/// @REST{POST /_api/collection}
///
/// Creates an new collection with a given name. The request must contain an
/// object with the following attributes.
///
/// @LIT{name}: The name of the collection.
///
/// @LIT{waitForSync} (optional, default: false): If @LIT{true} then the data
/// is synchronised to disk before returning from a create or update of an
/// document.
///
/// @EXAMPLES
///
/// @verbinclude api-collection-create-collection
////////////////////////////////////////////////////////////////////////////////

function POST_api_collection (req, res) {
  var body;

  try {
    body = JSON.parse(req.requestBody || "{}") || {};
  }
  catch (err) {
    actions.resultBad(req, res, actions.ERROR_HTTP_CORRUPTED_JSON, err);
    return;
  }

  if (! body.hasOwnProperty("name")) {
    actions.resultBad(req, res, actions.ERROR_AVOCADO_ILLEGAL_NAME,
                      "name must be non-empty");
    return;
  }

  var name = body.name;
  var waitForSync = false;

  if (body.hasOwnProperty("waitForSync")) {
    waitForSync = body.waitForSync;
  }

  try {
    var collection = db._create(name, waitForSync);

    var result = {};
    var headers = {};

    result.id = collection._id;
    result.name = collection.name();
    result.waitForSync = waitForSync;
    result.status = collection.status();

    headers.location = "/" + API + "/" + collection._id;
      
    actions.resultOk(req, res, actions.HTTP_OK, result, headers);
  }
  catch (err) {
    actions.resultException(req, res, err);
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @brief returns all collections
///
/// @REST{GET /_api/collection}
///
/// Returns an object with an attribute @LIT{collections} containing a 
/// list of all collection descriptions. The same information is also
/// available in the @LIT{names} as hash map with the collection names
/// as keys.
///
/// @EXAMPLES
///
/// Return information about all collections:
///
/// @verbinclude api-collection-all-collections
////////////////////////////////////////////////////////////////////////////////

function GET_api_collections (req, res) {
  var list = [];
  var names = {};
  var collections = db._collections();

  for (var i = 0;  i < collections.length;  ++i) {
    var collection = collections[i];
    var rep = CollectionRepresentation(collection, true);
    
    list.push(rep);
    names[rep.name] = rep;
  }

  var result = { collections : list, names : names };
  
  actions.resultOk(req, res, actions.HTTP_OK, result);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief returns a collection
///
/// @REST{GET /_api/collection/@FA{collection-identifier}}
//////////////////////////////////////////////////////////
///
/// The result is an objects describing the collection with the following
/// attributes:
///
/// @LIT{id}: The identifier of the collection.
///
/// @LIT{name}: The name of the collection.
///
/// @LIT{waitForSync}: If @LIT{true} then creating or changing a document will
/// wait until the data has been synchronised to disk.
///
/// @LIT{status}: The status of the collection as number.
///
/// - 1: new born collection
/// - 2: unloaded
/// - 3: loaded
/// - 4: in the process of being unloaded
/// - 5: deleted
///
/// Every other status indicates a corrupted collection.
///
/// If the @FA{collection-identifier} is unknown, then a @LIT{HTTP 404} is
/// returned.
///
/// It is possible to specify a name instead of an identifier.  In this case the
/// response will contain a field "Location" which contains the correct
/// location.
///
/// @REST{GET /_api/collection/@FA{collection-identifier}/count}
////////////////////////////////////////////////////////////////
///
/// In addition to the above, the result also contains the number of documents.
/// Note that this will always load the collection into memory.
///
/// @LIT{count}: The number of documents inside the collection.
///
/// @REST{GET /_api/collection/@FA{collection-identifier}/figures}
//////////////////////////////////////////////////////////////////
///
/// In addition to the above, the result also contains the number of documents
/// and additional statistical information about the collection.  Note that this
/// will always load the collection into memory.
///
/// @LIT{count}: The number of documents inside the collection.
///
/// @LIT{figures.alive.count}: The number of living documents.
///
/// @LIT{figures.alive.size}: The total size in bytes used by all living
/// documents.
///
/// @LIT{figures.dead.count}: The number of dead documents.
///
/// @LIT{figures.dead.size}: The total size in bytes used by all dead
/// documents.
///
/// @LIT{figures.datafile.count}: The number of active datafiles.
///
/// @LIT{journalSize}: The maximal size of the journal in bytes.
///
/// @EXAMPLES
/////////////
///
/// Using an identifier:
///
/// @verbinclude api-collection-get-collection-identifier
///
/// Using a name:
///
/// @verbinclude api-collection-get-collection-name
///
/// Using an identifier and requesting the number of documents:
///
/// @verbinclude api-collection-get-collection-count
///
/// Using an identifier and requesting the figures of the collection:
///
/// @verbinclude api-collection-get-collection-figures
////////////////////////////////////////////////////////////////////////////////

function GET_api_collection (req, res) {

  // .............................................................................
  // /_api/collection
  // .............................................................................

  if (req.suffix.length == 0) {
    GET_api_collections(req, res);
  }
  else {
    var name = decodeURIComponent(req.suffix[0]);
    var id = parseInt(name) || name;
    var collection = db._collection(id);
    
    if (collection == null) {
      actions.collectionNotFound(req, res, name);
    }
    else {
      // .............................................................................
      // /_api/collection/<identifier>
      // .............................................................................

      if (req.suffix.length == 1) {
        var result = CollectionRepresentation(collection, true, false, false);
        var headers = { location : "/" + API + "/" + collection._id };

        actions.resultOk(req, res, actions.HTTP_OK, result, headers);
      }

      else if (req.suffix.length == 2) {
        var sub = decodeURIComponent(req.suffix[1]);

        // .............................................................................
        // /_api/collection/<identifier>/figures
        // .............................................................................

        if (sub == "figures") {
          var result = CollectionRepresentation(collection, true, true, true);
          var headers = { location : "/" + API + "/" + collection._id + "/figures" };

          actions.resultOk(req, res, actions.HTTP_OK, result, headers);
        }

        // .............................................................................
        // /_api/collection/<identifier>/count
        // .............................................................................

        else if (sub == "count") {
          var result = CollectionRepresentation(collection, true, true, false);
          var headers = { location : "/" + API + "/" + collection._id + "/count" };
      
          actions.resultOk(req, res, actions.HTTP_OK, result, headers);
        }

        else {
          actions.resultNotFound(req, res, "expecting one of the resources 'count', 'figures'");
        }
      }
      else {
        actions.resultBad(req, res, actions.ERROR_HTTP_BAD_PARAMETER,
                          "expect GET /" + API + "/<collection-identifer>/<method>");
      }
    }
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @brief loads a collection
///
/// @REST{PUT /_api/collection/@FA{collection-identifier}/load}
///
/// Loads a collection into memory.  On success an object with the following
///
/// @LIT{id}: The identifier of the collection.
///<commit_msg>re-fixed issue #38: added API method GET /_api/collection/parameter<commit_after>////////////////////////////////////////////////////////////////////////////////
/// @brief querying and managing collections
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2012 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 <NAME>
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

var actions = require("actions");
var API = "_api/collection";

// -----------------------------------------------------------------------------
// --SECTION--                                                 private functions
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup AvocadoAPI
/// @{
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/// @brief collection representation
////////////////////////////////////////////////////////////////////////////////

function CollectionRepresentation (collection, showParameter, showCount, showFigures) {
  var result = {};

  result.id = collection._id;
  result.name = collection.name();

  if (showParameter) {
    var parameter = collection.parameter();

    result.waitForSync = parameter.waitForSync;
    result.journalSize = parameter.journalSize;
  }

  if (showCount) {
    result.count = collection.count();
  }

  if (showFigures) {
    var figures = collection.figures();

    if (figures) {
      result.figures = {
        alive : {
          count : figures.numberAlive,
          size : figures.sizeAlive
        },
        dead : {
          count : figures.numberDead,
          size : figures.sizeDead
        },
        datafiles : {
          count : figures.numberDatafiles
        }
      };
    }
  }

  result.status = collection.status();

  return result;
}

////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

// -----------------------------------------------------------------------------
// --SECTION--                                                  public functions
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup AvocadoAPI
/// @{
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/// @brief creates a collection
///
/// @REST{POST /_api/collection}
///
/// Creates an new collection with a given name. The request must contain an
/// object with the following attributes.
///
/// @LIT{name}: The name of the collection.
///
/// @LIT{waitForSync} (optional, default: false): If @LIT{true} then the data
/// is synchronised to disk before returning from a create or update of an
/// document.
///
/// @EXAMPLES
///
/// @verbinclude api-collection-create-collection
////////////////////////////////////////////////////////////////////////////////

function POST_api_collection (req, res) {
  var body;

  try {
    body = JSON.parse(req.requestBody || "{}") || {};
  }
  catch (err) {
    actions.resultBad(req, res, actions.ERROR_HTTP_CORRUPTED_JSON, err);
    return;
  }

  if (! body.hasOwnProperty("name")) {
    actions.resultBad(req, res, actions.ERROR_AVOCADO_ILLEGAL_NAME,
                      "name must be non-empty");
    return;
  }

  var name = body.name;
  var waitForSync = false;

  if (body.hasOwnProperty("waitForSync")) {
    waitForSync = body.waitForSync;
  }

  try {
    var collection = db._create(name, waitForSync);

    var result = {};
    var headers = {};

    result.id = collection._id;
    result.name = collection.name();
    result.waitForSync = waitForSync;
    result.status = collection.status();

    headers.location = "/" + API + "/" + collection._id;
      
    actions.resultOk(req, res, actions.HTTP_OK, result, headers);
  }
  catch (err) {
    actions.resultException(req, res, err);
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @brief returns all collections
///
/// @REST{GET /_api/collection}
///
/// Returns an object with an attribute @LIT{collections} containing a 
/// list of all collection descriptions. The same information is also
/// available in the @LIT{names} as hash map with the collection names
/// as keys.
///
/// @EXAMPLES
///
/// Return information about all collections:
///
/// @verbinclude api-collection-all-collections
////////////////////////////////////////////////////////////////////////////////

function GET_api_collections (req, res) {
  var list = [];
  var names = {};
  var collections = db._collections();

  for (var i = 0;  i < collections.length;  ++i) {
    var collection = collections[i];
    var rep = CollectionRepresentation(collection);
    
    list.push(rep);
    names[rep.name] = rep;
  }

  var result = { collections : list, names : names };
  
  actions.resultOk(req, res, actions.HTTP_OK, result);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief returns a collection
///
/// @REST{GET /_api/collection/@FA{collection-identifier}}
//////////////////////////////////////////////////////////
///
/// The result is an objects describing the collection with the following
/// attributes:
///
/// @LIT{id}: The identifier of the collection.
///
/// @LIT{name}: The name of the collection.
///
/// @LIT{status}: The status of the collection as number.
///
/// - 1: new born collection
/// - 2: unloaded
/// - 3: loaded
/// - 4: in the process of being unloaded
/// - 5: deleted
///
/// Every other status indicates a corrupted collection.
///
/// If the @FA{collection-identifier} is unknown, then a @LIT{HTTP 404} is
/// returned.
///
/// It is possible to specify a name instead of an identifier.  In this case the
/// response will contain a field "Location" which contains the correct
/// location.
///
/// @REST{GET /_api/collection/@FA{collection-identifier}/parameter}
////////////////////////////////////////////////////////////////
///
/// In addition to the above, the result will always contain the
/// @LIT{waitForSync} parameter. This is achieved by forcing a load of
/// the underlying collection.
///
/// @LIT{waitForSync}: If @LIT{true} then creating or changing a document will
/// wait until the data has been synchronised to disk. 
///
/// @LIT{count}: The number of documents inside the collection.
///
/// @REST{GET /_api/collection/@FA{collection-identifier}/count}
////////////////////////////////////////////////////////////////
///
/// In addition to the above, the result also contains the number of documents.
/// Note that this will always load the collection into memory.
///
/// @LIT{count}: The number of documents inside the collection.
///
/// @REST{GET /_api/collection/@FA{collection-identifier}/figures}
//////////////////////////////////////////////////////////////////
///
/// In addition to the above, the result also contains the number of documents
/// and additional statistical information about the collection.  Note that this
/// will always load the collection into memory.
///
/// @LIT{count}: The number of documents inside the collection.
///
/// @LIT{figures.alive.count}: The number of living documents.
///
/// @LIT{figures.alive.size}: The total size in bytes used by all living
/// documents.
///
/// @LIT{figures.dead.count}: The number of dead documents.
///
/// @LIT{figures.dead.size}: The total size in bytes used by all dead
/// documents.
///
/// @LIT{figures.datafile.count}: The number of active datafiles.
///
/// @LIT{journalSize}: The maximal size of the journal in bytes.
///
/// @EXAMPLES
/////////////
///
/// Using an identifier:
///
/// @verbinclude api-collection-get-collection-identifier
///
/// Using a name:
///
/// @verbinclude api-collection-get-collection-name
///
/// Using an identifier and requesting the number of documents:
///
/// @verbinclude api-collection-get-collection-count
///
/// Using an identifier and requesting the figures of the collection:
///
/// @verbinclude api-collection-get-collection-figures
////////////////////////////////////////////////////////////////////////////////

function GET_api_collection (req, res) {

  // .............................................................................
  // /_api/collection
  // .............................................................................

  if (req.suffix.length == 0) {
    GET_api_collections(req, res);
  }
  else {
    var name = decodeURIComponent(req.suffix[0]);
    var id = parseInt(name) || name;
    var collection = db._collection(id);
    
    if (collection == null) {
      actions.collectionNotFound(req, res, name);
    }
    else {
      // .............................................................................
      // /_api/collection/<identifier>
      // .............................................................................

      if (req.suffix.length == 1) {
        var result = CollectionRepresentation(collection, false, false, false);
        var headers = { location : "/" + API + "/" + collection._id };

        actions.resultOk(req, res, actions.HTTP_OK, result, headers);
      }

      else if (req.suffix.length == 2) {
        var sub = decodeURIComponent(req.suffix[1]);

        // .............................................................................
        // /_api/collection/<identifier>/figures
        // .............................................................................

        if (sub == "figures") {
          var result = CollectionRepresentation(collection, true, true, true);
          var headers = { location : "/" + API + "/" + collection._id + "/figures" };

          actions.resultOk(req, res, actions.HTTP_OK, result, headers);
        }

        // .............................................................................
        // /_api/collection/<identifier>/count
        // .............................................................................

        else if (sub == "count") {
          var result = CollectionRepresentation(collection, true, true, false);
          var headers = { location : "/" + API + "/" + collection._id + "/count" };
      
          actions.resultOk(req, res, actions.HTTP_OK, result, headers);
        }
        
        // .............................................................................
        // /_api/collection/<identifier>/parameter
        // .............................................................................

        else if (sub == "parameter") {
          var result = CollectionRepresentation(collection, true, false, false);
          var headers = { location : "/" + API + "/" + collection._id + "/parameter" };
      
          actions.resultOk(req, res, actions.HTTP_OK, result, headers);
        }

        else {
          actions.resultNotFound(req, res, "expecting one of the resources 'count', 'figures', 'parameter'");
        }
      }
      else {
        actions.resultBad(req, res, actions.ERROR_HTTP_BAD_PARAMETER,
                          "expect GET /" + API + "/<collection-identifer>/<method>");
      }
    }
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @brief loads a collection
///
/// @REST{PUT /_api/collection/@FA{collection-identifier}/load}
///
/// Loads a collection into memory.  On success an object with the following
///
/// @LIT{id}: The identifier of the collection.
///
{'fraction_non_alphanumeric': 0.33112168252378565, 'fraction_numerical': 0.002253380070105158, 'mean_word_length': 1.2293023255813953, 'pattern_counts': {'":': 0, '<': 18, '<?xml version=': 0, '>': 16, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6594869', 'n_tokens_mistral': 5569, 'n_tokens_neox': 5388, 'n_words': 1657}