ActiveRecord ընդդեմ Ecto- ի երկրորդ մաս

Սա «ActiveRecord vs. Ecto» շարքի երկրորդ մասն է, որում Batman- ը և Batgirl- ը պայքարում են տվյալների շտեմարանների որոնման շուրջ, և մենք համեմատում ենք խնձորն ու նարինջը:

ActiveRecord- ի և Ecto- ի առաջին մասի տվյալների շտեմարանների սխեմաները ուսումնասիրելուց հետո այս գրառումը ներառում է, թե ինչպես ActiveRecord- ը, այնպես էլ Ecto- ն ծրագրավորողներին հնարավորություն են տալիս հարցնել տվյալների բազան, և թե ինչպես ActiveRecord- ը և Ecto- ն համեմատում են նույն պահանջների հետ: Անապարհին մենք նաև կպարզենք Բատգերի 1989–2011 թվականների ինքնությունը:

Սերմերի տվյալներ

Եկեք սկսենք! Այս շարքի առաջին գրառմամբ սահմանված տվյալների բազայի կառուցվածքի հիման վրա ենթադրենք, որ օգտագործողները և հաշիվ ապրանքագրերի աղյուսակներն ունեն դրանցում պահվող հետևյալ տվյալները.

օգտվողներ

* ActiveRecord- ի ստեղծած_ատիպ դաշտը, որպես կանխադրված, անվանված է inserted_at- ը Ecto- ում:

հաշիվ-ապրանքագրեր

* ActiveRecord- ի ստեղծած_ատիպ դաշտը, որպես կանխադրված, անվանված է inserted_at- ը Ecto- ում:

Այս գրառման միջոցով կատարված հարցումները ենթադրում են, որ վերը նշված տվյալները պահվում են տվյալների բազայում, այնպես որ այն կարդալիս հիշեք այս տեղեկատվությանը:

Գտեք իրը ​​`օգտագործելով իր հիմնական բանալին

Սկսենք տվյալների բազայից գրառում ստանալով ՝ օգտագործելով դրա հիմնական բանալին:

ActiveRecord

irb (հիմնական): 001: 0> User.find (1) Օգտագործողի բեռ (0.4 մմ) ԸՆՏՐԵԼ «օգտվողներ»: * «օգտվողները» -ից «Ո՞ւր են» օգտվողները »:« id »= $ 1 LIMIT $ 2 [[" id ", 1 ], ["LIMIT", 1]] => # 

Էկտո

iex (3)> Repo.get (օգտվող ՝ 1)
[կարգաբերում] QUERY OK աղբյուր = "օգտվողները" db = 5.2ms վերծանում = 2.5ms հերթ = 0.1ms
ԸՆՏՐԵԼ u0. "Id", u0. "Full_name", u0. "Email", u0. "Inserted_at", u0. "Updated_at" FROM "օգտվողները" AS u0 WHERE (u0. "Id" = $ 1) [1]
Financex.Accounts.User
  __meta__: # Ecto.Schema.Metadata <: բեռնված, "օգտվողներ">,
  էլ. փոստ ՝ "[email protected]",
  full_name ՝ «Բեթ Քեյն»,
  id: 1,
  inserted_at: ~ N [2018-01-01 10: 01: 00.000000],
  հաշիվ-ապրանքագրեր. # Ecto.Association.NotLoaded <ասոցիացիա. հաշիվ-ապրանքագրերը բեռնված չեն>,
  updated_at: ~ N [2018-01-01 10: 01: 00.000000]
}

Համեմատություն

Երկու դեպքն էլ բավականին նման են: ActiveRecord- ը հենվում է User Model դասի գտած դասի մեթոդի վրա: Դա նշանակում է, որ յուրաքանչյուր ActiveRecord երեխաների դաս ունի իր մեջ գտնելու իր մեթոդը:

Ecto- ն օգտագործում է այլ մոտեցում ՝ ապավինելով Repository հայեցակարգին ՝ որպես միջնորդ ՝ քարտեզագրման շերտի և տիրույթի միջև: Ecto- ն օգտագործելիս Օգտագործողի մոդուլը գիտելիք չունի այն մասին, թե ինչպես գտնել իրեն: Նման պատասխանատվությունը առկա է «Ռեպո» մոդուլում, որն ի վիճակի է այն քարտեզագրելու տուփի տակ, որը մեր դեպքում «Պոստգրես» է:

SQL հարցումը ինքնին համեմատելիս մենք կարող ենք նկատել մի քանի տարբերություններ.

  • ActiveRecord- ը բեռնում է բոլոր դաշտերը (օգտվողները. *), Մինչդեռ Ecto- ն բեռնում է միայն սխեմաների սահմանման մեջ նշված դաշտերը:
  • ActiveRecord- ը հարցման մեջ պարունակում է LIMIT 1, մինչդեռ Ecto- ը `ոչ:

