Class: DataMapper::Adapters::AppEngineAdapter::QueryBuilder

Inherits:
Object
  • Object
show all
Includes:
Query::Conditions, Datastore::Query::Constants
Defined in:
lib/appengine_adapter.rb

Constant Summary collapse

@@OPERATORS =
{
EqualToComparison => EQUAL,
GreaterThanComparison => GREATER_THAN,
GreaterThanOrEqualToComparison => GREATER_THAN_OR_EQUAL,
LessThanComparison => LESS_THAN,
LessThanOrEqualToComparison => LESS_THAN_OR_EQUAL,
}.freeze
@@NEGATED_OPERATORS =
{
  EqualToComparison => NOT_EQUAL,
  GreaterThanComparison => LESS_THAN_OR_EQUAL,
  GreaterThanOrEqualToComparison => LESS_THAN,
  LessThanComparison => GREATER_THAN_OR_EQUAL,
  LessThanOrEqualToComparison => GREATER_THAN,
}.freeze

Instance Method Summary collapse

Constructor Details

#initialize(query, kind, adapter) ⇒ QueryBuilder

Returns a new instance of QueryBuilder.

Raises:

  • (NotImplementedError)


169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/appengine_adapter.rb', line 169

def initialize(query, kind, adapter)
  @model = query.model
  @kind = kind
  @limit = query.limit
  @offset = query.offset
  @maybe_get = true
  @must_be_get = false
  @keys = []
  @dm_query = query
  @adapter = adapter

  @query = Datastore::Query.new(kind)
  parse_order(query.order)
  parse_conditions(query.conditions)
  raise NotImplementedError if @must_be_get && !@maybe_get
end

Instance Method Details

#entity_to_hash(key_prop, entity) ⇒ Object



403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
# File 'lib/appengine_adapter.rb', line 403

def entity_to_hash(key_prop, entity)
  # TODO: This is broken. We should be setting all properties
  return if entity.nil?
  key = entity.get_key
  hash = {}
  @dm_query.fields.each do |property|
    name = property.field
    if property.key?
      if property.serial?
        hash[name] = key.get_id
      elsif property.type == String
        hash[name] = key.get_name
      else
        hash[name] = key
      end
    else
      hash[name] = property.typecast(entity.get_property(name))
    end
  end
  hash
end

#get_entitiesObject



373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
# File 'lib/appengine_adapter.rb', line 373

def get_entities
  if is_get?
    Datastore.get(@keys)
  else
    begin
      chunk_size = FetchOptions::DEFAULT_CHUNK_SIZE
      options = FetchOptions::Builder.with_chunk_size(
          chunk_size)
      options.limit(@limit) if @limit
      options.offset(@offset) if @offset
      @query.iterator(options).collect {|e| e}
    rescue java.lang.IllegalArgumentException => ex
      raise ArgumentError, ex.message
    end
  end
end

#is_get?Boolean

Returns:

  • (Boolean)


369
370
371
# File 'lib/appengine_adapter.rb', line 369

def is_get?
  @maybe_get && @keys.size > 0
end

#keysObject



425
426
427
# File 'lib/appengine_adapter.rb', line 425

def keys
  @keys
end

#parse_and(op) ⇒ Object



282
283
284
285
286
287
288
289
290
# File 'lib/appengine_adapter.rb', line 282

def parse_and(op)
  if @maybe_get && (@found_and || op.operands.size > 1)
    @maybe_get = false
  end
  @found_and = true
  op.each do |conditions|
    parse_conditions(conditions)
  end
end

#parse_comparison(op) ⇒ Object



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/appengine_adapter.rb', line 292

def parse_comparison(op)
  if op.respond_to?(:relationship?) && op.relationship?
    return parse_conditions(op.foreign_key_mapping)
  elsif (respond_to?(:foreign_key_conditions) &&
         op.subject.kind_of?(Associations::Relationship))
    return parse_conditions(foreign_key_conditions(op))
  end
  property = op.subject
  value = op.value
  negated = op.negated?
  if @maybe_get
    if property.key? && !negated
      case op
      when EqualToComparison 
        @keys << parse_key(property, value)
      when InclusionComparison
        parse_key_inclusion(op)
        @must_be_get = true
        return
      else
        @maybe_get = false
      end
    else
      @maybe_get = false
    end
  end

  if op.kind_of?(InclusionComparison)
    parse_inclusion(op)
  elsif property.type == Types::AncestorKey
    @query.ancestor = value
  else
    if negated
      filter_op = @@NEGATED_OPERATORS[op.class]
    else
      filter_op = @@OPERATORS[op.class]
    end
    if filter_op.nil?
      raise ArgumentError, "#{op.class} is not a supported comparison"
    end
    name = property_name(op.subject)
    value = property_value(op.subject, op.value)
    @query.filter(name, filter_op, value)
  end
