dynamoid 3.9.0 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -7
  3. data/README.md +20 -23
  4. data/dynamoid.gemspec +1 -2
  5. data/lib/dynamoid/adapter.rb +18 -12
  6. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/create_table.rb +2 -2
  7. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/filter_expression_convertor.rb +78 -0
  8. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/item_updater.rb +19 -1
  9. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/projection_expression_convertor.rb +38 -0
  10. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/query.rb +46 -61
  11. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/scan.rb +33 -27
  12. data/lib/dynamoid/adapter_plugin/aws_sdk_v3.rb +87 -62
  13. data/lib/dynamoid/associations/belongs_to.rb +6 -6
  14. data/lib/dynamoid/associations.rb +1 -1
  15. data/lib/dynamoid/config/options.rb +12 -12
  16. data/lib/dynamoid/config.rb +1 -0
  17. data/lib/dynamoid/criteria/chain.rb +95 -133
  18. data/lib/dynamoid/criteria/key_fields_detector.rb +6 -7
  19. data/lib/dynamoid/criteria/nonexistent_fields_detector.rb +2 -2
  20. data/lib/dynamoid/criteria/where_conditions.rb +29 -0
  21. data/lib/dynamoid/dirty.rb +1 -1
  22. data/lib/dynamoid/document.rb +1 -1
  23. data/lib/dynamoid/dumping.rb +2 -2
  24. data/lib/dynamoid/fields/declare.rb +6 -6
  25. data/lib/dynamoid/fields.rb +6 -8
  26. data/lib/dynamoid/finders.rb +17 -26
  27. data/lib/dynamoid/indexes.rb +6 -7
  28. data/lib/dynamoid/loadable.rb +2 -2
  29. data/lib/dynamoid/persistence/save.rb +12 -16
  30. data/lib/dynamoid/persistence/update_fields.rb +2 -2
  31. data/lib/dynamoid/persistence/update_validations.rb +1 -1
  32. data/lib/dynamoid/persistence.rb +39 -4
  33. data/lib/dynamoid/type_casting.rb +15 -14
  34. data/lib/dynamoid/undumping.rb +1 -1
  35. data/lib/dynamoid/version.rb +1 -1
  36. metadata +17 -16
  37. data/lib/dynamoid/criteria/ignored_conditions_detector.rb +0 -41
  38. data/lib/dynamoid/criteria/overwritten_conditions_detector.rb +0 -40
@@ -14,7 +14,7 @@ module Dynamoid
14
14
  module AdapterPlugin
15
15
  # The AwsSdkV3 adapter provides support for the aws-sdk version 2 for ruby.
16
16
 
17
- # Note: Don't use keyword arguments in public methods as far as method
17
+ # NOTE: Don't use keyword arguments in public methods as far as method
18
18
  # calls on adapter are delegated to the plugin.
19
19
  #
20
20
  # There are breaking changes in Ruby related to delegating keyword
@@ -24,31 +24,6 @@ module Dynamoid
24
24
 
25
25
  class AwsSdkV3
26
26
  EQ = 'EQ'
27
- RANGE_MAP = {
28
- range_greater_than: 'GT',
29
- range_less_than: 'LT',
30
- range_gte: 'GE',
31
- range_lte: 'LE',
32
- range_begins_with: 'BEGINS_WITH',
33
- range_between: 'BETWEEN',
34
- range_eq: 'EQ'
35
- }.freeze
36
-
37
- FIELD_MAP = {
38
- eq: 'EQ',
39
- ne: 'NE',
40
- gt: 'GT',
41
- lt: 'LT',
42
- gte: 'GE',
43
- lte: 'LE',
44
- begins_with: 'BEGINS_WITH',
45
- between: 'BETWEEN',
46
- in: 'IN',
47
- contains: 'CONTAINS',
48
- not_contains: 'NOT_CONTAINS',
49
- null: 'NULL',
50
- not_null: 'NOT_NULL',
51
- }.freeze
52
27
  HASH_KEY = 'HASH'
53
28
  RANGE_KEY = 'RANGE'
54
29
  STRING_TYPE = 'S'
@@ -70,26 +45,72 @@ module Dynamoid
70
45
 
71
46
  CONNECTION_CONFIG_OPTIONS = %i[endpoint region http_continue_timeout http_idle_timeout http_open_timeout http_read_timeout].freeze
