activeitem 0.0.15 → 0.0.17

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2706d94d7f88c19fdb9fc14d8a9408d1f8dc088a5eabf2e583d60351173b9870
4
- data.tar.gz: 41c8e00d8b5a45ec26b58123efc86437795e1921c59dc8c44f3a770727496702
3
+ metadata.gz: 3a4b1c7cc7af49e67776f895886bdf472d1e801ab3280f3ea28101bd20b8a701
4
+ data.tar.gz: 7415931df8a26b187e4a9ab52586b67c7cea08f0fa3a476ab09b863c02250835
5
5
  SHA512:
6
- metadata.gz: 41689642db2d18371ae7f9c7987a72d001f3aa18d5e7bb3f0a7248100e5de2a8aa6cb039742057c176bfb6f9eeaa02a240847d50c64f6f02c54372da6c069020
7
- data.tar.gz: 5c1a35c85c0b6d641fd630681fccb4995637eb733c50eada9a197a1c2fd503cc62855c1ad14eb81e19ccf324e1da2941ab93cbf87b72e5116285b8b5e49ed7a8
6
+ metadata.gz: b8c97c1ffadb921d440702b5299cf4bb219ba97ad0351cef7bebfea1125e74bf027ddc0546e1a254da8d6da8952598bf170368404c6ab65074ecc8379bfaec3b
7
+ data.tar.gz: b9936169d15857e1423de8f622ddfe85b7be05458739f569ce1983dd6565f234ae2789f12978ac530106cc98203906a99b9666805d1e2e43c7e0a275e7c41d06
@@ -143,9 +143,13 @@ module ActiveItem
143
143
  "#{association_name.to_s.camelize}Index"
144
144
  end
145
145
 
146
+ # Store partition key in DynamoDB format (camelCase) for consistency
147
+ # with RECENT_INDEX and explicit index definitions
148
+ dynamo_key = to_dynamo_key(foreign_key)
149
+
146
150
  @_belongs_to_indexes ||= {}
147
151
  @_belongs_to_indexes = @_belongs_to_indexes.merge(
148
- index_name => { partition_key: foreign_key }
152
+ index_name => { partition_key: dynamo_key }
149
153
  )
150
154
  end
151
155
  end
@@ -559,6 +559,12 @@ module ActiveItem
559
559
  else
560
560
  paginated_scan_with_conditions(normalized_conditions, exclusive_start_key, per_page)
561
561
  end
562
+ rescue Aws::DynamoDB::Errors::ValidationException => e
563
+ raise unless e.message.include?('specified index')
564
+
565
+ warn "[ActiveItem] WARNING: Index not found on table '#{resolved_model.table_name}'. " \
566
+ 'Falling back to table scan. Create the index: belt setup tables && belt deploy'
567
+ paginated_scan_with_conditions(normalized_conditions, exclusive_start_key, per_page)
562
568
  rescue Aws::DynamoDB::Errors::AccessDeniedException => e
563
569
  raise ActiveItem::AccessDeniedError.new(model_name: resolved_model.name, table: resolved_model.table_name,
564
570
  operation: 'PaginatedQuery', original_error: e)
@@ -788,7 +794,7 @@ module ActiveItem
788
794
  effective_index = index_name || resolved_model.send(:detect_index_for_conditions, normalized_conditions)
789
795
 
790
796
  if effective_index && normalized_conditions.any?
791
- query_with_index_normalized(effective_index, normalized_conditions)
797
+ query_with_index_or_fallback(effective_index, normalized_conditions)
792
798
  else
793
799
  scan_with_conditions_normalized(normalized_conditions)
794
800
  end
@@ -838,6 +844,13 @@ module ActiveItem
838
844
  end
839
845
 
840
846
  limit_value ? [total, limit_value].min : total
847
+ rescue Aws::DynamoDB::Errors::ValidationException => e
848
+ raise unless e.message.include?('specified index')
849
+
850
+ warn "[ActiveItem] WARNING: Index not found on table '#{resolved_model.table_name}'. " \
851
+ 'Falling back to table scan for count. Create the index: belt setup tables && belt deploy'
852
+ # Retry without the index
853
+ count_via_scan(normalized_conditions)
841
854
  rescue Aws::DynamoDB::Errors::AccessDeniedException => e
842
855
  raise ActiveItem::AccessDeniedError.new(model_name: resolved_model.name, table: resolved_model.table_name,
843
856
  operation: 'Count', original_error: e)
@@ -911,6 +924,26 @@ module ActiveItem
911
924
  params
912
925
  end
913
926
 
927
+ # Fallback count via scan when an index is missing
928
+ def count_via_scan(normalized_conditions)
929
+ total = 0
930
+ exclusive_start_key = nil
931
+
932
+ loop do
933
+ params = build_count_scan_params(normalized_conditions)
934
+ params[:exclusive_start_key] = exclusive_start_key if exclusive_start_key
935
+ params[:limit] = limit_value if limit_value
936
+
937
+ response = resolved_model.dynamodb.scan(params)
938
+ total += response.count
939
+ exclusive_start_key = response.last_evaluated_key
940
+ break unless exclusive_start_key
941
+ break if limit_value && total >= limit_value
942
+ end
943
+
944
+ limit_value ? [total, limit_value].min : total
945
+ end
946
+
914
947
  # Build scan params for a count-only scan
915
948
  def build_count_scan_params(normalized_conditions)
916
949
  filter_parts = []
@@ -1082,6 +1115,18 @@ module ActiveItem
1082
1115
  foreign_key.to_s == attr_name ? nil : foreign_key.to_s
1083
1116
  end
1084
1117
 
1118
+ # Attempt a GSI query, falling back to a filtered scan if the index doesn't exist.
1119
+ # Logs a warning when falling back so the developer knows to create the index.
1120
+ def query_with_index_or_fallback(idx_name, normalized_conditions)
1121
+ query_with_index_normalized(idx_name, normalized_conditions)
1122
+ rescue Aws::DynamoDB::Errors::ValidationException => e
1123
+ raise unless e.message.include?('specified index')
1124
+
1125
+ warn "[ActiveItem] WARNING: Index '#{idx_name}' not found on table '#{resolved_model.table_name}'. " \
1126
+ 'Falling back to table scan. Create the index for better performance: belt setup tables && belt deploy'
1127
+ scan_with_conditions_normalized(normalized_conditions)
1128
+ end
1129
+
1085
1130
  def query_with_index_normalized(idx_name, normalized_conditions)
1086
1131
  # Get the partition key from conditions (Ruby snake_case)
1087
1132
  ruby_partition_key = normalized_conditions.keys.first.to_s
@@ -1090,6 +1135,7 @@ module ActiveItem
1090
1135
  # Get the actual DynamoDB partition key name from the index definition
1091
1136
  # The index definition stores the DynamoDB key name (which may be camelCase)
1092
1137
  index_config = resolved_model.indexes[idx_name] || {}
1138
+ # Always convert to DynamoDB key format (camelCase) for the expression
1093
1139
  dynamo_partition_key = index_config[:partition_key]&.to_s || resolved_model.to_dynamo_key(ruby_partition_key)
1094
1140
 
1095
1141
  return fanout_query(idx_name, normalized_conditions, index_config, dynamo_partition_key, partition_value) if partition_value.is_a?(Array)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveItem
4
- VERSION = '0.0.15'
4
+ VERSION = '0.0.17'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activeitem
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.15
4
+ version: 0.0.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Davis