Class: Lotus::Model::Adapters::Memory::Query

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Enumerable
Defined in:
lib/lotus/model/adapters/memory/query.rb

Overview

Query the in-memory database with a powerful API.

All the methods are chainable, it allows advanced composition of conditions.

This works as a lazy filtering mechanism: the records are fetched from the database only when needed.

It implements Ruby’s ‘Enumerable` and borrows some methods from `Array`. Expect a query to act like them.

Examples:


query.where(language: 'ruby')
     .and(framework: 'lotus')
     .reverse_order(:users_count).all

# the records are fetched only when we invoke #all

Since:

  • 0.1.0

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(dataset, collection, &blk) ⇒ Query

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Initialize a query

Parameters:

Since:

  • 0.1.0



56
57
58
59
60
61
62
# File 'lib/lotus/model/adapters/memory/query.rb', line 56

def initialize(dataset, collection, &blk)
  @dataset    = dataset
  @collection = collection
  @conditions = []
  @modifiers  = []
  instance_eval(&blk) if block_given?
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(m, *args, &blk) ⇒ Object (protected)

Since:

  • 0.1.0



545
546
547
548
549
550
551
# File 'lib/lotus/model/adapters/memory/query.rb', line 545

def method_missing(m, *args, &blk)
  if @context.respond_to?(m)
    apply @context.public_send(m, *args, &blk)
  else
    super
  end
end

Instance Attribute Details

#conditionsObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Since:

  • 0.1.0



39
40
41
# File 'lib/lotus/model/adapters/memory/query.rb', line 39

def conditions
  @conditions
end

#modifiersObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Since:

  • 0.1.0



45
46
47
# File 'lib/lotus/model/adapters/memory/query.rb', line 45

def modifiers
  @modifiers
end

Instance Method Details

#allArray

Resolves the query by fetching records from the database and translating them into entities.

Returns:

  • (Array)

    a collection of entities

Since:

  • 0.1.0



70
71
72
# File 'lib/lotus/model/adapters/memory/query.rb', line 70

def all
  @collection.deserialize(run)
end

#average(column) ⇒ Numeric Also known as: avg

Returns the average of the values for the given column.

Examples:


query.average(:comments_count)

Parameters:

  • column (Symbol)

    the column name

Returns:

  • (Numeric)

Since:

  • 0.1.0



429
430
431
432
433
# File 'lib/lotus/model/adapters/memory/query.rb', line 429

def average(column)
  if s = sum(column)
    s / _all_with_present_column(column).count.to_f
  end
end

#countFixnum

Returns a count of the records for the current conditions.

Examples:


query.where(author_id: 23).count # => 5

Returns:

  • (Fixnum)

Since:

  • 0.1.0



527
528
529
# File 'lib/lotus/model/adapters/memory/query.rb', line 527

def count
  run.count
end

#exclude(condition = nil, &blk) ⇒ Object Also known as: not

Logical negation of a #where condition.

It accepts a ‘Hash` with only one pair. The key must be the name of the column expressed as a `Symbol`. The value is the one used by the internal filtering logic.

Examples:

Fixed value


query.exclude(language: 'java')

Array


query.exclude(id: [4, 9])

Range


query.exclude(year: 1900..1982)

Multiple conditions


query.exclude(language: 'java')
     .exclude(company: 'enterprise')

Using block


query.exclude { age > 31 }

Multiple conditions with blocks


query.exclude { language == 'java' }
     .exclude { framework == 'spring' }

Mixed hash and block conditions


query.exclude(language: 'java')
     .exclude { framework == 'spring' }

Parameters:

  • condition (Hash) (defaults to: nil)

Returns:

  • self

Since:

  • 0.1.0



227
228
229
230
231
232
233
234
235
236
237
# File 'lib/lotus/model/adapters/memory/query.rb', line 227

def exclude(condition = nil, &blk)
  if blk
    _push_evaluated_block_condition(:where, blk, :reject)
  elsif condition
    _push_to_expanded_condition(:where, condition) do |column, value|
      Proc.new { reject { |r| r.fetch(column) == value} }
    end
  end

  self
end