72
47
 
73
- attr_reader :table_cache
48
+ # See https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html
49
+ # rubocop:disable Metrics/CollectionLiteralLength
50
+ RESERVED_WORDS = Set.new(
51
+ %i[
52
+ ABORT ABSOLUTE ACTION ADD AFTER AGENT AGGREGATE ALL ALLOCATE ALTER ANALYZE
53
+ AND ANY ARCHIVE ARE ARRAY AS ASC ASCII ASENSITIVE ASSERTION ASYMMETRIC AT
54
+ ATOMIC ATTACH ATTRIBUTE AUTH AUTHORIZATION AUTHORIZE AUTO AVG BACK BACKUP
55
+ BASE BATCH BEFORE BEGIN BETWEEN BIGINT BINARY BIT BLOB BLOCK BOOLEAN BOTH
56
+ BREADTH BUCKET BULK BY BYTE CALL CALLED CALLING CAPACITY CASCADE CASCADED
57
+ CASE CAST CATALOG CHAR CHARACTER CHECK CLASS CLOB CLOSE CLUSTER CLUSTERED
58
+ CLUSTERING CLUSTERS COALESCE COLLATE COLLATION COLLECTION COLUMN COLUMNS
59
+ COMBINE COMMENT COMMIT COMPACT COMPILE COMPRESS CONDITION CONFLICT CONNECT
60
+ CONNECTION CONSISTENCY CONSISTENT CONSTRAINT CONSTRAINTS CONSTRUCTOR
61
+ CONSUMED CONTINUE CONVERT COPY CORRESPONDING COUNT COUNTER CREATE CROSS
62
+ CUBE CURRENT CURSOR CYCLE DATA DATABASE DATE DATETIME DAY DEALLOCATE DEC
63
+ DECIMAL DECLARE DEFAULT DEFERRABLE DEFERRED DEFINE DEFINED DEFINITION
64
+ DELETE DELIMITED DEPTH DEREF DESC DESCRIBE DESCRIPTOR DETACH DETERMINISTIC
65
+ DIAGNOSTICS DIRECTORIES DISABLE DISCONNECT DISTINCT DISTRIBUTE DO DOMAIN
66
+ DOUBLE DROP DUMP DURATION DYNAMIC EACH ELEMENT ELSE ELSEIF EMPTY ENABLE
67
+ END EQUAL EQUALS ERROR ESCAPE ESCAPED EVAL EVALUATE EXCEEDED EXCEPT
68
+ EXCEPTION EXCEPTIONS EXCLUSIVE EXEC EXECUTE EXISTS EXIT EXPLAIN EXPLODE
69
+ EXPORT EXPRESSION EXTENDED EXTERNAL EXTRACT FAIL FALSE FAMILY FETCH FIELDS
70
+ FILE FILTER FILTERING FINAL FINISH FIRST FIXED FLATTERN FLOAT FOR FORCE
71
+ FOREIGN FORMAT FORWARD FOUND FREE FROM FULL FUNCTION FUNCTIONS GENERAL
72
+ GENERATE GET GLOB GLOBAL GO GOTO GRANT GREATER GROUP GROUPING HANDLER HASH
73
+ HAVE HAVING HEAP HIDDEN HOLD HOUR IDENTIFIED IDENTITY IF IGNORE IMMEDIATE
74
+ IMPORT IN INCLUDING INCLUSIVE INCREMENT INCREMENTAL INDEX INDEXED INDEXES
75
+ INDICATOR INFINITE INITIALLY INLINE INNER INNTER INOUT INPUT INSENSITIVE
76
+ INSERT INSTEAD INT INTEGER INTERSECT INTERVAL INTO INVALIDATE IS ISOLATION
77
+ ITEM ITEMS ITERATE JOIN KEY KEYS LAG LANGUAGE LARGE LAST LATERAL LEAD
78
+ LEADING LEAVE LEFT LENGTH LESS LEVEL LIKE LIMIT LIMITED LINES LIST LOAD
79
+ LOCAL LOCALTIME LOCALTIMESTAMP LOCATION LOCATOR LOCK LOCKS LOG LOGED LONG
80
+ LOOP LOWER MAP MATCH MATERIALIZED MAX MAXLEN MEMBER MERGE METHOD METRICS
81
+ MIN MINUS MINUTE MISSING MOD MODE MODIFIES MODIFY MODULE MONTH MULTI
82
+ MULTISET NAME NAMES NATIONAL NATURAL NCHAR NCLOB NEW NEXT NO NONE NOT NULL
83
+ NULLIF NUMBER NUMERIC OBJECT OF OFFLINE OFFSET OLD ON ONLINE ONLY OPAQUE
84
+ OPEN OPERATOR OPTION OR ORDER ORDINALITY OTHER OTHERS OUT OUTER OUTPUT
85
+ OVER OVERLAPS OVERRIDE OWNER PAD PARALLEL PARAMETER PARAMETERS PARTIAL
86
+ PARTITION PARTITIONED PARTITIONS PATH PERCENT PERCENTILE PERMISSION
87
+ PERMISSIONS PIPE PIPELINED PLAN POOL POSITION PRECISION PREPARE PRESERVE
88
+ PRIMARY PRIOR PRIVATE PRIVILEGES PROCEDURE PROCESSED PROJECT PROJECTION
89
+ PROPERTY PROVISIONING PUBLIC PUT QUERY QUIT QUORUM RAISE RANDOM RANGE RANK
90
+ RAW READ READS REAL REBUILD RECORD RECURSIVE REDUCE REF REFERENCE
91
+ REFERENCES REFERENCING REGEXP REGION REINDEX RELATIVE RELEASE REMAINDER
92
+ RENAME REPEAT REPLACE REQUEST RESET RESIGNAL RESOURCE RESPONSE RESTORE
93
+ RESTRICT RESULT RETURN RETURNING RETURNS REVERSE REVOKE RIGHT ROLE ROLES
94
+ ROLLBACK ROLLUP ROUTINE ROW ROWS RULE RULES SAMPLE SATISFIES SAVE SAVEPOINT
95
+ SCAN SCHEMA SCOPE SCROLL SEARCH SECOND SECTION SEGMENT SEGMENTS SELECT SELF
96
+ SEMI SENSITIVE SEPARATE SEQUENCE SERIALIZABLE SESSION SET SETS SHARD SHARE
97
+ SHARED SHORT SHOW SIGNAL SIMILAR SIZE SKEWED SMALLINT SNAPSHOT SOME SOURCE
98
+ SPACE SPACES SPARSE SPECIFIC SPECIFICTYPE SPLIT SQL SQLCODE SQLERROR
99
+ SQLEXCEPTION SQLSTATE SQLWARNING START STATE STATIC STATUS STORAGE STORE
100
+ STORED STREAM STRING STRUCT STYLE SUB SUBMULTISET SUBPARTITION SUBSTRING
101
+ SUBTYPE SUM SUPER SYMMETRIC SYNONYM SYSTEM TABLE TABLESAMPLE TEMP TEMPORARY
102
+ TERMINATED TEXT THAN THEN THROUGHPUT TIME TIMESTAMP TIMEZONE TINYINT TO
103
+ TOKEN TOTAL TOUCH TRAILING TRANSACTION TRANSFORM TRANSLATE TRANSLATION
104
+ TREAT TRIGGER TRIM TRUE TRUNCATE TTL TUPLE TYPE UNDER UNDO UNION UNIQUE UNIT
105
+ UNKNOWN UNLOGGED UNNEST UNPROCESSED UNSIGNED UNTIL UPDATE UPPER URL USAGE
106
+ USE USER USERS USING UUID VACUUM VALUE VALUED VALUES VARCHAR VARIABLE
107
+ VARIANCE VARINT VARYING VIEW VIEWS VIRTUAL VOID WAIT WHEN WHENEVER WHERE
108
+ WHILE WINDOW WITH WITHIN WITHOUT WORK WRAPPED WRITE YEAR ZONE
109
+ ]
110
+ ).freeze
111
+ # rubocop:enable Metrics/CollectionLiteralLength
74
112
 