Բոլոր իրերը հավաքելը

Եկեք մի քայլ էլ գնանք և բեռնելու ենք բոլոր օգտվողներին տվյալների բազայից:

ActiveRecord

irb (հիմնական): 001: 0> User.all օգտվողի բեռ (0,5 մետր) ԸՆՏՐԵԼ «օգտվողներ»: * «օգտվողներից» LIMIT $ 1 [["" LIMIT ", 11]] => # , # , # , # ]>

Էկտո

iex (4)> Repo.all (օգտվող)
[կարգաբերում] QUERY OK աղբյուր = "օգտվողները" db = 2.8ms վերծանում = 0.2ms հերթ = 0.2ms
ԸՆՏՐԵԼ u0. "Id", u0. "Full_name", u0. "Email", u0. "Inserted_at", u0. "Updated_at" FROM "օգտվողները" AS u0 []
[
  Financex.Accounts.User
    __meta__: # Ecto.Schema.Metadata <: բեռնված, "օգտվողներ">,
    էլ. փոստ ՝ "[email protected]",
    full_name ՝ «Բեթ Քեյն»,
    id: 1,
    inserted_at: ~ N [2018-01-01 10: 01: 00.000000],
    հաշիվ-ապրանքագրեր. # Ecto.Association.NotLoaded <ասոցիացիա. հաշիվ-ապրանքագրերը բեռնված չեն>,
    updated_at: ~ N [2018-01-01 10: 01: 00.000000]
  ,
  Financex.Accounts.User
    __meta__: # Ecto.Schema.Metadata <: բեռնված, "օգտվողներ">,
    էլ. փոստ ՝ "[email protected]",
    full_name ՝ «Բարբարա Գորդոն»,
    id: 2,
    inserted_at: ~ N [2018-01-02 10: 02: 00.000000],
    հաշիվ-ապրանքագրեր. # Ecto.Association.NotLoaded <ասոցիացիա. հաշիվ-ապրանքագրերը բեռնված չեն>,
    updated_at: ~ N [2018-01-02 10: 02: 00.000000]
  ,
  Financex.Accounts.User
    __meta__: # Ecto.Schema.Metadata <: բեռնված, "օգտվողներ">,
    էլ. փոստ ՝ "[email protected]",
    full_name ՝ «Կասանդրա Կայեն»,
    id: 3,
    inserted_at: ~ N [2018-01-03 10: 03: 00.000000],
    հաշիվ-ապրանքագրեր. # Ecto.Association.NotLoaded <ասոցիացիա. հաշիվ-ապրանքագրերը բեռնված չեն>,
    updated_at: ~ N [2018-01-03 10: 03: 00.000000]
  ,
  Financex.Accounts.User
    __meta__: # Ecto.Schema.Metadata <: բեռնված, "օգտվողներ">,
    էլ. փոստ ՝ "[email protected]",
    full_name ՝ «Ստեֆանի Բրաուն»,
    id: 4,
    inserted_at: ~ N [2018-01-04 10: 04: 00.000000],
    հաշիվ-ապրանքագրեր. # Ecto.Association.NotLoaded <ասոցիացիա. հաշիվ-ապրանքագրերը բեռնված չեն>,
    updated_at: ~ N [2018-01-04 10: 04: 00.000000]
  }
]

Համեմատություն

Այն հետևում է ճիշտ նույն օրինակին, ինչպես նախորդ բաժինը: ActiveRecord- ը օգտագործում է բոլոր դասի մեթոդը, և Ecto- ն ապավինում է պահեստի օրինակին `գրառումները բեռնելու համար:

SQL հարցումների մեջ կրկին կան որոշ տարբերություններ.

Հարցումը պայմաններով

Շատ քիչ հավանական է, որ մեզ անհրաժեշտ լինի բոլոր գրառումները վերցնել սեղանից: Ընդհանուր անհրաժեշտություն է `պայմանների օգտագործումը` վերադարձված տվյալների զտման համար:

Եկեք օգտագործենք այդ օրինակը ՝ նշելու այն բոլոր այն հաշիվ-ապրանքագրերը, որոնք դեռ պետք է վճարվեն (Ո՞րտեղ վճարված է NULL):

ActiveRecord

irb (main): 024: 0> Invoice.where (pay_at: nil) Invoice Load (18.2ms) SELECT «հաշիվ-ապրանքագրեր»: * FROM- ից «Ո՞ւր են» հաշիվ-ապրանքագրեր »: , 11]] => # , # <Պարտատոմսերի ID: 4, user_id: 4, Payment_method: nil, Payment_at: nil, afirandina_at:" 2018-01-05 08:00:00 ", թարմացված_at:" 2018-01-05 08:00:00 ">]>

Էկտո

iex (19)> որտեղ (հաշիվ-ապրանքագիր, [i], is_nil (i.paid_at)) |> Repo.all ()
[կարգաբերում] QUERY OK աղբյուր = "հաշիվ ապրանքագիր" db = 20.2ms
SELECT i0. "Id", i0. "Payment_method", i0. "Payment_at", i0. "User_id", i0. "Inserted_at", i0. "Updated_at" FROM "ապրանքագրերը" AS i0 WHERE (i0 "" pay_at "IS ԴԱՏԱՐԿ) []
[
  % Financex.Accounts.Invoice {
    __meta__: # Ecto.Schema.Metadata <: բեռնված, "հաշիվ-ապրանքագրեր">,
    id: 3,
    inserted_at: ~ N [2018-01-04 08: 00: 00.000000],
    վճարված_աթ. նիլ,
    Payment_method: nil,
    updated_at: ~ N [2018-01-04 08: 00: 00.000000],
    օգտվող ՝ # Ecto.Association.NotLoaded <ասոցիացիա. օգտվողը բեռնված չէ>,
    user_id: 3
  ,
  % Financex.Accounts.Invoice {
    __meta__: # Ecto.Schema.Metadata <: բեռնված, "հաշիվ-ապրանքագրեր">,
    id: 4,
    inserted_at: ~ N [2018-01-04 08: 00: 00.000000],
    վճարված_աթ. նիլ,
    Payment_method: nil,
    updated_at: ~ N [2018-01-04 08: 00: 00.000000],
    օգտվող ՝ # Ecto.Association.NotLoaded <ասոցիացիա. օգտվողը բեռնված չէ>,
    user_id: 4
  }
]

Համեմատություն

Երկու օրինակներում էլ օգտագործվում է այն բառն բառը, որը SQL WHERE կետի հետ կապ է: Չնայած ստեղծված SQL հարցումները բավականին նման են, երկու գործիքներն այնտեղ հասնելու եղանակը ունի մի քանի կարևոր տարբերություններ:

ActiveRecord- ը ինքնաբերաբար վերափոխում է վճարովի_ատը. Որպեսզի Ecto- ով օգտագործելով նույն արդյունքը, մշակողները պետք է ավելի բացահայտ լինեն իրենց մտադրության վերաբերյալ ՝ զանգահարելով is_nil ():

Մեկ այլ տարբերություն, որը պետք է ընդգծվի, գործառույթի «մաքուր» պահվածքն է, որտեղ գտնվում է Ecto- ում: Երբ միայն ֆունկցիան զանգահարելիս, այն չի կապում տվյալների բազայի հետ: Որտեղ գործառույթի վերադարձը Ecto.Query կառուցվածքն է.

iex (20)> որտեղ (հաշիվ-ապրանքագիր, [i], is_nil (i.paid_at))
# Ecto.Query 

Տվյալների շտեմարանը շոշափվում է միայն այն ժամանակ, երբ Repo.all () գործառույթը կոչվում է ՝ Ecto.Query կառուցվածքը որպես փաստարկ ընդունելով: Այս մոտեցումը թույլ է տալիս հարցման կազմը Ecto- ում, որը հաջորդ բաժնի առարկա է:

Հարցման կազմը

Տվյալների բազայի հարցումների ամենահզոր կողմերից մեկը կազմն է: Այն հարցումը նկարագրում է այնպիսի եղանակով, որը պարունակում է ավելին, քան մեկ պայման:

Եթե ​​դուք կառուցում եք հում SQL հարցումներ, նշանակում է, որ դուք հավանաբար կօգտագործեք ինչ-որ հաշտեցում: Պատկերացրեք, դուք ունեք երկու պայման.

  1. not_paid = 'pay_at NULL'
  2. Payment_with_paypal = 'Payment_method = "Paypal"'

Այս երկու պայմանները միավորելու համար, օգտագործելով հում SQL, նշանակում է, որ դուք պետք է համաձայնեցեք դրանք ՝ օգտագործելով նման մի բան,

ԸՆՏՐԵԼ * Անվճար հաշիվ-ապրանքագրեր ԱՏԵՂ # {not_paid} AND # {վճարված_վճար_paypal}

Բարեբախտաբար, և ActiveRecord- ը, և Ecto- ն դրա համար լուծում ունեն:

ActiveRecord

irb (հիմնական): 003: 0> Invoice.where.not (Payment_at: nil). Where (Payment_method: "Paypal") Invoice Load (8.0ms) SELECT "հաշիվ-ապրանքագրեր": * FROM- ից "ապրանքագրերը" ՈՐՏԵՂ "հաշիվ-ապրանքագրեր": " Payment_at «ՉԻ NULL ԵՎ« հաշիվ-ապրանքագրեր »:" Payment_method "= $ 1 LIMIT $ 2 [[" "Payment_method", "Paypal"], ["LIMIT", 11]] => # ]>

Էկտո

iex (6)> հաշիվ ապրանքագիր> | որտեղ ([i], ոչ is_nil (i.paid_at)) |>> որտեղ ([i], i.payment_method == "Paypal") |> Repo.all ()
[կարգաբերում] QUERY OK աղբյուր = "հաշիվ-ապրանքագրեր" db = 30.0ms վերծանում = 0.6ms հերթ = 0.2ms
SELECT i0. "Id", i0. "Payment_method", i0. "Payment_at", i0. "User_id", i0. "Inserted_at", i0. "Updated_at" FROM "ապրանքագրերը" AS i0 WHERE (ՈՉ (i0 ") վճարված_at "IS NULL)) AND (i0." Payment_method "= 'Paypal') []
[
  Financex.Accounts.Ivoice {
    __meta__: # Ecto.Schema.Metadata <: բեռնված, "հաշիվ-ապրանքագրեր">,
    id: 2,
    inserted_at: ~ N [2018-01-03 08: 00: 00.000000],
    վճարված_at ՝ # DateTime <2018-02-01 08: 00: 00.000000Z>,
    Payment_method` «Paypal»,
    updated_at: ~ N [2018-01-03 08: 00: 00.000000],
    օգտվող ՝ # Ecto.Association.NotLoaded <ասոցիացիա. օգտվողը բեռնված չէ>,
    user_id: 2
  }
]

