aliased tables. firm: { "people[users]": {type: "User"}, "people[old]": {"age>:": 50 } # Every ToDo which has a User as Person or people older than 50. It is not an OR, it is an AND, but {people:{type:"User","age>":50}} or {"people":{type:"User"},"people":{"age>:":50} means every User older than 50. "users" and "old" are independet questions and both must be present, if firm should be true.

This commit is contained in:
Denis Knauf 2011-11-21 10:06:46 +01:00
parent 8eebb3ac1f
commit 69aceef180
3 changed files with 70 additions and 19 deletions

View file

@ -107,9 +107,9 @@ class SmqlToAR
# Model der Relation `rel` von `model` # Model der Relation `rel` von `model`
def self.model_of model, rel def self.model_of model, rel
rel = rel.to_sym p model_of: rel.to_sym, model: model
r = model.reflections[ rel].andand.klass r = model.reflections[ rel.to_sym].andand.klass
r.nil? && :self == rel ? model : r r.nil? && rel === :self ? model : r
end end
# Eine Spalte in einer Tabelle, relativ zu `Column#model`. # Eine Spalte in einer Tabelle, relativ zu `Column#model`.
@ -120,10 +120,43 @@ class SmqlToAR
attr_reader :path, :col attr_reader :path, :col
attr_accessor :model attr_accessor :model
class Col
attr_accessor :col, :as
def initialize col, as = nil
if col.kind_of? Col
@col, @as = col.col, col.as
elsif /^(.*?)\[(.*)\]$/ =~ col.to_s
@col, @as = $1, $2
else
@col, @as = col.to_s, as.to_s
@as = nil if @as.blank?
end
end
def to_s() @col end
def to_sym() to_s.to_sym end
def inspect() "#{@col}[@#{@as}]" end
def to_alias() "#{@col}[#{@as}]" end
def == other
other = Col.new other
@col = other.col && @as == other.col
end
def === other
other = Col.new other
@col == other.col
end
end
def initialize model, *col def initialize model, *col
@model = model @model = model
@last_model = nil @last_model = nil
*@path, @col = *Array.wrap( col).collect( &it.to_s.split( /[.\/]/)).flatten.collect( &:to_sym).reject( &it==:self) *@path, @col = *Array.wrap( col).
collect( &it.to_s.split( /[.\/]/)).
flatten.
collect( &Col.method( :new)).
reject( &it===:self)
end end
def last_model def last_model
@ -132,9 +165,10 @@ class SmqlToAR
def each def each
model = @model model = @model
p each: self, path: @path
@path.each do |rel| @path.each do |rel|
rel = rel.to_sym p rel: rel
unless :self == rel unless rel === :self
model = SmqlToAR.model_of model, rel model = SmqlToAR.model_of model, rel
return false unless model return false unless model
yield rel, model yield rel, model
@ -169,6 +203,7 @@ class SmqlToAR
exe.call pp, model exe.call pp, model
end end
end end
def self?() !@col end def self?() !@col end
def length() @path.length+(self.self? ? 0 : 1) end def length() @path.length+(self.self? ? 0 : 1) end
def size() @path.size+(self.self? ? 0 : 1) end def size() @path.size+(self.self? ? 0 : 1) end

View file

@ -56,9 +56,11 @@ class SmqlToAR
r = nil r = nil
#p :try_parse => { :model => model, :colop => colop, :value => val } #p :try_parse => { :model => model, :colop => colop, :value => val }
conditions.each do |c| conditions.each do |c|
raise_unless colop =~ /^(?:\d*:)?(.*?)(\W*)$/, UnexpectedColOpError.new( model, colop, val) raise_unless colop =~ /^(?:\d*:)?(.*?)((?:\W*(?!\])\W)?)$/, UnexpectedColOpError.new( model, colop, val)
col, op = $1, $2 col, op = $1, $2
p col: col, op: op
col = split_keys( col).collect {|c| Column.new model, c } col = split_keys( col).collect {|c| Column.new model, c }
p col: col
r = c.try_parse model, col, op, val r = c.try_parse model, col, op, val
break if r break if r
end end
@ -246,6 +248,7 @@ class SmqlToAR
model, *sub = sub model, *sub = sub
t = table + col.path + [col.col] t = table + col.path + [col.col]
col.joins.each {|j, m| builder.joins table+j, m } col.joins.each {|j, m| builder.joins table+j, m }
p build_t: t
builder.joins t, model builder.joins t, model
b2 = 1 == sub.length ? builder : Or.new( builder) b2 = 1 == sub.length ? builder : Or.new( builder)
sub.each {|i| i.collect( &it.build( And.new( b2), t)); p 'or' => b2 } sub.each {|i| i.collect( &it.build( And.new( b2), t)); p 'or' => b2 }

View file