#exist?TrueClass, FalseClass

Checks if at least one record exists for the current conditions.

Examples:


query.where(author_id: 23).exists? # => true

Returns:

  • (TrueClass, FalseClass)

Since:

  • 0.1.0



514
515
516
# File 'lib/lotus/model/adapters/memory/query.rb', line 514

def exist?
  !count.zero?
end

#interval(column) ⇒ Numeric

Returns the difference between the MAX and MIN for the given column.

Examples:


query.interval(:comments_count)

Parameters:

  • column (Symbol)

    the column name

Returns:

  • (Numeric)

See Also:

Since:

  • 0.1.0



481
482
483
484
# File 'lib/lotus/model/adapters/memory/query.rb', line 481

def interval(column)
  max(column) - min(column)
rescue NoMethodError
end

#limit(number) ⇒ Object

Limit the number of records to return.

Examples:


query.limit(1)

Parameters:

  • number (Fixnum)

Returns:

  • self

Since:

  • 0.1.0



372
373
374
375
# File 'lib/lotus/model/adapters/memory/query.rb', line 372

def limit(number)
  modifiers.push(Proc.new{ replace(flatten.first(number)) })
  self
end

#max(column) ⇒ Object

Returns the maximum value for the given column.

Examples:


query.max(:comments_count)

Parameters:

  • column (Symbol)

    the column name

Returns:

  • result

Since:

  • 0.1.0



448
449
450
# File 'lib/lotus/model/adapters/memory/query.rb', line 448

def max(column)
  _all_with_present_column(column).max
end

#min(column) ⇒ Object

Returns the minimum value for the given column.

Examples:


query.min(:comments_count)

Parameters:

  • column (Symbol)

    the column name

Returns:

  • result

Since:

  • 0.1.0



463
464
465
# File 'lib/lotus/model/adapters/memory/query.rb', line 463

def min(column)
  _all_with_present_column(column).min
end

#negate!Object

This method is defined in order to make the interface of ‘Memory::Query` identical to `Sql::Query`, but this feature is NOT implemented

Raises:

  • (NotImplementedError)

See Also:

Since:

  • 0.1.0



540
541
542
# File 'lib/lotus/model/adapters/memory/query.rb', line 540

def negate!
  raise NotImplementedError
end

#offset(number) ⇒ Object

Simulate an ‘OFFSET` clause, without the need of specify a limit.

Examples:


query.offset(10)

Parameters:

  • number (Fixnum)

Returns:

  • self

Since:

  • 0.1.0



388
389
390
391
# File 'lib/lotus/model/adapters/memory/query.rb', line 388

def offset(number)
  modifiers.unshift(Proc.new{ replace(flatten.drop(number)) })
  self
end

#or(condition = nil, &blk) ⇒ Object

Adds a condition that behaves like SQL ‘OR`.

It accepts a ‘Hash` with only one pair. The key must be the name of the column expressed as a `Symbol`. The value is the one used by the SQL query

This condition will be ignored if not used with WHERE.

Examples:

Fixed value


query.where(language: 'ruby').or(framework: 'lotus')

Array


query.where(id: 1).or(author_id: [15, 23])

Range


query.where(country: 'italy').or(year: 1900..1982)

Using block


query.where { age == 31 }.or { age == 32 }

Mixed hash and block conditions


query.where(language: 'ruby')
     .or { framework == 'lotus' }

Parameters:

  • condition (Hash) (defaults to: nil)

Returns:

  • self

Since:

  • 0.1.0



173
174
175
176
177
178
179
180
181
182
183
# File 'lib/lotus/model/adapters/memory/query.rb', line 173

def or(condition = nil, &blk)
  if blk
    _push_evaluated_block_condition(:or, blk, :find_all)
  elsif condition
    _push_to_expanded_condition(:or, condition) do |column, value|
      Proc.new { find_all { |r| r.fetch(column) == value} }
    end
  end

  self
end

#order(*columns) ⇒ Object Also known as: asc

Specify the ascending order of the records, sorted by the given columns.

Examples:

Single column


query.order(:name)

Multiple columns


query.order(:name, :year)

Multiple invokations


