Class: Aws::Dynamodb::Query::Query

Inherits:
Object
  • Object
show all
Defined in:
lib/aws/dynamodb/query.rb

Class Method Summary collapse

Class Method Details

.call(table_name, model_class: nil, nullify_model_attributes: false, result_type: Struct, index_name: nil, select: nil, key_condition_expression: nil, expression_attribute_names: nil, expression_attribute_values: nil, scan_index_forward: true, return_consumed_capacity: nil, pagination: false, last_evaluated_key: nil, aws_region: ENV['AWS_REGION'], aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'], aws_secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'], aws_dynamodb_endpoint: ENV['aws_dynamodb_endpoint']) ⇒ Array

Queries Dynamodb for items.

Parameters:

  • table_name (String)

    The name of the table containing the requested items.

  • model_class (Class) (defaults to: nil)

    model record class used to find stored attributes

  • nullify_model_attributes (Boolean) (defaults to: false)

    false by default, when true assigns null to model attributes that are not returned with DB resultset

  • result_type (Class) (defaults to: Struct)

    model_class (pass the record class e.g. User), Struct, or Hash

  • index_name (String) (defaults to: nil)

    The name of an index to query

  • select (String) (defaults to: nil)

    The attributes to be returned in the result: ALL_ATTRIBUTES, ALL_PROJECTED_ATTRIBUTES, COUNT, SPECIFIC_ATTRIBUTES

  • key_condition_expression (String) (defaults to: nil)

    The condition that specifies the key value(s) for items to be retrieved by the Query action

  • expression_attribute_names (Hash) (defaults to: nil)

    One or more substitution tokens for attribute names in an expression

  • expression_attribute_values (Hash) (defaults to: nil)

    One or more values that can be substituted in an expression

  • scan_index_forward (String) (defaults to: true)

    Specifies the order for index traversal: If true (default), the traversal is performed in ascending order; if false, the traversal is performed in descending order

  • return_consumed_capacity (String) (defaults to: nil)

    Determines the level of detail about provisioned throughput consumption that is returned in the response: INDEXES, TOTAL, NONE

  • pagination (Boolean) (defaults to: false)

    true by default. This will get maximum 1MB of data. To retrieve all your table data which could exceed 1MB, set pagination to false.

  • last_evaluated_key (String) (defaults to: nil)

    if pagination is true, this will point to the next page of data to retrieve

  • aws_region (String) (defaults to: ENV['AWS_REGION'])

    AWS region is obtained by default from ENV, use this parameter to override with your own

  • aws_access_key_id (String) (defaults to: ENV['AWS_ACCESS_KEY_ID'])

    AWS access key id is obtained by default from ENV, use this parameter to override with your own

  • aws_secret_access_key (String) (defaults to: ENV['AWS_SECRET_ACCESS_KEY'])

    AWS secret access key is obtained by default from ENV, use this parameter to override with your own

  • aws_dynamodb_endpoint (String) (defaults to: ENV['aws_dynamodb_endpoint'])

    AWS dynamodb endpoint is obtained by default from ENV, use this parameter to override with your own

Returns:

  • (Array)

    array of Ruby Hash objects where each object contains all the values returned from dynamodb in dasherized format

See Also:



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/aws/dynamodb/query.rb', line 34

