Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # Oleg Andreev <oleganza@gmail.com>
- # November 3, 2008
- # (code is awful, but it works)
- #
- # The idea is to avoid composite SQL queries when association collection is already here,
- # but to use composite SQL query when collection is not here to load less data:
- #
- # # When no data available:
- #
- # person.articles.first(:coauthor_id => 2)
- # # => SELECT * FROM articles WHERE ((person_id = 1) AND (coauthor_id = 2)) LIMIT 1
- #
- # # When all collection is loaded already: (e.g. by `person.articles.to_a`)
- #
- # person.articles.first(:coauthor_id => 2)
- # # => person.articles.detect{|a| a.coauthor_id == 2 } # no sql request!
- #
- #
- module DataMapper
- module Associations
- module OneToMany
- class Proxy
- def first(*args)
- if args.last.respond_to?(:merge)
- query = args.pop
- # Patch start.
- if c = @children
- Query.filter_first(c, query)
- else # Patch end.
- @relationship.get_children(@parent, query, :first, *args)
- end
- else
- super
- end
- end # first
- def all(query = {})
- if query.empty?
- self
- else
- # Patch start.
- if c = @children
- Query.filter_all(c, query)
- else # Patch end.
- @relationship.get_children(@parent, query)
- end
- end
- end # all
- end # Proxy
- end # OneToMany
- end # Associations
- class Query
- class <<self
- def filter_first(collection, hash)
- hash = hash.dup
- collection = _prefilter_collection(collection, hash)
- collection.detect do |r|
- filter_record(r, hash)
- end
- end
- def filter_all(collection, hash)
- hash = hash.dup
- collection = _prefilter_collection(collection, hash)
- collection.select do |r|
- filter_record(r, hash)
- end
- end
- def filter_record(r, hash)
- hash.each do |k, v|
- return false unless k.__native_apply__(r, v)
- end
- end
- # TODO: :limit, :offset
- def _prefilter_collection(collection, hash)
- if order = hash.delete(:order)
- order = [ order ] unless Array === order
- collection = collection.sort do |a, b|
- __order_tuple__(a, b, order)
- end
- end
- collection
- end
- def __order_tuple__(a, b, order)
- order.each do |operator|
- r = operator.__native_order__(a,b)
- return r unless r == 0
- end
- return 0
- end
- end # Query eigenclass
- class Operator
- def __native_apply__(r, v)
- left = r.send(@target)
- case @operator
- when :gt: left > v
- when :gte: left >= v
- when :lt: left < v
- when :lte: left <= v
- when :not: left != v
- when :eql: left == v
- when :like: left =~ /^#{v.gsub(/%/,'.*')}$/u
- when :in: v.include?(left)
- end
- end
- def __native_order__(a,b)
- a = a.send(@target)
- b = b.send(@target)
- r = (a <=> b)
- r *= -1 if @operator == :desc
- r
- end
- end
- class ::Symbol
- def __native_apply__(r, v)
- r.send(self) == v
- end
- def __native_order__(a,b) # ascending
- a.__send__(self) <=> b.__send__(self)
- end
- end
- end # Query
- end # DM
Add Comment
Please, Sign In to add comment