Skip to content

Commit

Permalink
Added a "property chain" to conditions of a query.
Browse files Browse the repository at this point in the history
This is not the the creation of DM::Query::Path. It is the ability
to specify a path as follows:

  Zoo.all('Zoo.animals.name' => 'Monkey')

The key must be given as a string in dot (.) notation. Any table
that is not linked or included in through :links and :includes option
is automatically added to the :links option.

With this ability, do we need DM::Query::Path ??
  • Loading branch information
Guy van den Berg committed Apr 7, 2008
1 parent 63365f8 commit 9423911
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 27 deletions.
43 changes: 23 additions & 20 deletions lib/data_mapper/adapters/data_objects_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ def query_read_statement(query)
child_keys = relationship.child_key.to_a

# We only do LEFT OUTER JOIN for now
s = 'LEFT OUTER JOIN '
s = ' LEFT OUTER JOIN '
s << parent_model_name << ' ON '
parts = []
relationship.parent_key.zip(child_keys) do |parent_key,child_key|
Expand All @@ -326,15 +326,18 @@ def query_read_statement(query)

unless query.conditions.empty?
sql << " WHERE "
sql << "(" << query.conditions.map do |operator, property, value|
sql << "(" << query.conditions.map do |operator, property, value|
# deriving the model name from the property and not the query
# allows for "foreign" properties to be qualified correctly
model_name = property.target.resource_name(property.target.repository.name)
case operator
when :eql, :in then equality_operator(query,operator, property, qualify, value)
when :not then inequality_operator(query,operator, property, qualify, value)
when :like then "#{property_to_column_name(query.model_name, property, qualify)} LIKE ?"
when :gt then "#{property_to_column_name(query.model_name, property, qualify)} > ?"
when :gte then "#{property_to_column_name(query.model_name, property, qualify)} >= ?"
when :lt then "#{property_to_column_name(query.model_name, property, qualify)} < ?"
when :lte then "#{property_to_column_name(query.model_name, property, qualify)} <= ?"
when :eql, :in then equality_operator(query,model_name,operator, property, qualify, value)
when :not then inequality_operator(query,model_name,operator, property, qualify, value)
when :like then "#{property_to_column_name(model_name, property, qualify)} LIKE ?"
when :gt then "#{property_to_column_name(model_name, property, qualify)} > ?"
when :gte then "#{property_to_column_name(model_name, property, qualify)} >= ?"
when :lt then "#{property_to_column_name(model_name, property, qualify)} < ?"
when :lte then "#{property_to_column_name(model_name, property, qualify)} <= ?"
else raise "CAN HAS CRASH?"
end
end.join(') AND (') << ")"
Expand All @@ -351,29 +354,29 @@ def query_read_statement(query)

sql << " LIMIT #{query.limit}" if query.limit
sql << " OFFSET #{query.offset}" if query.offset && query.offset > 0

sql
end

def equality_operator(query, operator, property, qualify, value)
def equality_operator(query, model_name, operator, property, qualify, value)
case value
when Array then "#{property_to_column_name(query.model_name, property, qualify)} IN ?"
when NilClass then "#{property_to_column_name(query.model_name, property, qualify)} IS NULL"
when Array then "#{property_to_column_name(model_name, property, qualify)} IN ?"
when NilClass then "#{property_to_column_name(model_name, property, qualify)} IS NULL"
when DataMapper::Query then
query.merge_sub_select_conditions(operator, property, value)
"#{property_to_column_name(query.model_name, property, qualify)} IN (#{query_read_statement(value)})"
else "#{property_to_column_name(query.model_name, property, qualify)} = ?"
"#{property_to_column_name(model_name, property, qualify)} IN (#{query_read_statement(value)})"
else "#{property_to_column_name(model_name, property, qualify)} = ?"
end
end

def inequality_operator(query, operator, property, qualify, value)
def inequality_operator(query, model_name, operator, property, qualify, value)
case value
when Array then "#{property_to_column_name(query.model_name, property, qualify)} NOT IN ?"
when NilClass then "#{property_to_column_name(query.model_name, property, qualify)} IS NOT NULL"
when Array then "#{property_to_column_name(model_name, property, qualify)} NOT IN ?"
when NilClass then "#{property_to_column_name(model_name, property, qualify)} IS NOT NULL"
when DataMapper::Query then
query.merge_sub_select_conditions(operator, property, value)
"#{property_to_column_name(query.model_name, property, qualify)} NOT IN (#{query_read_statement(value)})"
else "#{property_to_column_name(query.model_name, property, qualify)} <> ?"
"#{property_to_column_name(model_name, property, qualify)} NOT IN (#{query_read_statement(value)})"
else "#{property_to_column_name(model_name, property, qualify)} <> ?"
end
end

Expand Down
36 changes: 33 additions & 3 deletions lib/data_mapper/query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def merge_sub_select_conditions(operator, property, value)

def initialize(model, options = {})
validate_model(model)
validate_options(options)
validate_options(options)

@repository = model.repository
repository_name = @repository.name
Expand Down Expand Up @@ -289,17 +289,47 @@ def normalize_includes
# TODO: normalize Array of Symbol, String, DM::Property 1-jump-away or DM::Query::Path
end

def normalize_property_chain(property)
# DM::Query.new(Zoo, 'Zoo.displays.name' => 'foo')

relationships = []
model = @model
result = nil
property.to_s.split('.').map do |part|
next if DataMapper::Inflection.classify(part) == model.to_s

if model.properties(model.repository.name)[part] != nil
result = model.properties(model.repository.name)[part]
elsif model.relationships.has_key?(part.to_sym)
relationship = model.relationships[part.to_sym]
model = relationship.child_model == model ? relationship.parent_model : relationship.child_model
relationships << relationship
else
raise ArgumentError, "Could not normalize property chain for #{property.inspect}"
end
end

# Add joins if not already joined
relationships.map do |relationship|
@links << relationship if !@links.include?(relationship) && !@includes.include?(relationship)
end

result
end

def append_condition(property, value)
operator = :eql

property = case property
when DataMapper::Property
property
when Operator
operator = property.type
@properties[property.to_sym]
when Symbol, String
@properties[property]
prop = @properties[property]
prop = normalize_property_chain(property) unless prop
prop
else
raise ArgumentError, "Condition type #{property.inspect} not supported"
end
Expand Down
17 changes: 13 additions & 4 deletions spec/integration/query_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,6 @@ class Vehicle
Region.new(:id=>1,:name=>'North West').save
Factory.new(:id=>2000,:region_id=>1,:name=>'North West Plant').save
Vehicle.new(:id=>1,:factory_id=>2000,:name=>'10 ton delivery truck').save

#Teacher.new(:id_a => 1, :id_b => 2, :name => 'Math Prof').save
#Student.new(:id => 1, :parent_id_a => 1, :parent_id_b => 2,:name => 'Joey').save
end
end

Expand Down Expand Up @@ -264,8 +261,20 @@ class Vehicle
DataMapper::Query.new(Vehicle,:links=>[:sailing])
}.should raise_error(ArgumentError)
end


it 'should accept a property chain as the key to a condition' do
repository(:sqlite3) do
vehicle = Vehicle.first('Vehicle.factory.region.name' => 'North West')
vehicle.name.should == '10 ton delivery truck'
end

end

it 'should auto generate the link if a DM::Property from a different resource is in the :fields option'

# it 'should take properties of associations in the conditions clause' do
# query = DataMapper::Query.new(Vehicle, 'Vehicle.factory.region.name' => 'foo')
# end

it 'should create links with composite keys'

Expand Down

0 comments on commit 9423911

Please sign in to comment.