def self.call(table_name,
              model_class: nil,
              nullify_model_attributes: false,
              result_type: Struct,
              index_name: nil,
              select: nil,
              key_condition_expression: nil,
              expression_attribute_names: nil,
              expression_attribute_values: nil,
              scan_index_forward: true,
              return_consumed_capacity: nil,
              pagination: false,
              last_evaluated_key: nil,
              aws_region: ENV['AWS_REGION'],
              aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
              aws_secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'],
              aws_dynamodb_endpoint: ENV['aws_dynamodb_endpoint'])
  result = []
  # DyanmoDB Query results are divided into "pages" of data that are 1 MB in size (or less).
  # Agents data exceeds 1MB so we need multiple pages.
  # @see http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.Pagination
  last_evaluated_key = last_evaluated_key
  loop do
    payload = {
      TableName: table_name
    }
    # optional query parameters
    payload[:IndexName]                 = index_name if index_name
    payload[:select]                    = select if select
    payload[:KeyConditionExpression]    = key_condition_expression if key_condition_expression
    payload[:ExpressionAttributeNames]  = expression_attribute_names if expression_attribute_names
    payload[:ExpressionAttributeValues] = expression_attribute_values if expression_attribute_values
    payload[:scan_index_forward]        = scan_index_forward if scan_index_forward
    payload[:ReturnConsumedCapacity]    = return_consumed_capacity if return_consumed_capacity
    payload[:ExclusiveStartKey]         = last_evaluated_key if last_evaluated_key

    # generate AWS Authorization header
    aws_signature = Aws::Signature::V4::Signature.new(aws_region, aws_access_key_id, aws_secret_access_key)
    aws_signature.generate_signature('dynamodb', 'DynamoDB_20120810.Query', 'POST', payload, aws_dynamodb_endpoint, '/')
    # Create the HTTP objects
    uri = URI.parse(aws_dynamodb_endpoint)
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = aws_dynamodb_endpoint.include?('https')
    request = Net::HTTP::Post.new(uri.request_uri, aws_signature.headers)
    request.body = payload.to_json
    res = http.request(request)

    # error => return empty array
    unless res.is_a? Net::HTTPSuccess
      Rails.logger.error "Error querying DynamoDB: code=#{res.code} message=#{res.message} body=#{res.body}" if defined? Rails

      # jsonapi error format
      result = {
        errors: [
          status: res.code,
          title:  res.message,
          detail: res.body
        ]
      }
      return result
    end

    # Create DAO struct object <struct Struct::Customer id>
    Struct.new('AwsDynamodbQueryDAO', *model_class.attributes.attributes.keys) if result_type.eql? Struct

    # success => parse returned json
    json = Yajl::Parser.new.parse(res.body)

    # extract and construct resultset array
    json['Items'].each do |item|
      record = {}
      stored_attributes = []
      item.keys.each do |key|
        # @see: http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_AttributeValue.html
        # Sample dynamodb item: {"date_joined"=>{"S"=>"2017-09-30T16:00:00+00:00"}, "contact_country_code"=>{"NULL"=>true}, "full_name"=>{"S"=>"Breena"}, "has_bank_swift"=>{"BOOL"=>false}}
        # NULL will fall back to default nil
        # Number will be handled afterwards
        dynamodb_data_types = %w[S BS L M NS S SS BOOL]
        # value could be found in one of these data types
        key_dasherized = key.to_s.dasherize
        dynamodb_data_types.each do |data_type|
          record[key_dasherized] = record[key_dasherized] || item[key][data_type]
        end

        # handle numbers: integer or float
        value = item[key]['N']
        if record[key_dasherized].nil? and value.present?
          begin
            record[key_dasherized] = Integer(value)
          rescue ArgumentError
            record[key_dasherized] = Float(value)
          end
        end

        stored_attributes += [key.to_sym]
      end

      # nullify remaining model attributes to be included in returned result
      if nullify_model_attributes and model_class.respond_to? :attributes
        unset_attributes = model_class.attributes.attributes.keys - stored_attributes
        unset_attributes.each do |attr|
          record[attr.to_s.dasherize] = nil
        end
      end

      case result_type.to_s
        when Struct.to_s
          # order values in the same order as model attribute_keys
          attribute_values = []
          model_class.attributes.attributes.keys.each do |attribute_key|
            attribute_values << record[attribute_key.to_s.dasherize]
          end
          result << Struct::AwsDynamodbQueryDAO.new(*attribute_values)
        when model_class.to_s
          result << model_class.new(record)
        else
          result << record
      end
    end

    # LastEvaluatedKey in the response indicates that not all of the items have been retrieved.
    # It should be used as the ExclusiveStartKey for the next Query request to retrieve the next page items.
    last_evaluated_key = json['LastEvaluatedKey']
    # Break if pagination is enabled. Else, absence of LastEvaluatedKey indicates that there are no more pages to retrieve.
    break if pagination.present? || last_evaluated_key.blank?
  end
  result
end