Համեմատություն

Երկու հարցումներն էլ պատասխանում են միևնույն հարցին. «Ո՞ր հաշիվ-ապրանքագրերը վճարվեցին և օգտագործվեցին Paypal»:

Ինչպես արդեն սպասվում էր, ActiveRecord- ը առաջարկում է հարցումը կազմելու ավելի համակողմանի միջոց (այդ օրինակով), մինչդեռ Ecto- ն ծրագրավորողներին պահանջում է մի փոքր ավելին ծախսել հարցումը գրելու համար: Սովորաբար, Batgirl- ը (որբը, մեկը համր է Կասանդրա Կայնի ինքնության հետ) կամ «Activerecord» - ը այնքան էլ բծախնդիր չէ:

Մի հիմարվեք վերը ցույց տրված «Ecto» հարցման կեղծվածությունից և ակնհայտ բարդությունից: Իրական համաշխարհային միջավայրում այդ հարցումը վերաշարադրվելու էր `ավելի նման լինելու համար.

Հաշիվ-ապրանքագիր
|> որտեղ ([i], չէ is_nil (i.paid_at))
|> որտեղ ([i], i.payment_method == «Paypal»)
|> Repo.all ()

Տեսնելով այդ տեսանկյունից, գործառույթի «մաքուր» ասպեկտների համադրությունը, որտեղ ինքնուրույն տվյալների բազաներ չի իրականացվում, խողովակների օպերատորի հետ միասին, Ecto- ում հարցման կազմը իսկապես մաքուր է դարձնում:

Պատվիրել

Պատվիրելը հարցման կարևոր կողմն է: Այն թույլ է տալիս մշակողներին ապահովել, որ հարցման արդյունքը հետևում է սահմանված կարգին:

ActiveRecord

irb (հիմնական): 002: 0> Invoice.order (afirandina_at:: desc) Հաշվեկշռային բեռ (1.5 մմ) ԸՆՏՐԵԼ «հաշիվ-ապրանքագրեր»: * «Անվճար» ապրանքագրերից «Պատվիրեք« հաշիվ-ապրանքագրերից »:« ստեղծեց_ատ »DESC LIMIT 1 $ ", 11]] => # , # <Պարտատոմսերի ID: 3, user_id: 3, Payment_method: nil, Payment_at: nil, afirandina_at: "2018-01-04 08:00:00", թարմացվել_at: "2018-01-04 08:00:00">, # <հաշիվ-ապրանքագիր: 2, user_id: 2, Payment_method: "Paypal", pay_at: "2018-02-01 08:00:00", ստեղծվել_at. "2018 թ. -01-03 08:00:00 ", թարմացվել է` "2018-01-03 08:00:00">, # <Պարտատոմսերի ID: 1, user_id: 1, վճարման_մեթոդ `" Կրեդիտ քարտ ", վճարված_ատ ՝" 2018- 02-01 08:00:00 ", create_at:" 2018-01-02 08:00:00 ", թարմացված_at." 2018-01-02 08:00:00 ">]>