@ -28,20 +28,27 @@ class SmqlToAR
def to_i() @vid end def to_i() @vid end
end end
class Aliases < Hash
def self.new prefix, *a, &e
e ||= lambda do |h, k|
j = Array.wrap( k).compact
h[k] = h.key?(j) ? h[j] : "#{prefix},#{j.collect( &:to_alias).join( ',')}"
end
super *a, &e
end
end
attr_reader :table_alias, :model, :table_model, :base_table, :_where, :_select, :_wobs, :_joins, :prefix, :_vid attr_reader :table_alias, :model, :table_model, :base_table, :_where, :_select, :_wobs, :_joins, :prefix, :_vid
attr_accessor :logger, :limit, :offset attr_accessor :logger, :limit, :offset
def initialize model, prefix = nil def initialize model, prefix = nil
@prefix = "smql" @prefix = "smql"
@logger = SmqlToAR.logger @logger = SmqlToAR.logger
@table_alias = Hash.new do |h, k| @table_alias = Aliases.new @prefix
j = Array.wrap( k).compact
h[k] = h.key?(j) ? h[j] : "#{@prefix},#{j.join(',')}"
end
@_vid, @_where, @_wobs, @model, @quoter = 0, SmqlToAR::And[], {}, model, model.connection @_vid, @_where, @_wobs, @model, @quoter = 0, SmqlToAR::And[], {}, model, model.connection
@base_table = [model.table_name.to_sym] @base_table = [Column::Col.new( model.table_name)]
@table_alias[ @base_table] = @base_table.first @table_alias[ @base_table] = @base_table.first
t = quote_table_name @table_alias[ @base_table] t = quote_table_name @base_table.first.col
@_select, @_joins, @_joined, @_includes, @_order = ["DISTINCT #{t}.*"], "", [@base_table], [], [] @_select, @_joins, @_joined, @_includes, @_order = ["DISTINCT #{t}.*"], "", [@base_table], [], []
@table_model = {@base_table => @model} @table_model = {@base_table => @model}
end end
@ -49,7 +56,7 @@ class SmqlToAR
def vid() Vid.new( @_vid+=1) end def vid() Vid.new( @_vid+=1) end
def inspect def inspect
"#<#{self.class.name}:#{"0x%x"% (self.object_id<<1)}|#{@prefix}:#{@base_table}:#{@model} vid=#{@_vid} where=#{@_where} wobs=#{@_wobs} select=#{@_select} aliases=#{@_table_alias}>" "#<#{self.class.name}:#{"0x%x"% (self.object_id<<1)}|#{@prefix}:#{@base_table}:#{@model} vid=#{@_vid} where=#{@_where} wobs=#{@_wobs} select=#{@_select} aliases=#{@table_alias}>"
end end
# Jede via where uebergebene Condition wird geodert und alle zusammen werden geundet. # Jede via where uebergebene Condition wird geodert und alle zusammen werden geundet.
@ -71,14 +78,19 @@ class SmqlToAR
end end
def quote_table_name name def quote_table_name name
name = case name
when Array, Column::Col then @table_alias[Array.wrap name]
else name.to_s
end
@quoter.quote_table_name( name).gsub /"\."/, ',' @quoter.quote_table_name( name).gsub /"\."/, ','
end end
def column table, name def column table, name
"#{quote_table_name table.kind_of?(String) ? table : @table_alias[table]}.#{quote_column_name name}" "#{quote_table_name table}.#{quote_column_name name}"
end end
def build_join orig, pretable, table, prekey, key def build_join orig, pretable, table, prekey, key
p build_join: {orig: orig, pretable: pretable, table: table, prekey: prekey, key: key}, alias: @table_alias
" LEFT JOIN #{orig} AS #{quote_table_name table} ON #{column pretable, prekey} = #{column table, key} " " LEFT JOIN #{orig} AS #{quote_table_name table} ON #{column pretable, prekey} = #{column table, key} "
end end
@ -92,13 +104,14 @@ class SmqlToAR
@table_model[ table] = model @table_model[ table] = model
premodel = @table_model[ pretable] premodel = @table_model[ pretable]
t = @table_alias[ table] t = @table_alias[ table]
pt = quote_table_name @table_alias[ table[ 0...-1]] pt = quote_table_name table[ 0...-1]
refl = premodel.reflections[table.last] p table: table
refl = premodel.reflections[table.last.to_sym]
case refl case refl
when ActiveRecord::Reflection::ThroughReflection when ActiveRecord::Reflection::ThroughReflection
through = refl.through_reflection through = refl.through_reflection
throughtable = table[0...-1]+[through.name.to_sym] throughtable = table[0...-1]+[Column::Col.new( through.name, table.last.as)]
srctable = throughtable+[refl.source_reflection.name] srctable = throughtable+[Column::Col.new( refl.source_reflection.name, table.last.as)]
@table_model[ srctable] = model @table_model[ srctable] = model
@table_alias[ table] = @table_alias[ srctable] @table_alias[ table] = @table_alias[ srctable]
join_ throughtable, through.klass, quote_table_name( through.table_name) join_ throughtable, through.klass, quote_table_name( through.table_name)