75
- # Build an array of values for Condition
76
- # Is used in ScanFilter and QueryFilter
77
- # https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Condition.html
78
- # @param [String] operator value of RANGE_MAP or FIELD_MAP hash, e.g. "EQ", "LT" etc
79
- # @param [Object] value scalar value or array/set
80
- def self.attribute_value_list(operator, value)
81
- # For BETWEEN and IN operators we should keep value as is (it should be already an array)
82
- # NULL and NOT_NULL require absence of attribute list
83
- # For all the other operators we wrap the value with array
84
- # https://docs.aws.amazon.com/en_us/amazondynamodb/latest/developerguide/LegacyConditionalParameters.Conditions.html
85
- if %w[BETWEEN IN].include?(operator)
86
- [value].flatten
87
- elsif %w[NULL NOT_NULL].include?(operator)
88
- nil
89
- else
90
- [value]
91
- end
92
- end
113
+ attr_reader :table_cache
93
114
 
94
115
  # Establish the connection to DynamoDB.
95
116
  #
@@ -470,25 +491,32 @@ module Dynamoid
470
491
  # only really useful for range queries, since it can only find by one hash key at once. Only provide
471
492
  # one range key to the hash.
472
493
  #
494
+ # Dynamoid.adapter.query('users', { id: [[:eq, '1']], age: [[:between, [10, 30]]] }, { batch_size: 1000 })
495
+ #
473
496
  # @param [String] table_name the name of the table
