<commit_before>/**
 * @module data-api
 * @summary: This module's purpose is to: abstract the underlying persistence layer
 * from the business logic.
 *
 * @description:
 * If you don't always use DynamoDB here is where that would be abstracted.  Conditionals on tableName
 * and other parameters would route data through a different transport to be stored or retrieved from
 * a different persistent medium.  In that case, this should be moved to a module "dynamo" and
 * included here seperately to abstract the dynamo specific logic and let this module simply
 * call the correct "database driver" methods and return results.
 *
 * Author: <NAME>
 * Created On: 2015-07-06.
 * @license Apache-2.0
 */

"use strict";

var p = require('bluebird'),
  _ = require('lodash');


module.exports = function construct(config, log) {
  var m = {};
  config = config ? config : {};
  config = _.defaults(config, {});

  log = log || {log: _.empty, error: _.empty};
  var Dynamite = require('dynamite');
  var dynamite = new Dynamite.Client(config.aws);

  var aws = global.AWS || require('aws-sdk');
  var awsClient = new aws.DynamoDB(); // May need to pass it apiVersion: {apiVersion: '2012-08-10'}
  var dynamo = awsClient;
  var docClient = new require('dynamodb-doc').DynamoDB(awsClient);

  /**
   * Stores data about tables in the database so that keys and indexes can be queried effectively.
   * @type {{}}
   */
  config.tables = {};

  m.update = function (table, filter, item) {
    var query = dynamite.newUpdateBuilder(table);
    return m.init(table)
      .then(function(tableMeta) {
        log.debug('Processing filters...');
        processFilter(tableMeta, query, filter);
        query.enableUpsert();
        _.each(item, function(val, key) {
          query.putAttribute(key, val);
        });
        //_.each(params.addItem, function(val, key) {
        //  query.addToAttribute(key, val);
        //});
        return executeQuery(query);
      });
  };

  m.delete = function(table, filter) {
    var query = dynamite.deleteItem(table);
    return m.init(table)
      .then(function(tableMeta) {
        processFilter(tableMeta, query, filter);
        return executeQuery(query);
      });
  };


  /**
   * Supports params itself as "item" or params = { item: {item} }
   * @param table
   * @param params
   * @returns {*}
   */
  m.insert = function(table, params) {
    log.log('dynamo.insert()', table, params);
    var def = p.defer();
    docClient.putItem({
      TableName: table,
      Item: params.item || params
    }, function(err, data) {
      if (err) return def.reject(err);
      else     {
        def.resolve(true);
      }
    });
    return def.promise;
  };
  m.save = m.insert;

  m.insertMany = function(table, items) {
    // TODO: implement using BatchWriteItem
    return p.map(items, function(item) {
      return m.insert(table, item);
    });
  };

  m.scan = function(table, params) {
    //var query = dynamite.newScanBuilder(table);
    //if(params.limit) {
    //  query.setLimit(params.limit);
    //}
    //return executeQuery(query, function(result) {
    //  return result.result;
    //});

    params = _.defaults(params || {}, {
      limit: 1000
    });

    var def = p.defer();
    docClient.scan({
      TableName: table,
      Limit: params.limit
    }, function(err, data) {
      if (err) return def.reject(err);
      else     {
        def.resolve(data.Items);
        console.log(data);
      }           // successful response
    });
    return def.promise;
  };

  m.query = function(table, filter, selection) {
    log.debug('Starting query...');
    var query = dynamite.newQueryBuilder(table);
    return m.init(table)
      .then(function(tableMeta) {
        log.debug('Processing filters...');
        processFilter(tableMeta, query, filter);
        if (filter.limit) {
          query.setLimit(filter.limit);
        }
        if (filter.sort == 'desc') {
          query.scanBackward();
        }
        if (filter.sort == 'asc') {
          query.scanForward();
        }

        if (selection) query.selectAttributes(selection);

        // optional. Checkout `QueryBuilder.js` for all supported comp operators.
        // .indexLessThan('GSI range key name', value)
        return executeQuery(query, function(result) {
          if (result.result && result.result.length > 0) return result.result;
          else return null;
        });
      });
  };

  m.getRowCount = function(table, params) {
    log.debug('Starting getRowCount()...', table, params);

    var def = p.defer();

    awsClient.scan({
      TableName: table
    }, function(err, result) {
      if (err) {
        def.reject(err);
      }
      else {
        def.resolve(result.Count);
      }
    });
    return def.promise;
  };

  function expressionify(query,commaSeperatedString) {
    var selectors = commaSeperatedString.split(',');

    if (selectors.length < 1) {
      // TODO: replace with return robustError()
      log.error('@BAD_PARAMETER', {details: {commaSeperatedString: commaSeperatedString}});
      throw "dynamo-data-api.find(): selection should be a comma separated string";
    }
    var expression = "";

    _.each(selectors, function(selector) {
      console.log('SELCTOR', selector);
      var expressionKey = '#'+selector;

      query.ExpressionAttributeNames = query.ExpressionAttributeNames || {}

      query.ExpressionAttributeNames[expressionKey] = selector;
      expression+=expressionKey+',';
    });
    return expression.substr(0, expression.length-1);
  }

  m.find = function(table, filter, selection) {
    log.debug('Running dynamo-data-api.find()...');
    var query = {TableName: table};

    return m.init(table)
      .then(function(tableMeta) {
        log.debug('Processing filters...');
        docFilter(tableMeta, query, filter);

        if (selection) {
          query.ProjectionExpression = expressionify(query,selection);
        }

        // optional. Checkout `QueryBuilder.js` for all supported comp operators.
        // .indexLessThan('GSI range key name', value)
        return execute('query',query, function(result) {
          console.log('RESULT', result);
          return result.Items[0];
        });
      });
  };

  m.init = function(tableName) {<commit_msg>Bugfix find()<commit_after>/**
 * @module data-api
 * @summary: This module's purpose is to: abstract the underlying persistence layer
 * from the business logic.
 *
 * @description:
 * If you don't always use DynamoDB here is where that would be abstracted.  Conditionals on tableName
 * and other parameters would route data through a different transport to be stored or retrieved from
 * a different persistent medium.  In that case, this should be moved to a module "dynamo" and
 * included here seperately to abstract the dynamo specific logic and let this module simply
 * call the correct "database driver" methods and return results.
 *
 * Author: <NAME>
 * Created On: 2015-07-06.
 * @license Apache-2.0
 */

"use strict";

var p = require('bluebird'),
  _ = require('lodash');


module.exports = function construct(config, log) {
  var m = {};
  config = config ? config : {};
  config = _.defaults(config, {});

  log = log || {log: _.empty, error: _.empty};
  var Dynamite = require('dynamite');
  var dynamite = new Dynamite.Client(config.aws);

  var aws = global.AWS || require('aws-sdk');
  var awsClient = new aws.DynamoDB(); // May need to pass it apiVersion: {apiVersion: '2012-08-10'}
  var dynamo = awsClient;
  var docClient = new require('dynamodb-doc').DynamoDB(awsClient);

  /**
   * Stores data about tables in the database so that keys and indexes can be queried effectively.
   * @type {{}}
   */
  config.tables = {};

  m.update = function (table, filter, item) {
    var query = dynamite.newUpdateBuilder(table);
    return m.init(table)
      .then(function(tableMeta) {
        log.debug('Processing filters...');
        processFilter(tableMeta, query, filter);
        query.enableUpsert();
        _.each(item, function(val, key) {
          query.putAttribute(key, val);
        });
        //_.each(params.addItem, function(val, key) {
        //  query.addToAttribute(key, val);
        //});
        return executeQuery(query);
      });
  };

  m.delete = function(table, filter) {
    var query = dynamite.deleteItem(table);
    return m.init(table)
      .then(function(tableMeta) {
        processFilter(tableMeta, query, filter);
        return executeQuery(query);
      });
  };


  /**
   * Supports params itself as "item" or params = { item: {item} }
   * @param table
   * @param params
   * @returns {*}
   */
  m.insert = function(table, params) {
    log.log('dynamo.insert()', table, params);
    var def = p.defer();
    docClient.putItem({
      TableName: table,
      Item: params.item || params
    }, function(err, data) {
      if (err) return def.reject(err);
      else     {
        def.resolve(true);
      }
    });
    return def.promise;
  };
  m.save = m.insert;

  m.insertMany = function(table, items) {
    // TODO: implement using BatchWriteItem
    return p.map(items, function(item) {
      return m.insert(table, item);
    });
  };

  m.scan = function(table, params) {
    //var query = dynamite.newScanBuilder(table);
    //if(params.limit) {
    //  query.setLimit(params.limit);
    //}
    //return executeQuery(query, function(result) {
    //  return result.result;
    //});

    params = _.defaults(params || {}, {
      limit: 1000
    });

    var def = p.defer();
    docClient.scan({
      TableName: table,
      Limit: params.limit
    }, function(err, data) {
      if (err) return def.reject(err);
      else     {
        def.resolve(data.Items);
        console.log(data);
      }           // successful response
    });
    return def.promise;
  };

  m.query = function(table, filter, selection) {
    log.debug('Starting query...');
    var query = dynamite.newQueryBuilder(table);
    return m.init(table)
      .then(function(tableMeta) {
        log.debug('Processing filters...');
        processFilter(tableMeta, query, filter);
        if (filter.limit) {
          query.setLimit(filter.limit);
        }
        if (filter.sort == 'desc') {
          query.scanBackward();
        }
        if (filter.sort == 'asc') {
          query.scanForward();
        }

        if (selection) query.selectAttributes(selection);

        // optional. Checkout `QueryBuilder.js` for all supported comp operators.
        // .indexLessThan('GSI range key name', value)
        return executeQuery(query, function(result) {
          if (result.result && result.result.length > 0) return result.result;
          else return null;
        });
      });
  };

  m.getRowCount = function(table, params) {
    log.debug('Starting getRowCount()...', table, params);

    var def = p.defer();

    awsClient.scan({
      TableName: table
    }, function(err, result) {
      if (err) {
        def.reject(err);
      }
      else {
        def.resolve(result.Count);
      }
    });
    return def.promise;
  };

  function expressionify(query,commaSeperatedString) {
    var selectors = commaSeperatedString.split(',');

    if (selectors.length < 1) {
      // TODO: replace with return robustError()
      log.error('@BAD_PARAMETER', {details: {commaSeperatedString: commaSeperatedString}});
      throw "dynamo-data-api.find(): selection should be a comma separated string";
    }
    var expression = "";

    _.each(selectors, function(selector) {
      var expressionKey = '#'+selector;

      query.ExpressionAttributeNames = query.ExpressionAttributeNames || {}

      query.ExpressionAttributeNames[expressionKey] = selector;
      expression+=expressionKey+',';
    });

    var projectionExpression =  expression.substr(0, expression.length-1);
    log.debug('dynamo-data-api.find(): projectionExpression', projectionExpression)
    return projectionExpression
  }

  m.find = function(table, filter, selection) {
    log.debug('Running dynamo-data-api.find()...');
    var query = {TableName: table};

    return m.init(table)
      .then(function(tableMeta) {
        log.debug('Processing filters...');
        docFilter(tableMeta, query, filter);

        if (selection) {
          query.ProjectionExpression = expressionify(query,selection);
        }

        // optional. Checkout `QueryBuilder.js` for all supported comp operators.
        // .indexLessThan('GSI range key name', value)
        return execute('query',query, function(result) {
          console.log('RESULT', result);
          return result.Items[0];
        });
      });
  };

  m.init = function(tableName) {
{'fraction_non_alphanumeric': 0.13873561406274634, 'fraction_numerical': 0.004256660886016081, 'mean_word_length': 2.5940509915014163, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1149981', 'n_tokens_mistral': 3820, 'n_tokens_neox': 3707, 'n_words': 941}