dm-adapter-simpledb 1.2.0 → 1.3.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.
@@ -1,3 +1,20 @@
1
+ == 1.3.0 2010-01-19
2
+
3
+ * 1 major enhancement:
4
+ * Now supports :offset in queries. Combined with improved :limit support, this
5
+ makes it possible to efficiently paginate result sets.
6
+
7
+ * 1 minor enhancement:
8
+ * Better quoting of select statements using the 'sdbtools' library.
9
+
10
+ == 1.2.0 2010-01-13
11
+
12
+ * 2 minor enhancements:
13
+ * Better support for String attributes with multiple values which were not
14
+ created by the String chunking system.
15
+ * Now tries to take model and repository into account when determining storage
16
+ name for a table.
17
+
1
18
  == 1.1.0 2009-11-24
2
19
 
3
20
  * 2 major enhancements:
data/README CHANGED
@@ -16,6 +16,7 @@ Features:
16
16
  * Array properties
17
17
  * Basic aggregation support (Model.count("..."))
18
18
  * String "chunking" permits attributes to exceed the 1024-byte limit
19
+ * Support for efficient :limit and :offset, for result set paging
19
20
 
20
21
  Note: as of version 1.0.0, this gem supports supports the DataMapper 0.10.*
21
22
  series and breaks backwards compatibility with DataMapper 0.9.*.
@@ -39,6 +40,7 @@ http://github.com/devver/dm-adapter-simpledb/
39
40
  == TODO
40
41
 
41
42
  * More complete handling of NOT conditions in queries
43
+ * Support for ORs, parens, and nested queries in general
42
44
  * Robust quoting in SELECT calls
43
45
  * Handle exclusive ranges natively
44
46
  Implement as inclusive range + filter step
@@ -48,15 +50,6 @@ http://github.com/devver/dm-adapter-simpledb/
48
50
  - Store floats using exponential notation
49
51
  * Option to store Date/Time/DateTime as ISO8601
50
52
  * Full aggregate support (min/max/etc)
51
- * Offset support
52
- Note, from the SimpleDB documentation:
53
-
54
- "The next token returned by count(*) and select are interchangeable as long
55
- as the where and order by clauses match. For example, if you want to return
56
- the 200 items after the first 10,000 (similar to an offset), you can perform
57
- a count with a limit clause of 10,000 and use the next token to return the
58
- next 200 items with select."
59
-
60
53
  * Option to use libxml if available
61
54
  * Parallelized queries for increased throughput
62
55
  * Support of normalized 1:1 table:domain schemes that works with associations
@@ -64,6 +57,7 @@ http://github.com/devver/dm-adapter-simpledb/
64
57
  * Support BatchPutAttributes
65
58
  * Silence SSL warnings
66
59
  See http://pivotallabs.com/users/carl/blog/articles/1079-standup-blog-11-24-2009-model-validations-without-backing-store-associations-to-array-and-ssl-with-aws
60
+ * Token cache for reduced requests when given an offset
67
61
 
68
62
  == Usage
69
63
 
data/Rakefile CHANGED
@@ -81,7 +81,7 @@ END
81
81
  gem.add_dependency('dm-migrations', '~> 0.10.0')
82
82
  gem.add_dependency('dm-types', '~> 0.10.0')
83
83
  gem.add_dependency('uuidtools', '~> 2.0')
84
- gem.add_dependency('right_aws', '~> 1.10')
84
+ gem.add_dependency('sdbtools', '~> 0.2')
85
85
  end
86
86
  Jeweler::GemcutterTasks.new
87
87
  rescue LoadError
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.2.0
1
+ 1.3.0
@@ -8,6 +8,7 @@ require 'dm-aggregates'
8
8
  require 'digest/sha1'
9
9
  require 'right_aws'
10
10
  require 'uuidtools'
11
+ require 'sdbtools'
11
12
 
12
13
  require 'dm-adapter-simpledb/sdb_array'
13
14
  require 'dm-adapter-simpledb/utils'
@@ -92,7 +92,16 @@ module DataMapper
92
92
  query.conditions.operands.reject!{ |op|
93
93
  !unsupported_conditions.include?(op)
94
94
  }
95
- records = query.filter_records(proto_resources)
95
+
96
+ # This used to be a simple call to Query#filter_records(), but that
97
+ # caused the result limit to be re-imposed on an already limited result
98
+ # set, with the upshot that too few records were returned. So here we do
99
+ # everything filter_records() does EXCEPT limiting.
100
+ records = proto_resources
101
+ records = records.uniq if query.unique?
102
+ records = query.match_records(records)
103
+ records = query.sort_records(records)
104
+
96
105
 
97
106
  records
98
107
  end
@@ -123,7 +132,10 @@ module DataMapper
123
132
  end
124
133
 
125
134
  def query(query_call, query_limit = 999999999)
126
- select(query_call, query_limit).collect{|x| x.values[0]}
135
+ SDBTools::Operation.new(sdb, :select, query_call).inject([]){
136
+ |a, results|
137
+ a.concat(results[:items].map{|i| i.values.first})
138
+ }[0...query_limit]
127
139
  end
128
140
 
129
141
  def aggregate(query)
@@ -147,7 +159,7 @@ module DataMapper
147
159
  token = :none
148
160
  begin
149
161
  results = sdb.get_attributes(domain, '__dm_consistency_token', '__dm_consistency_token')
150
- tokens = results[:attributes]['__dm_consistency_token']
162
+ tokens = Array(results[:attributes]['__dm_consistency_token'])
151
163
  end until tokens.include?(@current_consistency_token)