474
- # @param [Hash] options the options to query the table with
475
- # @option options [String] :hash_value the value of the hash key to find
476
- # @option options [Number, Number] :range_between find the range key within this range
477
- # @option options [Number] :range_greater_than find range keys greater than this
478
- # @option options [Number] :range_less_than find range keys less than this
479
- # @option options [Number] :range_gte find range keys greater than or equal to this
480
- # @option options [Number] :range_lte find range keys less than or equal to this
497
+ # @param [Array[Array]] key_conditions conditions for the primary key attributes
498
+ # @param [Array[Array]] non_key_conditions (optional) conditions for non-primary key attributes
499
+ # @param [Hash] options (optional) the options to query the table with
500
+ # @option options [Boolean] :consistent_read You can set the ConsistentRead parameter to true and obtain a strongly consistent result
501
+ # @option options [Boolean] :scan_index_forward 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.
502
+ # @option options [Symbop] :select The attributes to be returned in the result (one of ALL_ATTRIBUTES, ALL_PROJECTED_ATTRIBUTES, ...)
503
+ # @option options [Symbol] :index_name The name of an index to query. This index can be any local secondary index or global secondary index on the table.
504
+ # @option options [Hash] :exclusive_start_key The primary key of the first item that this operation will evaluate.
505
+ # @option options [Integer] :batch_size The number of items to lazily load one by one
506
+ # @option options [Integer] :record_limit The maximum number of items to return (not necessarily the number of evaluated items)
507
+ # @option options [Integer] :scan_limit The maximum number of items to evaluate (not necessarily the number of matching items)
508
+ # @option options [Array[Symbol]] :project The attributes to retrieve from the table
481
509
  #
482
510
  # @return [Enumerable] matching items
483
511
  #
484
512
  # @since 1.0.0
485
513
  #
486
514
  # @todo Provide support for various other options http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#query-instance_method
487
- def query(table_name, options = {})
515
+ def query(table_name, key_conditions, non_key_conditions = {}, options = {})
488
516
  Enumerator.new do |yielder|
489
517
  table = describe_table(table_name)
490
518
 