query.order(:name).order(:year)

Parameters:

  • columns (Array<Symbol>)

    the column names

Returns:

  • self

See Also:

Since:

  • 0.1.0



285
286
287
288
289
290
291
# File 'lib/lotus/model/adapters/memory/query.rb', line 285

def order(*columns)
  Lotus::Utils::Kernel.Array(columns).each do |column|
    modifiers.push(Proc.new{ sort_by!{|r| r.fetch(column)} })
  end

  self
end

#range(column) ⇒ Range

Returns a range of values between the MAX and the MIN for the given column.

Examples:


query.range(:comments_count)

Parameters:

  • column (Symbol)

    the column name

Returns:

  • (Range)

See Also:

Since:

  • 0.1.0



501
502
503
# File 'lib/lotus/model/adapters/memory/query.rb', line 501

def range(column)
  min(column)..max(column)
end

#reverse_order(*columns) ⇒ Object Also known as: desc

Specify the descending order of the records, sorted by the given columns.

Examples:

Single column


query.reverse_order(:name)

Multiple columns


query.reverse_order(:name, :year)

Multiple invokations


query.reverse_order(:name).reverse_order(:year)

Parameters:

  • columns (Array<Symbol>)

    the column names

Returns:

  • self

See Also:

Since:

  • 0.3.1



334
335
336
337
338
339
340
# File 'lib/lotus/model/adapters/memory/query.rb', line 334

def reverse_order(*columns)
  Lotus::Utils::Kernel.Array(columns).each do |column|
    modifiers.push(Proc.new{ sort_by!{|r| r.fetch(column)}.reverse! })
  end

  self
end

#select(*columns) ⇒ Object

Select only the specified columns.

By default a query selects all the mapped columns.

Examples:

Single column


query.select(:name)

Multiple columns


query.select(:name, :year)

Parameters:

  • columns (Array<Symbol>)

Returns:

  • self

Since:

  • 0.1.0



258
259
260
261
# File 'lib/lotus/model/adapters/memory/query.rb', line 258

def select(*columns)
  columns = Lotus::Utils::Kernel.Array(columns)
  modifiers.push(Proc.new{ flatten!; each {|r| r.delete_if {|k,_| !columns.include?(k)} } })
end

#sum(column) ⇒ Numeric

Returns the sum of the values for the given column.

Examples:


query.sum(:comments_count)

Parameters:

  • column (Symbol)

    the column name

Returns:

  • (Numeric)

Since:

  • 0.1.0



404
405
406
407
408
409
410
411
412
413
414
415
416
# File 'lib/lotus/model/adapters/memory/query.rb', line 404

def sum(column)
  result = all

  if result.any?
    result.inject(0.0) do |acc, record|
      if value = record.public_send(column)
        acc += value
      end

      acc
    end
  end
end

#where(condition = nil, &blk) ⇒ Object Also known as: and

Adds a condition that behaves like SQL ‘WHERE`.

It accepts a ‘Hash` with only one pair. The key must be the name of the column expressed as a `Symbol`. The value is the one used by the internal filtering logic.

Examples:

Fixed value


query.where(language: 'ruby')

Array


query.where(id: [1, 3])

Range


query.where(year: 1900..1982)

Using block


query.where { age > 31 }

Multiple conditions


query.where(language: 'ruby')
     .where(framework: 'lotus')

Multiple conditions with blocks


query.where { language == 'ruby' }
     .where { framework == 'lotus' }

Mixed hash and block conditions


query.where(language: 'ruby')
     .where { framework == 'lotus' }

Parameters:

  • condition (Hash) (defaults to: nil)

Returns:

  • self

Since:

  • 0.1.0



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/lotus/model/adapters/memory/query.rb', line 116

def where(condition = nil, &blk)
  if blk
    _push_evaluated_block_condition(:where, blk, :find_all)
  elsif condition
    _push_to_expanded_condition(:where, condition) do |column, value|
      Proc.new {
        find_all { |r|
          case value
          when Array,Set,Range
            value.include?(r.fetch(column, nil))
          else
            r.fetch(column, nil) == value
          end
        }
      }
    end
  end

  self
end