end

#parse_conditions(conditions) ⇒ Object



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/appengine_adapter.rb', line 225

def parse_conditions(conditions)
  case conditions
  when NullOperation then
    return
  when NotOperation then
    if OrOperation === conditions.operand
      parse_and(conditions.operand)
    elsif AbstractComparison === conditions.operand
      parse_comparison(conditions.operand)
    else
      raise NotImplementedError, "NOT operator is not supported with #{conditions.operand.class.name}"
    end
  when AbstractComparison then
    parse_comparison(conditions)
  when OrOperation  then
    parse_or(conditions)
  when AndOperation then
    parse_and(conditions)
  else
    raise ArgumentError, "invalid conditions #{conditions.class}: #{conditions.inspect}"
  end
end

#parse_inclusion(op) ⇒ Object



338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/appengine_adapter.rb', line 338

def parse_inclusion(op)
  if Range === op.value
    parse_range(op)
  else
    name = property_name(op.subject)
    values = op.value.map {|x| property_value(op.subject, x)}
    if op.negated?
      values.each do |value|
        @query.filter(name, NOT_EQUAL, value)
      end
    else
      @query.filter(name, IN, java.util.ArrayList.new(values))
    end
  end
end

#parse_key(property, value) ⇒ Object



248
249
250
251
252
253
# File 'lib/appengine_adapter.rb', line 248

def parse_key(property, value)
  unless property.key?
    raise ArgumentError, "#{property_name(property)} is not the key"
  end
  Types::Key.typecast(value, property)
end

#parse_key_inclusion(op) ⇒ Object

Raises:

  • (NotImplementedError)


275
276
277
278
279
280
# File 'lib/appengine_adapter.rb', line 275

def parse_key_inclusion(op)
  raise NotImplementedError unless op.value.kind_of? Array
  op.value.each do |value|
    @keys << parse_key(op.subject, value)
  end
end

#parse_or(or_op) ⇒ Object



255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/appengine_adapter.rb', line 255

def parse_or(or_op)
  if !@maybe_get
    raise NotImplementedError, "OR only supported with key equality comparisons"
  end
  @must_be_get = true
  or_op.each do |op|
    case op
      when OrOperation  then
        parse_or(op)
      when EqualToComparison then
        key = parse_key(op.subject, op.value)
        @keys << key
      when InclusionComparison then
        parse_key_inclusion(op)
      else
        raise NotImplementedError, "Unsupported condition #{op.class} inside OR"
    end
  end
end

#parse_order(order) ⇒ Object



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/appengine_adapter.rb', line 202

def parse_order(order)
  if order.size == 1 && order[0].operator != :desc
    if order[0].target.key?
      # omit the default key ordering.
      # This lets inequality filters work
      return
    end
  end
  order.map do |order|
    if order.operator == :desc
      direction = DESCENDING
    else
      direction = ASCENDING
    end
    name = if order.target.key?
      '__key__'
    else
      property_name(order.target)
    end
    @query.sort(name, direction)
  end
end

#parse_range(op) ⇒ Object

Raises:

  • (NotImplementedError)


354
355
356
357
358
359
360
361
362
363
364
365
366
367
# File 'lib/appengine_adapter.rb', line 354

def parse_range(op)
  range = op.value
  raise NotImplementedError unless range.is_a? Range
  raise NotImplementedError if op.negated?
  name = property_name(op.subject)
  begin_op = GREATER_THAN_OR_EQUAL
  end_op = if range.exclude_end?
    LESS_THAN
  else
    LESS_THAN_OR_EQUAL
  end
  @query.filter(name, begin_op, range.begin)
  @query.filter(name, end_op, range.end)
end

#property_name(property) ⇒ Object



186
187
188
189
190
191
192
# File 'lib/appengine_adapter.rb', line 186

def property_name(property)
  if property.key?
    '__key__'
  else
    property.field
  end
end

#property_value(property, value) ⇒ Object



194
195
196
197
198
199
200
# File 'lib/appengine_adapter.rb', line 194

def property_value(property, value)
  if property.key?
    parse_key(property, value)
  else
    @adapter.convert_value(property, value)
  end
end

#runObject



390
391
392
393
394
395
396
397
398
399
400
401
# File 'lib/appengine_adapter.rb', line 390

def run
  key_prop = @model.key(@adapter.name).first.field
  entities = get_entities
  hashes = entities.map do |entity|
    entity_to_hash(key_prop, entity)
  end
  resources = @model.load(hashes, @dm_query)
  resources.zip(entities) do |resource, entity|
    resource.instance_variable_set :@__entity__, entity
  end
  resources
end