491
- Query.new(client, table, options).call.each do |page|
519
+ Query.new(client, table, key_conditions, non_key_conditions, options).call.each do |page|
492
520
  yielder.yield(
493
521
  page.items.map { |item| item_to_hash(item) },
494
522
  last_evaluated_key: page.last_evaluated_key
@@ -497,11 +525,11 @@ module Dynamoid
497
525
  end
498
526
  end
499
527
 
500
- def query_count(table_name, options = {})
528
+ def query_count(table_name, key_conditions, non_key_conditions, options)
501
529
  table = describe_table(table_name)
502
530
  options[:select] = 'COUNT'
503
531
 
504
- Query.new(client, table, options).call
532
+ Query.new(client, table, key_conditions, non_key_conditions, options).call
505
533
  .map(&:count)
506
534
  .reduce(:+)
507
535
  end
@@ -558,7 +586,7 @@ module Dynamoid
558
586
  end
559
587
 
560
588
  def count(table_name)
561
- describe_table(table_name, true).item_count
589
+ describe_table(table_name, reload: true).item_count
562
590
  end
563
591
 
564
592
  # Run PartiQL query.
@@ -605,10 +633,7 @@ module Dynamoid
605
633
  conditions.delete(:unless_exists).try(:each) do |col|
606
634
  expected[col.to_s][:exists] = false
607
635
  end
608
- conditions.delete(:if_exists).try(:each) do |col, val|
609
- expected[col.to_s][:exists] = true
610
- expected[col.to_s][:value] = val
611
- end
636
+
612
637
  conditions.delete(:if).try(:each) do |col, val|
613
638
  expected[col.to_s][:value] = val
614
639
  end
@@ -619,7 +644,7 @@ module Dynamoid
619
644
  #
620
645
  # New, semi-arbitrary API to get data on the table
621
646
  #
622
- def describe_table(table_name, reload = false)
647
+ def describe_table(table_name, reload: false)
623
648
  (!reload && table_cache[table_name]) || begin
624
649
  table_cache[table_name] = Table.new(client.describe_table(table_name: table_name).data)
625
650
  end
@@ -39,14 +39,14 @@ module Dynamoid
39
39
  #
40
40
  # @since 0.2.0
41
41
  def target_association
42
- has_many_key_name = options[:inverse_of] || source.class.to_s.underscore.pluralize.to_sym
43
- has_one_key_name = options[:inverse_of] || source.class.to_s.underscore.to_sym
44
- unless target_class.associations[has_many_key_name].nil?
45
- return has_many_key_name if target_class.associations[has_many_key_name][:type] == :has_many
42
+ name = options[:inverse_of] || source.class.to_s.underscore.pluralize.to_sym
43
+ if target_class.associations.dig(name, :type) == :has_many
44
+ return name
46
45
  end
47
46
 
48
- unless target_class.associations[has_one_key_name].nil?
49
- return has_one_key_name if target_class.associations[has_one_key_name][:type] == :has_one
47
+ name = options[:inverse_of] || source.class.to_s.underscore.to_sym
48
+ if target_class.associations.dig(name, :type) == :has_one
49
+ return name # rubocop:disable Style/RedundantReturn
50
50
  end
51
51
  end
52
52
  end
@@ -265,7 +265,7 @@ module Dynamoid
265
265
  @associations[:"#{name}_ids"] ||= Dynamoid::Associations.const_get(type.to_s.camelcase).new(self, name, options)
266
266
  end
267
267
 
268
- define_method("#{name}=".to_sym) do |objects|
268
+ define_method(:"#{name}=") do |objects|
269
269
  @associations[:"#{name}_ids"] ||= Dynamoid::Associations.const_get(type.to_s.camelcase).new(self, name, options)
270
270
  @associations[:"#{name}_ids"].setter(objects)
271
271
  end
@@ -33,21 +33,21 @@ module Dynamoid
33
33
  defaults[name] = settings[name] = options[:default]
34
34
 
35
35
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
36
- def #{name}
37
- settings[#{name.inspect}]
38
- end
36
+ def #{name} # def endpoint
37
+ settings[#{name.inspect}] # settings["endpoint"]
38
+ end # end
39
39
 
40
- def #{name}=(value)
41
- settings[#{name.inspect}] = value
42
- end
40
+ def #{name}=(value) # def endpoint=(value)
41
+ settings[#{name.inspect}] = value # settings["endpoint"] = value
42
+ end # end
43
43
 
44
- def #{name}?
45
- #{name}
46
- end
44
+ def #{name}? # def endpoint?
45
+ #{name} # endpoint
46
+ end # end
47
47
 
48
- def reset_#{name}
49
- settings[#{name.inspect}] = defaults[#{name.inspect}]
50
- end
48
+ def reset_#{name} # def reset_endpoint
49
+ settings[#{name.inspect}] = defaults[#{name.inspect}] # settings["endpoint"] = defaults["endpoint"]
50
+ end # end
51
51
  RUBY
52
52
  end
53
53
 
@@ -59,6 +59,7 @@ module Dynamoid
59
59
  option :http_idle_timeout, default: nil # - default: 5
60
60
  option :http_open_timeout, default: nil # - default: 15
61
61
  option :http_read_timeout, default: nil # - default: 60
62
+ option :create_table_on_save, default: true
62
63
 
63
64
  # The default logger for Dynamoid: either the Rails logger or just stdout.
64
65
  #