select és pluck két különböző és hasznos módja annak, hogy egy adatbázis-tábla egy vagy több oszlopának értékeit gyűjteményként adjuk vissza. Képzeljük el például, hogy van egy questions tábla az adatbázisban. Minden question-nek három mezője lehet: id, question_text és egy foreign_key egy kapcsolódó táblából. Ha csak a question_text oszlop értékeit szeretné visszaadni, akkor a select vagy a pluck értéket használná. Szeretném megvizsgálni a két módszer közötti különbségeket, és a pluck egy Rails-alkalmazáson belüli alkalmazásával fejezem be.

Select

select két egyedi módon működik:

i.select egy blokkot vesz fel, és ugyanúgy működik, mint a Array#select.

Model.all.select { |m| m.field == value }

Ez a Ruby objektumok egy tömbjét építi fel az adatbázisból a hatókör számára, tömbtá alakítja őket, és a Array#select segítségével iterálja őket.

select vs where

A blokk átadása a select-ba egy nagy adathalmazzal (pl. sok sor) lassabb, mint más stratégiák használata, mint például a where:

Model.where(field: value)

mert a Model.all.select { |m| m.field == value } esetén a Rails először az összes táblázat sort Ruby objektumokká alakítja, majd lefuttatja az adott blokkot minden egyes Ruby objektumhoz, míg a Model.where(field: value) egy SQL utasítást generál, amely a nehéz munkát az SQL-re hárítja, ami gyorsabb, mint a Ruby az ilyen típusú szűrésnél.

ii. A select úgy módosítja az SQL-lekérdezés SELECT utasítását, hogy csak bizonyos mezőket kérjen le:

Model.select(:field)
# =>

Egy saját projektemből például:

Question.select(:question_text)
# => #<ActiveRecord::Relation >

Ez a következő SQL-t generálja:

SELECT "questions"."question_text" FROM "questions"

Megjegyezzük, hogy ebben a második példában a select módszer nem egy tömböt ad vissza, hanem egy ActiveRecord::Relation objektumot. Ennek eredményeképpen más ActiveRecord::QueryMethodokat láncolhat hozzá. Például:

Question.select(:question_text).limit(5)# => #<ActiveRecord::Relation >

Ez a következő SQL-t generálja:

SELECT "questions"."question_text" FROM "questions" LIMIT ? ]

Pluck

A Rails útmutatókból:

pluck egy vagy több oszlop lekérdezésére használható egy modell mögöttes táblájából. Érvként egy oszlopnevekből álló listát fogad el, és a megadott oszlopok értékeinek egy tömbjét adja vissza a megfelelő adattípussal.

A fontos pont itt az, hogy a pluck egy tömböt ad vissza. Nem egy ActiveRecord::Relation objektumot ad vissza, mint a select. Tehát például:

Question.pluck(:question_text)# => # this returns an array with all of the values from the question_text column from the questions table.

Mert mivel a pluck egy adatbázis-lekérdezés eredményét egy Ruby-tömbté alakítja át anélkül, hogy egy ActiveRecord objektumot építene fel, ez nagyobb teljesítményű, mint a select.

Az azonban, hogy a pluck egy tömböt ad vissza, azt jelenti, hogy a pluck-re nem lehet szabványos ActiveRecord lekérdezéseket láncolni. Ennek megtétele egy NoMethodError:

Question.pluck(:question_text).limit(5)# => NoMethodError: undefined method `limit' for #<Array:0x007fc5c1dfb3b0>

Viszont a pluck működik ActiveRecord objektumokon, így a következő a várt eredményt fogja visszaadni:

Question.limit(5).pluck(:question_text)#=> 

pluck több argumentumot is elfogadhat egy többdimenziós tömb felépítéséhez:

Question.pluck(:question_text, :course_id)#=> , , ...]

Mikor használnám a Pluck-ot ?

Az pluck segítségével ActiveRecord modellobjektumokat határoltunk le, hogy meghatározzuk, mely modellobjektumokhoz tartoznak rekordok egy másik táblázatban.

Vegyük például a következő osztályt:

class Appointment < ActiveRecord::Base
has_many :chargesend

Ha meg akarjuk határozni, hogy melyik appointments rendelkezik charges és melyik nem, akkor használhatjuk a pluck-t egy scope-ban. Íme, hogyan:

Először is, ne feledjük, hogy a Model.where(field: value) esetében a value lehet egyetlen objektum (mint integer, string stb.), vagy lehet egy array objektumokból álló array.

Az alábbiak tehát érvényesek, és az összes appointments ids-t tartalmazó appointments-t visszaadják, amelyek a tömbben szerepelnek.

Appointment.where(id: )# => returns all appointments with ids that are included in the array

Hát, tudjuk, hogy a pluck egy tömböt ad vissza, így létrehozhatunk egy olyan tömböt, amely tartalmazza azokat az idegen kulcsokat, amelyeket a kapcsolódó modellben hatókörbe akarunk vonni. Például, ha meg akarjuk határozni, hogy melyik appointments-hoz tartozik charges, akkor létrehozhatunk egy tömböt a charges tábla appointment_id oszlopából, így:

Charge.pluck(:appointment_id)# => returns an array with all of the appointment_ids from the charges table, like: 

Hogy meghatározzuk, melyik appointments-hoz tartoznak díjak, azt írhatjuk:

Appointment.where(id: Charge.pluck(:appointment_id))# this is the same as Appointment.where(id: )

És ezt átalakítva a Appointment modell hatókörévé:

class Appointment < ActiveRecord::Base
has_many :charges scope :received_payment_info, -> {
where(id: Charge.pluck(:appointment_id)) }end

Így most, amikor meg akarjuk határozni, hogy melyik appointments-hoz van társítva charges, írhatjuk:

Appointment.where(id: Charge.pluck(:appointment_id))# this is the same as Appointment.where(id: )

Azt, hogy melyik appointments-hoz van társítva charges, leírhatjuk:

Appointment.received_payment_info#=> #<ActiveRecord::Relation 

amely a következő SQL-lekérdezéseket generálja:

SELECT "charges"."appointment_id" FROM "charges"SELECT "appointments".* FROM "appointments" WHERE ("appointments"."id" IN (35, 44, 82) OR "appointments"."id" IS NULL)

pluck lehetővé tette számunkra:

  1. hatékonyan lekérdezni egy kapcsolódó adatbázis táblát, hogy…
  2. generáljunk egy tömböt a kapcsolódó táblából az elsődleges tábla objektumainak összes idegen_kulcsával
  3. felhasználjuk ezt a tömböt annak meghatározására, hogy az elsődleges tábla mely objektumai rendelkeznek kapcsolódó objektumokkal a másodlagos táblában

Remélem, hasznosnak találtad ezt. Jó szórakozást a szedegetéshez és a kiválasztáshoz.

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.