Rails 3: query con arel e l’algebra relazionale

16 febbraio 2010

Arel e l’algebra relazionale

Grandi novità per ActiveRecord 3.0: l’ORM abbandona la sua sintassi per evolversi verso una strutturazione ancora più flessibile ed intuitiva abbracciando Arel, una gemma che implementa il concetto di algebra relazionale. Ma, cos’è l’algebra relazionale?

Il nome ‘algebra’ ci riporta ad operatori che tutti conosciamo ed utilizziamo quotidianamente: parlo dei classici simboli +, -, /, etc. che applicati al sistema dei numeri reali concorrono a definire la cosiddetta algebra elementare. Il termine algebra relazionale indica che in questo caso gli operatori insistono non sull’insieme R ma su quello delle relazioni; quindi l’algebra relazionale descrive una query come una successione di operazioni sull’insieme delle relazioni.

Prendiamo ad esempio il semplicissimo frammento SQL:

SELECT * FROM pals 
LEFT JOIN countries on countries.id = pals.country_id 
WHERE pals.age > 10

la sua controparte in algebra relazionale è la seguente:

Query in forma algebrica

Implementando questo meccanismo Arel rende di fatto possibile concatenare in modo intuitivo e trasparente un indefinito numero di operatori (selezione, join, ordinamento, etc.) sobbarcandosi il costo computazionale della loro traduzione in SQL.

Per sperimentare questa funzionalità creiamo gli oggetti Pal e Country in showcase_3_0 approfittando dell’occasione anche per notare i piccoli cambiamenti nella sintassi del comandi di generazione:

rails generate model Pal name:string country_id:integer 
rails generate model Country name:string 

Modifichiamo ora il file db/seeds.rb per specificare alcuni valori di default della tabella countries inserendo:

countries.create([{:name => 'Italy'},{:name => 'France'},{:name => 'Spain'}])

Definiamo la relazione (1, n) tra countries e pals, agendo sui rispettivi file nella cartella app/models come segue:

file country.rb

class Country < ActiveRecord::Base
  has_many :pals
end

file pal.rb

class Pal < ActiveRecord::Base
  belongs_to :country
end

Ora lanciamo i soliti comandi per la creazione ed il popolamento del database:

rake db:create && rake db:migrate && rake db:seed 

A questo punto possiamo invocare la console e sperimentare un po' le possibilità offerte dalla nuove API di ActiveRecord:

rails console  # per lanciare la console

>> Pal.create[{:name=>'Sandro',:country_id=>1},{:name=>'Francesca',:country_id=>1},{:name=>'Alberto',:country_id=>2}]
=> [#<Pal id: 2, name: "Sandro", country_id: 1, created_at:...
>> Pal.where(:country_id=>1)
=> #<ActiveRecord::Relation:0x1032350d8 @scope_for... 
>> Pal.where(:country_id=>1).all
=> [#<Pal id: 2, name: "Sandro", country_id: 1,...
>> Pal.where(:country_id=>1).to_sql
=> "SELECT     \"pals\".* FROM       \"pals\" WHERE     (\"pals\".\"country_id\" = 1)"
>> Pal.joins(:country).where(:countries=>{:name=>'Italy'}).order(:name).all
=> [#<Pal id: 3, name: "Francesca", country_id: 1, created_at: ...

Il meccanismo di generazione delle query, beneficiando del supporto di Arel, consente di concatenare fra di loro un numero indefinito di operazioni; inoltre finché su questa catena non viene invocato un metodo di iterazione (es: each) o di collezione (es: all) l'oggetto di ritorno è sempre di tipo ActiveRecord::Relation e quindi passibile di nuove concatenazioni.

Infine è possibile in ogni momento conoscere la forma dell'SQL risultante invocando sulla catena il metodo to_sql.

Con questi piccoli esperimenti abbiamo esplorato una piccola porzione dei benefici di questa nuova sintassi che nei prossimi mesi renderà interrogare le basi di dati ancora più semplice e flessibile; per chi stesse effettuando un porting di una applicazione 2.x ricordo infine che il vecchio approccio è ancora utilizzabile anche se 'deprecated'.

Se vuoi aggiornamenti su Rails 3: query con arel e l'algebra relazionale inserisci la tua e-mail nel box qui sotto:
 
X
Se vuoi aggiornamenti su Rails 3: query con arel e l'algebra relazionale

inserisci la tua e-mail nel box qui sotto:

Ho letto e acconsento l'informativa sulla privacy

Acconsento al trattamento di cui al punto 3 dell'informativa sulla privacy