152
164
  end
153
165
 
@@ -227,43 +239,59 @@ module DataMapper
227
239
  else
228
240
  raise ArgumentError, "Unsupported inclusion op: #{op.value.inspect}"
229
241
  end
242
+ when :or
243
+ # TODO There's no reason not to support OR
244
+ unsupported_conditions << op
230
245
  else raise "Invalid query op: #{op.inspect}"
231
246
  end
232
247
  end
233
248
  [conditions,order,unsupported_conditions]
234
249
  end
235
250
 
236
- def select(query_call, query_limit)
237
- items = []
238
- time = Benchmark.realtime do
239
- sdb_continuation_key = nil
240
- while (results = sdb.select(query_call, sdb_continuation_key)) do
241
- sdb_continuation_key = results[:next_token]
242
- items += results[:items]
243
- break if items.length > query_limit
244
- break if sdb_continuation_key.nil?
245
- end
246
- end; DataMapper.logger.debug(format_log_entry(query_call, time))
247
- items[0...query_limit]
248
- end
249
-
250
251
  #gets all results or proper number of results depending on the :limit
251
252
  def get_results(query, conditions, order)
252
253
  fields_to_request = query.fields.map{|f| f.field}
253
254
  fields_to_request << DmAdapterSimpledb::Record::METADATA_KEY
254
- output_list = fields_to_request.join(', ')
255
- query_call = "SELECT #{output_list} FROM #{domain} "
256
- query_call << "WHERE #{conditions.compact.join(' AND ')}" if conditions.length > 0
257
- query_call << " #{order}"
258
- if query.limit!=nil
259
- query_limit = query.limit
260
- query_call << " LIMIT #{query.limit}"
255
+
256
+ selection = SDBTools::Selection.new(
257
+ sdb,
258
+ domain,
259
+ :attributes => fields_to_request)
260
+
261
+ if query.order && query.order.length > 0
262
+ query_object = query.order[0]
263
+ #anything sorted on must be a condition for SDB
264
+ conditions << "#{query_object.target.name} IS NOT NULL"
265
+ selection.order_by = query_object.target.name
266
+ selection.order = case query_object.operator
267
+ when :asc then :ascending
268
+ when :desc then :descending
269
+ else raise "Unrecognized sort direction"
270
+ end
271
+ end
272
+ selection.conditions = conditions.compact.inject([]){|conds, cond|
273
+ conds << "AND" unless conds.empty?
274
+ conds << cond
275
+ }
276
+ if query.limit.nil?
277
+ selection.limit = :none
261
278
  else
262
- #on large items force the max limit
263
- query_limit = 999999999 #TODO hack for query.limit being nil
264
- #query_call << " limit 2500" #this doesn't work with continuation keys as it halts at the limit passed not just a limit per query.
279
+ selection.limit = query.limit
280
+ end
281
+ unless query.offset.nil?
282
+ selection.offset = query.offset
265
283
  end
266
- records = select(query_call, query_limit)
284
+
285
+ items = []
286
+ time = Benchmark.realtime do
287
+ # TODO update Record to be created from name/attributes pair
288
+ selection.each do |name, value|
289
+ items << {name => value}
290
+ end
291
+ end
292
+ DataMapper.logger.debug(format_log_entry(selection.to_s, time))
293
+
294
+ items
267
295
  end
268
296
 
269
297
  # Creates an item name for a query
@@ -105,6 +105,12 @@ describe 'with multiple records saved' do
105
105
  results = Hero.all(:limit => 110)
106
106
  results.should have(110).entries
107
107
  end
108
+
109
+ it "should be able to page through results" do
110
+ results1 = Hero.all(:limit => 10, :order => [:id.asc])
111
+ results2 = Hero.all(:offset => 9, :limit => 10, :order => [:id.asc])
112
+ results1.to_a.last.name.should be == results2.to_a.first.name
113
+ end
108
114
  end
109
115
 
110
116
  end
@@ -74,7 +74,20 @@ describe DataMapper::Adapters::SimpleDBAdapter do
74
74
  ])
75
75
  @record = Product.first
76
76
  end
77
-
78
77
  end
79
78
 
79
+ # it "should be able to request items with an offset" do
80
+ # @sdb.should_receive(:select).
81
+ # with(/count(\*).*LIMIT 10000/, anything).
82
+ # exactly(1).times.
83
+ # ordered.
84
+ # and_return(:next_token => "TOKEN")
85
+ # @sdb.should_receive(:select).
86
+ # with(anything, "TOKEN").
87
+ # exactly(1).times.
88
+ # ordered.
89
+ # and_return(:items => [])
90
+ # @record = Product.all(:offset => 10000, :limit => 10)
91
+ # end
92
+
80
93
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dm-adapter-simpledb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Boles
@@ -13,7 +13,7 @@ autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
15
 
16
- date: 2010-01-13 00:00:00 -05:00
16
+ date: 2010-01-19 00:00:00 -05:00
17
17
  default_executable:
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
@@ -67,14 +67,14 @@ dependencies:
67
67
  version: "2.0"
68
68
  version:
69
69
  - !ruby/object:Gem::Dependency
70
- name: right_aws
70
+ name: sdbtools
71
71
  type: :runtime
72
72
  version_requirement:
73
73
  version_requirements: !ruby/object:Gem::Requirement
74
74
  requirements:
75
75
  - - ~>
76
76
  - !ruby/object:Gem::Version
77
- version: "1.10"
77
+ version: "0.2"
78
78
  version:
79
79
  description: |
80
80
  A DataMapper adapter for Amazon's SimpleDB service.