Finding Records

The finder methods for DataMapper objects are defined in DataMapper::Repository. They include get(), all(), first()

Finder Methods

DataMapper has methods which allow you to grab a single record by key, the first match to a set of conditions, or a collection of records matching conditions.

1 zoo   = Zoo.get(1)                        # get the zoo with primary key of 1.
2 zoo   = Zoo.get!(1)                       # Or get! if you want an ObjectNotFoundError on failure
3 zoo   = Zoo.get('DFW')                    # wow, support for natural primary keys
4 zoo   = Zoo.get('Metro', 'DFW')           # more wow, composite key look-up
5 zoo   = Zoo.first(:name => 'Luke')        # first matching record with the name 'Luke'
6 zoos  = Zoo.all                           # all zoos
7 zoos  = Zoo.all(:open => true)            # all zoos that are open
8 zoos  = Zoo.all(:opened_on => (s..e))     # all zoos that opened on a date in the date-range

Scopes and Chaining

A call to all() or first() can be chained together to further build a query to the data-store:

1 all_zoos = Zoo.all
2 open_zoos = all_zoos.all(:open => true)
3 big_open_zoos = open_zoos.all(:animal_count => 1000)

As a direct consequence, you can define scopes without any extra work in your model.

 1 class Zoo
 2   # all the keys and property setup here
 3   def self.open
 4     all(:open => true)
 5   end
 6   def self.big
 7     all(:animal_count => 1000)
 8   end
 9 end
10 
11 big_open_zoos = Zoo.big.open

Scopes like this can even have arguments. Do anything in them, just ensure they return a Query of some kind.

Conditions

Rather than defining conditions using SQL fragments, we can actually specify conditions using a hash.

The examples above are pretty simple, but you might be wondering how we can specify conditions beyond equality without resorting to SQL. Well, thanks to some clever additions to the Symbol class, it’s easy!

1 exhibitions = Exhibition.all(:run_time.gt => 2, :run_time.lt => 5)
2 # => SQL conditions: 'run_time > 1 AND run_time < 5'

Valid symbol operators for the conditions are:

1 gt    # greater than
2 lt    # less than
3 gte   # greater than or equal
4 lte   # less than or equal
5 not   # not equal
6 eql   # equal
7 like  # like
8 in    # in - will be used automatically when an array is passed in as an argument

Order

To specify the order in which your results are to be sorted, use:

1 @zoos_by_tiger_count = Zoo.all(:order => [ :tiger_count.desc ])
2 # in SQL =>  select * from zoos ORDER BY tiger_count DESC

Available order vectors are:

1 asc  # sorting ascending
2 desc # sorting descending

Once you have the query, the order can be modified too. Just call reverse:

1 @least_tigers_first = @zoos_by_tiger_count.reverse
2 # in SQL =>  select * from zoos ORDER BY tiger_count ASC

Combining Queries

Sometimes, the simple queries DataMapper allows you to specify with the hash interface to all() just won’t cut it. This might be because you want to specify an OR condition, though that’s just one possibility. To accomplish more complex queries, DataMapper allows queries (or more accurately, Collections) to be combined using set operators.

 1 # Find all Zoos in Illinois, or those with five or more tigers
 2 Zoo.all(:state => 'IL') + Zoo.all(:tiger_count.gte => 5)
 3 # in SQL => SELECT * FROM "zoos" WHERE ("state" = 'IL' OR "tiger_count" >= 5)
 4 
 5 # It also works with the union operator
 6 Zoo.all(:state => 'IL') | Zoo.all(:tiger_count.gte => 5)
 7 # in SQL => SELECT * FROM "zoos" WHERE ("state" = 'IL' OR "tiger_count" >= 5)
 8 
 9 # Intersection produces an AND query
10 Zoo.all(:state => 'IL') & Zoo.all(:tiger_count.gte => 5)
11 # in SQL => SELECT * FROM "zoos" WHERE ("state" = 'IL' AND "tiger_count" >= 5)
12 
13 # Subtraction produces a NOT query
14 Zoo.all(:state => 'IL') - Zoo.all(:tiger_count.gte => 5)
15 # in SQL => SELECT * FROM "zoos" WHERE ("state" = 'IL' AND NOT("tiger_count" >= 5))

Of course, the latter two queries could be achieved using the standard symbol operators. Set operators work on any Collection though, and so Zoo.all(:state => 'IL') could just as easily be replaced with Zoo.open.big or any other method which returns a collection.

Compatibility

DataMapper supports other conditions syntaxes as well:

1 zoos = Zoo.all(:conditions => { :id => 34 })
2 
3 # You can use this syntax to call native storage engine functions
4 zoos = Zoo.all(:conditions => [ "id = ?", 34 ])
5 
6 # even mix and match
7 zoos = Zoo.all(:conditions => { :id => 34 }, :name.like => '%foo%')

Talking directly to your data-store

Sometimes you may find that you need to tweak a query manually.

1 zoos = repository(:default).adapter.select('SELECT name, open FROM zoos WHERE open = 1')
2 #      Note that this will not return Zoo objects, rather the raw data straight from the database

zoos will be full of Struct objects with name, and open attributes, rather than instances of the Zoo class. They’ll also be read-only. You can still use the interpolated array condition syntax as well:

1 zoos = repository(:default).adapter.select('SELECT name, open FROM zoos WHERE name = ?', "Awesome Zoo")

Counting

With DM-More’s DM-Aggregates included, the count method it adds will returns an integer of the number of records matching the every condition you pass in.

1 count = Zoo.count(:age.gt => 200) #=> 2