Էկտո

iex (6)> order_by (հաշիվ-ապրանքագիր ՝ ներքև. ՝ inserted_at) |> Repo.all ()
[կարգաբերում] QUERY OK աղբյուր = "հաշիվ ապրանքագրեր" db = 19.8ms
SELECT i0. "Id", i0. "Payment_method", i0. "Payment_at", i0. "User_id", i0. "Inserted_at", i0. "Updated_at" FROM "ապրանքագրերը" AS i0 ORDER BY i0 "-ով: inserted_at" DESC []
[
  Financex.Accounts.Ivoice {
    __meta__: # Ecto.Schema.Metadata <: բեռնված, "հաշիվ-ապրանքագրեր">,
    id: 3,
    inserted_at: ~ N [2018-01-04 08: 00: 00.000000],
    վճարված_աթ. նիլ,
    Payment_method: nil,
    updated_at: ~ N [2018-01-04 08: 00: 00.000000],
    օգտվող ՝ # Ecto.Association.NotLoaded <ասոցիացիա. օգտվողը բեռնված չէ>,
    user_id: 3
  ,
  Financex.Accounts.Ivoice {
    __meta__: # Ecto.Schema.Metadata <: բեռնված, "հաշիվ-ապրանքագրեր">,
    id: 4,
    inserted_at: ~ N [2018-01-04 08: 00: 00.000000],
    վճարված_աթ. նիլ,
    Payment_method: nil,
    updated_at: ~ N [2018-01-04 08: 00: 00.000000],
    օգտվող ՝ # Ecto.Association.NotLoaded <ասոցիացիա. օգտվողը բեռնված չէ>,
    user_id: 4
  ,
  Financex.Accounts.Ivoice {
    __meta__: # Ecto.Schema.Metadata <: բեռնված, "հաշիվ-ապրանքագրեր">,
    id: 2,
    inserted_at: ~ N [2018-01-03 08: 00: 00.000000],
    վճարված_at ՝ # DateTime <2018-02-01 08: 00: 00.000000Z>,
    Payment_method` «Paypal»,
    updated_at: ~ N [2018-01-03 08: 00: 00.000000],
    օգտվող ՝ # Ecto.Association.NotLoaded <ասոցիացիա. օգտվողը բեռնված չէ>,
    user_id: 2
  ,
  Financex.Accounts.Ivoice {
    __meta__: # Ecto.Schema.Metadata <: բեռնված, "հաշիվ-ապրանքագրեր">,
    id: 1,
    inserted_at: ~ N [2018-01-02 08: 00: 00.000000],
    վճարված_at ՝ # DateTime <2018-02-01 08: 00: 00.000000Z>,
    Payment_method` «Կրեդիտ քարտ»,
    updated_at: ~ N [2018-01-02 08: 00: 00.000000],
    օգտվող ՝ # Ecto.Association.NotLoaded <ասոցիացիա. օգտվողը բեռնված չէ>,
    user_id: 1
  }
]

Համեմատություն

Հարցմանը պատվեր ավելացնելը երկու գործիքներում էլ ուղիղ առաջ է:

Չնայած Ecto- ի օրինակը որպես առաջին պարամետր օգտագործում է հաշիվ-ապրանքագիր, կարգի_բյուր ֆունկցիան ընդունում է նաև Ecto.Query շարքերը, ինչը հնարավորություն է տալիս կարգի_բայ գործառույթն օգտագործել կոմպոզիցիաներում, ինչպես ՝

Հաշիվ-ապրանքագիր
|> որտեղ ([i], չէ is_nil (i.paid_at))
|> որտեղ ([i], i.payment_method == «Paypal»)
|> կարգի_բա (իջնում. ներմուծված_աթ)
|> Repo.all ()

Սահմանափակող

Ո՞րն է առանց սահմանի տվյալների բազա: Աղետ: Բարեբախտաբար, ինչպես ActiveRecord- ը, այնպես էլ Ecto- ն օգնում են սահմանափակել վերադարձված գրառումների քանակը:

ActiveRecord

irb (հիմնական): 004: 0> Invoice.limit (2)
Հաշվեկշռային բեռ (0.2ms) ԸՆՏՐԵԼ «հաշիվ-ապրանքագրեր»: * FROM- ից «ապրանքագրեր» հաշիվ-ապրանքագրեր $ 1 [["" LIMIT ", 2]]
=> # , # <Պարտատոմսերի ID: 2, user_id: 2, Payment_method:" Paypal ", pay_at:" 2018-02-01 08: 00:00 ", ստեղծվել_ատ ՝" 2018-01-03 08:00:00 ", թարմացված_at." 2018-01-03 08:00:00 ">]>

Էկտո

iex (22)> սահմանը (հաշիվ-ապրանքագիր, 2) |> Repo.all ()
[կարգաբերում] QUERY OK աղբյուր = "հաշիվ ապրանքագիր" db = 3.6ms
SELECT i0. "Id", i0. "Payment_method", i0. "Payment_at", i0. "User_id", i0. "Inserted_at", i0. "Updated_at" FROM "ապրանքագրերը" AS i0 LIMIT 2 []
[
  Financex.Accounts.Ivoice {
    __meta__: # Ecto.Schema.Metadata <: բեռնված, "հաշիվ-ապրանքագրեր">,
    id: 1,
    inserted_at: ~ N [2018-01-02 08: 00: 00.000000],
    վճարված_at ՝ # DateTime <2018-02-01 08: 00: 00.000000Z>,
    Payment_method` «Կրեդիտ քարտ»,
    updated_at: ~ N [2018-01-02 08: 00: 00.000000],
    օգտվող ՝ # Ecto.Association.NotLoaded <ասոցիացիա. օգտվողը բեռնված չէ>,
    user_id: 1
  ,
  Financex.Accounts.Ivoice {
    __meta__: # Ecto.Schema.Metadata <: բեռնված, "հաշիվ-ապրանքագրեր">,
    id: 2,
    inserted_at: ~ N [2018-01-03 08: 00: 00.000000],
    վճարված_at ՝ # DateTime <2018-02-01 08: 00: 00.000000Z>,
    Payment_method` «Paypal»,
    updated_at: ~ N [2018-01-03 08: 00: 00.000000],
    օգտվող ՝ # Ecto.Association.NotLoaded <ասոցիացիա. օգտվողը բեռնված չէ>,
    user_id: 2
  }
]

Համեմատություն

Ինչպես ActiveRecord- ը, այնպես էլ Ecto- ն ունեն հարցում, որի միջոցով վերադարձվում են գրառումների քանակը:

Ecto- ի սահմանաչափը գործում է ինչպես կարգի_ կարգին, հարմար է հարցման կոմպոզիցիաների համար:

Ասոցիացիաներ

ActiveRecord- ը և Ecto- ն ունեն տարբեր մոտեցումներ, երբ խոսքը վերաբերում է, թե ինչպես են գործակցվում ասոցիացիաների հետ:

ActiveRecord

ActiveRecord- ում դուք կարող եք օգտագործել որևէ մոդելով սահմանված որևէ ասոցիացիա, առանց դրա մասին որևէ հատուկ բան անելու, օրինակ.

irb (հիմնական): 012: 0> user = User.find (2) Օգտագործողի բեռ (0.3 մ) ԸՆՏՐԵԼ «օգտվողներ»: * «ԱՅՍ» օգտվողներից «Ո՞ւր են» օգտվողները »:" id "= $ 1 LIMIT $ 2 [[" id " , 2], ["LIMIT", 1]] => # User id: 2, full_name: "Barbara Gordon", էլ. Փոստ ՝ "[email protected]", ստեղծվել_at ՝ "2018-01-02 10:02: 00 ", updated_at:" 2018-01-02 10:02:00 "> irb (main): 013: 0> user.invoices Հաշվեկշռային բեռ (0.4 մմ) ԸՆՏՐԵԼ« հաշիվ-ապրանքագրեր »: . "user_id" = $ 1 LIMIT $ 2 [["user_id", 2], ["LIMIT", 11]] => # ] >

Վերևի օրինակը ցույց է տալիս, որ user.invoices- ին զանգահարելիս կարող ենք ստանալ օգտվողի հաշիվ-ապրանքագրերի ցուցակ: Դա անելու դեպքում ActiveRecord- ը ինքնաբերաբար զննեց տվյալների բազան և բեռնեց այն հաշիվ-ապրանքագրերը, որոնք կապված են օգտագործողի հետ: Թեև այս մոտեցումը հեշտացնում է իրերը, ավելի քիչ կոդ գրելու կամ լրացուցիչ քայլերի անհանգստանալու իմաստով, միգուցե խնդիր է առաջանում, եթե մի շարք օգտվողների կողմից այն կրկնվում է և յուրաքանչյուր օգտագործողի հաշիվ-ապրանքագրերը հանում: Այս հարցը հայտնի է որպես «N + 1 խնդիր»:

ActiveRecord- ում «N + 1 խնդրի» առաջարկված ամրագրումն է ՝ ներառում է ներառված եղանակը.

irb (հիմնական) ՝ 022: 0> user = User.includes (: հաշիվ-ապրանքագրեր). գտել (2) օգտվողի բեռ (0.3 մ) ԸՆՏՐԵԼ «օգտվողներ»: * «օգտվողներից» «Ո՞ւր» օգտագործողներից: "id" = $ 1 LIMIT $ 2 [["" id ", 2], [" LIMIT ", 1]] Factory Load (0.6ms) SELECT" հաշիվ-ապրանքագրեր ": * FROM- ից" Ո՞ւր է "հաշիվ-ապրանքագրեր": "user_id" = $ 1 [["user_id", 2]] => #  irb (հիմնական): 023: 0> user.invoices => # ]>

Այս դեպքում ActiveRecord- ը անհամբերությամբ բեռնում է հաշիվ-ապրանքագրերի ասոցիացիան օգտագործողին դուրս բերելու ժամանակ (ինչպես երևում է ցույց տրված երկու SQL հարցումներում):

Էկտո

Ինչպես երևի արդեն նկատել եք, Էկտոն իրականում դուր չի գալիս մոգություն կամ դաստիարակություն: Այն պահանջում է, որ մշակողները հստակ լինեն իրենց մտադրությունների վերաբերյալ:

Եկեք փորձենք user.invoices- ը Ecto- ի օգտագործման նույն մոտեցումը.

iex (7)> ​​օգտվող = Repo.get (օգտվող ՝ 2)
[կարգաբերում] QUERY OK աղբյուր = "օգտվողները" db = 18.3ms վերծանում = 0.6ms
ԸՆՏՐԵԼ u0. "Id", u0. "Full_name", u0. "Email", u0. "Inserted_at", u0. "Updated_at" FROM "օգտվողները" AS u0 WHERE (u0. "Id" = $ 1) [2]
Financex.Accounts.User
  __meta__: # Ecto.Schema.Metadata <: բեռնված, "օգտվողներ">,
  էլ. փոստ ՝ "[email protected]",
  full_name ՝ «Բարբարա Գորդոն»,
  id: 2,
  inserted_at: ~ N [2018-01-02 10: 02: 00.000000],
  հաշիվ-ապրանքագրեր. # Ecto.Association.NotLoaded <ասոցիացիա. հաշիվ-ապրանքագրերը բեռնված չեն>,
  updated_at: ~ N [2018-01-02 10: 02: 00.000000]
}
iex (8)> user.invoices
# Ecto.Association.NotLoaded <ասոցիացիա. Հաշիվ-ապրանքագրերը բեռնված չեն>

Արդյունքը Ecto.Association.NotLoaded- ն է: Ոչ այնքան օգտակար:

Հաշիվ-ապրանքագրերը մուտք ունենալու համար մշակողը պետք է Ecto- ին այդ մասին տեղեկացնի ՝ օգտագործելով նախաբեռնման գործառույթը

iex (12)> user = preload (Օգտագործող ՝ հաշիվ ապրանքագիր) |> Repo.get (2)
[կարգաբերում] QUERY OK աղբյուր = "օգտվողները" db = 11.8ms
ԸՆՏՐԵԼ u0. "Id", u0. "Full_name", u0. "Email", u0. "Inserted_at", u0. "Updated_at" FROM "օգտվողները" AS u0 WHERE (u0. "Id" = $ 1) [2]
[կարգաբերում] QUERY OK աղբյուր = "հաշիվ ապրանքագիր" db = 4.2ms
SELECT i0. "Id", i0. "Payment_method", i0. "Payment_at", i0. "User_id", i0. "Inserted_at", i0. "Updated_at", i0. "User_id" FROM "ապրանքագրերը" AS i0 WHERE ( i0. "user_id" = $ 1) Պատվիրեք i0- ով: "user_id" [2]
Financex.Accounts.User
  __meta__: # Ecto.Schema.Metadata <: բեռնված, "օգտվողներ">,
  էլ. փոստ ՝ "[email protected]",
  full_name ՝ «Բարբարա Գորդոն»,
  id: 2,
  inserted_at: ~ N [2018-01-02 10: 02: 00.000000],
  հաշիվ-ապրանքագրեր. [
    Financex.Accounts.Ivoice {
      __meta__: # Ecto.Schema.Metadata <: բեռնված, "հաշիվ-ապրանքագրեր">,
      id: 2,
      inserted_at: ~ N [2018-01-03 08: 00: 00.000000],
      վճարված_at ՝ # DateTime <2018-02-01 08: 00: 00.000000Z>,
      Payment_method` «Paypal»,
      updated_at: ~ N [2018-01-03 08: 00: 00.000000],
      օգտվող ՝ # Ecto.Association.NotLoaded <ասոցիացիա. օգտվողը բեռնված չէ>,
      user_id: 2
    }
  ],
  updated_at: ~ N [2018-01-02 10: 02: 00.000000]
}

iex (15)> user.invoices
[
  Financex.Accounts.Ivoice {
    __meta__: # Ecto.Schema.Metadata <: բեռնված, "հաշիվ-ապրանքագրեր">,
    id: 2,
    inserted_at: ~ N [2018-01-03 08: 00: 00.000000],
    վճարված_at ՝ # DateTime <2018-02-01 08: 00: 00.000000Z>,
    Payment_method` «Paypal»,
    updated_at: ~ N [2018-01-03 08: 00: 00.000000],
    օգտվող ՝ # Ecto.Association.NotLoaded <ասոցիացիա. օգտվողը բեռնված չէ>,
    user_id: 2
  }
]

Նմանապես ActiveRecord- ի մեջ ներառված է նաև, որ նախաբեռնումն է բերում համապատասխան հաշիվ-ապրանքագրերը, ինչը նրանց հասանելի կդարձնի user.invoices- ին զանգահարելիս:

Համեմատություն

Եվս մեկ անգամ, ActiveRecord- ի և Ecto- ի միջև մարտը ավարտվում է հայտնի կետով ՝ պայթյուն: Երկու գործիքներն էլ ծրագրավորողներին հնարավորություն են տալիս հեշտությամբ մուտք գործել ասոցիացիաներ, բայց մինչդեռ ActiveRecord- ը այն ավելի բուռն է դարձնում, դրա արդյունքը կարող է ունենալ անսպասելի վարք: Ecto- ն հետևում է WYSIWYG տեսակի մոտեցմանը, որը կատարում է միայն այն, ինչ երևում է ծրագրավորողի կողմից սահմանված հարցմանը:

Rails- ը հայտնի է դիմումի բոլոր տարբեր շերտերի պահպանումային ռազմավարություններ օգտագործելու և խթանելու համար: Օրինակներից մեկը վերաբերում է «Ռուսական տիկնիկ» քեշինգի մոտեցմանը, որը լիովին ապավինում է «N + 1 խնդրին» `իր հմայքը կատարելու մեխանիզմի պահելու մեխանիզմի համար:

Վավերագրեր

ActiveRecord- ում ներկա վավերագրերի մեծ մասը առկա է նաև Ecto- ում: Ահա ընդհանուր վավերացումների ցուցակը, և թե ինչպես ActiveRecord- ը և Ecto- ն են դրանք սահմանում.

Փաթաթեք

Այնտեղ այն կա. Հիմնական խնձորներն ընդդեմ նարինջի համեմատության:

ActiveRecord- ը կենտրոնանում է տվյալների բազայի հարցումների կատարման հեշտության վրա: Դրա հատկությունների մեծ մասը կենտրոնացած է հենց մոդելի դասերի վրա ՝ չպահանջելով, որ ծրագրավորողներն ունենան տվյալների բազայի խորը պատկերացում, ոչ էլ նման գործողությունների ազդեցությունը: ActiveRecord- ը լռելյայն կատարում է շատ բաներ: Չնայած դա ավելի հեշտացնում է սկսելը, այն ավելի բարդացնում է հասկանալ, թե ինչ է կատարվում կուլիսային մասում, և այն միայն այն դեպքում, եթե հետևեք «ActiveRecord ճանապարհին»:

Ecto- ն, մյուս կողմից, պահանջում է պարզաբանում, ինչը հանգեցնում է ավելի շատ բայական կոդի: Որպես օգուտ ՝ ամեն ինչ ուշադրության կենտրոնում է, կուլիսներում ոչինչ չկա, և դուք կարող եք նշել ձեր սեփական ճանապարհը:

Երկուսն էլ ունեն իրենց անկարգ, կախված ձեր տեսանկյունից և նախասիրությունից: Այսպիսով, համեմատելով խնձորն ու նարինջը, մենք հասնում ենք այս BAT-tle- ի ավարտին: Համարյա մոռացա ձեզ ասել, որ BatGirl- ի անվան կոդն էր (1989–2001)…: Oracle- ը: Բայց եկեք դրան չմտնենք:

Այս գրառումը գրված է հյուրի հեղինակ Էլվիո Վիկոսայի կողմից: Էլվիոն «Phoenix for Rails Developers» գրքի հեղինակն է:

Սկզբնապես հրապարակվել է blog.appsignal.com կայքում ՝ 2018 թվականի հոկտեմբերի 9-ին: