यह "ActiveRecord बनाम Ecto" श्रृंखला का दूसरा भाग है, जिसमें बैटमैन और बैटगर्ल डेटाबेस क्वेरी करने पर लड़ते हैं और हम सेब और संतरे की तुलना करते हैं।
पहले भाग में डेटाबेस स्कीमा और माइग्रेशन में ActiveRecord बनाम Ecto को देखने के बाद, यह पोस्ट कवर करती है कि कैसे ActiveRecord और Ecto दोनों डेवलपर्स को डेटाबेस क्वेरी करने में सक्षम बनाते हैं, और जब समान आवश्यकताओं से निपटते हैं तो दोनों ActiveRecord और Ecto की तुलना कैसे करते हैं। रास्ते में, हम बैटगर्ल की 1989-2011 की पहचान भी पता लगाएंगे।
सीड डेटा
आइए शुरू करें! पहले पोस्ट में परिभाषित डेटाबेस संरचना के आधार पर, मान लें कि users और invoices टेबल में निम्न डेटा संग्रहीत है:
users
| id | full_name | created_at* | updated_at | |
|---|---|---|---|---|
| 1 | Bette Kane | bette@kane.test | 2018-01-01 10:01:00 | 2018-01-01 10:01:00 |
| 2 | Barbara Gordon | barbara@gordon.test | 2018-01-02 10:02:00 | 2018-01-02 10:02:00 |
| 3 | Cassandra Cain | cassandra@cain.test | 2018-01-03 10:03:00 | 2018-01-03 10:03:00 |
| 4 | Stephanie Brown | stephanie@brown.test | 2018-01-04 10:04:00 | 2018-01-04 10:04:00 |
* ActiveRecord का created_at फ़ील्ड Ecto में डिफ़ॉल्ट रूप से inserted_at नामित है।
invoices
| id | user_id | payment_method | paid_at | created_at* | updated_at |
|---|---|---|---|---|---|
| 1 | 1 | Credit Card | 2018-02-01 08:00:00 | 2018-01-02 08:00:00 | 2018-01-02 08:00:00 |
| 2 | 2 | Paypal | 2018-02-01 08:00:00 | 2018-01-03 08:00:00 | 2018-01-03 08:00:00 |
| 3 | 3 | 2018-01-04 08:00:00 | 2018-01-04 08:00:00 | ||
| 4 | 4 | 2018-01-05 08:00:00 | 2018-01-05 08:00:00 |
* ActiveRecord का created_at फ़ील्ड Ecto में डिफ़ॉल्ट रूप से inserted_at नामित है।
इस पोस्ट में किए गए क्वेरीज़ मानते हैं कि उपरोक्त डेटा डेटाबेस में संग्रहीत है, इसलिए इसे पढ़ते समय इस जानकारी को ध्यान में रखें।
प्राथमिक कुंजी का उपयोग करके आइटम ढूंढना
आइए डेटाबेस से इसकी प्राथमिक कुंजी का उपयोग करके एक रिकॉर्ड प्राप्त करके शुरू करें।
ActiveRecord
irb(main):001:0> User.find(1)
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
=> #
Ecto
iex(3)> Repo.get(User, 1)
[debug] QUERY OK source="users" db=5.2ms decode=2.5ms queue=0.1ms
SELECT u0."id", u0."full_name", u0."email", u0."inserted_at", u0."updated_at" FROM "users" AS u0 WHERE (u0."id" = $1) [1]
%Financex.Accounts.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
email: "bette@kane.test",
full_name: "Bette Kane",
id: 1,
inserted_at: ~N[2018-01-01 10:01:00.000000],
invoices: #Ecto.Association.NotLoaded,
updated_at: ~N[2018-01-01 10:01:00.000000]
}
तुलना
दोनों मामले काफी समान हैं। ActiveRecord User मॉडल क्लास के find क्लास मेथड पर निर्भर करता है। इसका मतलब है कि हर ActiveRecord चाइल्ड क्लास का अपना find मेथड होता है।
Ecto एक अलग दृष्टिकोण का उपयोग करता है, जो मैपिंग लेयर और डोमेन के बीच मध्यस्थ के रूप में रिपॉजिटरी अवधारणा पर निर्भर करता है। Ecto का उपयोग करते समय, User मॉड्यूल को इस बारे में कोई जानकारी नहीं होती कि वह खुद को कैसे ढूंढे। ऐसी जिम्मेदारी Repo मॉड्यूल में मौजूद होती है, जो इसे अंतर्निहित डेटास्टोर में मैप करने में सक्षम होती है, जो हमारे मामले में Postgres है।
SQL क्वेरी की तुलना करते समय, हम कुछ अंतर देख सकते हैं:
- ActiveRecord सभी फ़ील्ड्स लोड करता है (
users.*), जबकि Ecto केवलschemaपरिभाषा में सूचीबद्ध फ़ील्ड्स लोड करता है। - ActiveRecord क्वेरी में
LIMIT 1शामिल करता है, जबकि Ecto नहीं करता।
सभी आइटम लाना
आइए एक कदम आगे बढ़ें और डेटाबेस से सभी उपयोगकर्ताओं को लोड करें।
ActiveRecord
irb(main):001:0> User.all
User Load (0.5ms) SELECT "users".* FROM "users" LIMIT $1 [["LIMIT", 11]]
=> #, #, #, #]>
Ecto
iex(4)> Repo.all(User)
[debug] QUERY OK source="users" db=2.8ms decode=0.2ms queue=0.2ms
SELECT u0."id", u0."full_name", u0."email", u0."inserted_at", u0."updated_at" FROM "users" AS u0 []
[
%Financex.Accounts.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
email: "bette@kane.test",
full_name: "Bette Kane",
id: 1,
inserted_at: ~N[2018-01-01 10:01:00.000000],
invoices: #Ecto.Association.NotLoaded,
updated_at: ~N[2018-01-01 10:01:00.000000]
},
%Financex.Accounts.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
email: "barbara@gordon.test",
full_name: "Barbara Gordon",
id: 2,
inserted_at: ~N[2018-01-02 10:02:00.000000],
invoices: #Ecto.Association.NotLoaded,
updated_at: ~N[2018-01-02 10:02:00.000000]
},
%Financex.Accounts.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
email: "cassandra@cain.test",
full_name: "Cassandra Cain",
id: 3,
inserted_at: ~N[2018-01-03 10:03:00.000000],
invoices: #Ecto.Association.NotLoaded,
updated_at: ~N[2018-01-03 10:03:00.000000]
},
%Financex.Accounts.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
email: "stephanie@brown.test",
full_name: "Stephanie Brown",
id: 4,
inserted_at: ~N[2018-01-04 10:04:00.000000],
invoices: #Ecto.Association.NotLoaded,
updated_at: ~N[2018-01-04 10:04:00.000000]
}
]
तुलना
यह पिछले खंड के समान पैटर्न का पालन करता है। ActiveRecord all क्लास मेथड का उपयोग करता है और Ecto रिकॉर्ड लोड करने के लिए रिपॉजिटरी पैटर्न पर निर्भर करता है।
SQL क्वेरीज़ में फिर से कुछ अंतर हैं:
- पिछले खंड के समान, ActiveRecord सभी फ़ील्ड्स लोड करता है (
users.*), जबकि Ecto केवलschemaपरिभाषा में सूचीबद्ध फ़ील्ड्स लोड करता है। - ActiveRecord
LIMIT 11भी परिभाषित करता है, जबकि Ecto बस सब कुछ लोड करता है। यह सीमा कंसोल पर उपयोग की जाने वालीinspectविधि से आती है (https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation.rb#L599)।
शर्तों के साथ क्वेरी करना
यह बहुत संभावना नहीं है कि हमें किसी टेबल से सभी रिकॉर्ड लाने की आवश्यकता हो। एक आम आवश्यकता शर्तों का उपयोग करना है, ताकि लौटाए गए डेटा को फ़िल्टर किया जा सके।
आइए उस उदाहरण का उपयोग करें ताकि सभी invoices को सूचीबद्ध किया जा सके जिनका अभी भुगतान नहीं किया गया है (WHERE paid_at IS NULL)।
ActiveRecord
irb(main):024:0> Invoice.where(paid_at: nil)
Invoice Load (18.2ms) SELECT "invoices".* FROM "invoices" WHERE "invoices"."paid_at" IS NULL LIMIT $1 [["LIMIT", 11]]
=> #, #]>
Ecto
iex(19)> where(Invoice, [i], is_nil(i.paid_at)) |> Repo.all()
[debug] QUERY OK source="invoices" db=20.2ms
SELECT i0."id", i0."payment_method", i0."paid_at", i0."user_id", i0."inserted_at", i0."updated_at" FROM "invoices" AS i0 WHERE (i0."paid_at" IS NULL) []
[
%Financex.Accounts.Invoice{
__meta__: #Ecto.Schema.Metadata<:loaded, "invoices">,
id: 3,
inserted_at: ~N[2018-01-04 08:00:00.000000],
paid_at: nil,
payment_method: nil,
updated_at: ~N[2018-01-04 08:00:00.000000],
user: #Ecto.Association.NotLoaded,
user_id: 3
},
%Financex.Accounts.Invoice{
__meta__: #Ecto.Schema.Metadata<:loaded, "invoices">,
id: 4,
inserted_at: ~N[2018-01-04 08:00:00.000000],
paid_at: nil,
payment_method: nil,
updated_at: ~N[2018-01-04 08:00:00.000000],
user: #Ecto.Association.NotLoaded,
user_id: 4
}
]
तुलना
दोनों उदाहरणों में, where कीवर्ड का उपयोग किया जाता है, जो SQL WHERE क्लॉज से एक कनेक्शन है। हालांकि उत्पन्न SQL क्वेरीज़ काफी समान हैं, दोनों टूल्स वहां कैसे पहुंचते हैं इसमें कुछ महत्वपूर्ण अंतर हैं।
ActiveRecord paid_at: nil आर्ग्युमेंट को paid_at IS NULL SQL स्टेटमेंट में स्वचालित रूप से बदल देता है। Ecto का उपयोग करके उसी आउटपुट तक पहुंचने के लिए, डेवलपर्स को अपने इरादे के बारे में अधिक स्पष्ट होने की आवश्यकता है, is_nil() को कॉल करके।
उजागर करने वाला एक और अंतर Ecto में फ़ंक्शन where का "शुद्ध" व्यवहार है। जब where फ़ंक्शन को अकेले कॉल किया जाता है, तो यह डेटाबेस के साथ इंटरैक्ट नहीं करता। where फ़ंक्शन का रिटर्न एक Ecto.Query स्ट्रक्ट है:
iex(20)> where(Invoice, [i], is_nil(i.paid_at))
#Ecto.Query
डेटाबेस को केवल तब छुआ जाता है जब Repo.all() फ़ंक्शन को कॉल किया जाता है, Ecto.Query स्ट्रक्ट को आर्ग्युमेंट के रूप में पास करके। यह दृष्टिकोण Ecto में क्वेरी संरचना की अनुमति देता है, जो अगले खंड का विषय है।
क्वेरी संरचना
डेटाबेस क्वेरी के सबसे शक्तिशाली पहलुओं में से एक संरचना है। यह एक क्वेरी का वर्णन करने का एक तरीका है जिसमें एक से अधिक शर्तें होती हैं।
यदि आप कच्चे SQL क्वेरी बना रहे हैं, तो इसका मतलब है कि आप शायद किसी प्रकार के कॉनकैटिनेशन का उपयोग करेंगे। कल्पना करें कि आपके पास दो शर्तें हैं:
not_paid = 'paid_at IS NOT NULL'paid_with_paypal = 'payment_method = "Paypal"'
उन दो शर्तों को कच्चे SQL का उपयोग करके संयोजित करने के लिए, आपको उन्हें किसी ऐसी चीज़ का उपयोग करके कॉनकैटिनेट करना होगा:
SELECT * FROM invoices WHERE #{not_paid} AND #{paid_with_paypal}
सौभाग्य से, ActiveRecord और Ecto दोनों के पास इसके लिए एक समाधान है।
ActiveRecord
irb(main):003:0> Invoice.where.not(paid_at: nil).where(payment_method: "Paypal")
Invoice Load (8.0ms) SELECT "invoices".* FROM "invoices" WHERE "invoices"."paid_at" IS NOT NULL AND "invoices"."payment_method" = $1 LIMIT $2 [["payment_method", "Paypal"], ["LIMIT", 11]]
=> #]>
Ecto
iex(6)> Invoice |> where([i], not is_nil(i.paid_at)) |> where([i], i.payment_method == "Paypal") |> Repo.all()
[debug] QUERY OK source="invoices" db=30.0ms decode=0.6ms queue=0.2ms
SELECT i0."id", i0."payment_method", i0."paid_at", i0."user_id", i0."inserted_at", i0."updated_at" FROM "invoices" AS i0 WHERE (NOT (i0."paid_at" IS NULL)) AND (i0."payment_method" = 'Paypal') []
[
%Financex.Accounts.Invoice{
__meta__: #Ecto.Schema.Metadata<:loaded, "invoices">,
id: 2,
inserted_at: ~N[2018-01-03 08:00:00.000000],
paid_at: #DateTime<2018-02-01 08:00:00.000000Z>,
payment_method: "Paypal",
updated_at: ~N[2018-01-03 08:00:00.000000],
user: #Ecto.Association.NotLoaded,
user_id: 2
}
]
तुलना
दोनों क्वेरीज़ एक ही सवाल का जवाब दे रही हैं: "कौन से इनवॉइस का भुगतान किया गया था और Paypal का उपयोग किया गया था?"।
जैसा कि पहले से ही अपेक्षित था, ActiveRecord उस उदाहरण के लिए क्वेरी लिखने का एक अधिक संक्षिप्त तरीका प्रदान करता है, जबकि Ecto को डेवलपर्स को क्वेरी लिखने पर थोड़ा अधिक खर्च करने की आवश्यकता होती है। हमेशा की तरह, बैटगर्ल (अनाथ, मूक Cassandra Cain पहचान वाली) या ActiveRecord कम वर्बोज़ है।
Ecto क्वेरी की वर्बोसिटी और स्पष्ट जटिलता से मूर्ख मत बनो। एक वास्तविक दुनिया के वातावरण में, उस क्वेरी को इस तरह फिर से लिखा जाएगा:
Invoice |> where([i], not is_nil(i.paid_at)) |> where([i], i.payment_method == "Paypal") |> Repo.all()
उस कोण से देखने पर, फ़ंक्शन where के "शुद्ध" पहलुओं का संयोजन, जो स्वयं डेटाबेस ऑपरेशन नहीं करता, पाइप ऑपरेटर के साथ, Ecto में क्वेरी संरचना को वास्तव में साफ बनाता है।
क्रमबद्ध करना
क्रमबद्ध करना क्वेरी का एक महत्वपूर्ण पहलू है। यह डेवलपर्स को यह सुनिश्चित करने में सक्षम बनाता है कि एक दी गई क्वेरी परिणाम एक निर्दिष्ट क्रम का पालन करता है।
ActiveRecord
irb(main):002:0> Invoice.order(created_at: :desc)
Invoice Load (1.5ms) SELECT "invoices".* FROM "invoices" ORDER BY "invoices"."created_at" DESC LIMIT $1 [["LIMIT", 11]]
=> #, #, #, #]>
Ecto
iex(6)> order_by(Invoice, desc: :inserted_at) |> Repo.all()
[debug] QUERY OK source="invoices" db=19.8ms
SELECT i0."id", i0."payment_method", i0."paid_at", i0."user_id", i0."inserted_at", i0."updated_at" FROM "invoices" AS i0 ORDER BY i0."inserted_at" DESC []
[
%Financex.Accounts.Invoice{
__meta__: #Ecto.Schema.Metadata<:loaded, "invoices">,
id: 3,
inserted_at: ~N[2018-01-04 08:00:00.000000],
paid_at: nil,
payment_method: nil,
updated_at: ~N[2018-01-04 08:00:00.000000],
user: #Ecto.Association.NotLoaded,
user_id: 3
},
%Financex.Accounts.Invoice{
__meta__: #Ecto.Schema.Metadata<:loaded, "invoices">,
id: 4,
inserted_at: ~N[2018-01-04 08:00:00.000000],
paid_at: nil,
payment_method: nil,
updated_at: ~N[2018-01-04 08:00:00.000000],
user: #Ecto.Association.NotLoaded,
user_id: 4
},
%Financex.Accounts.Invoice{
__meta__: #Ecto.Schema.Metadata<:loaded, "invoices">,
id: 2,
inserted_at: ~N[2018-01-03 08:00:00.000000],
paid_at: #DateTime<2018-02-01 08:00:00.000000Z>,
payment_method: "Paypal",
updated_at: ~N[2018-01-03 08:00:00.000000],
user: #Ecto.Association.NotLoaded,
user_id: 2
},
%Financex.Accounts.Invoice{
__meta__: #Ecto.Schema.Metadata<:loaded, "invoices">,
id: 1,
inserted_at: ~N[2018-01-02 08:00:00.000000],
paid_at: #DateTime<2018-02-01 08:00:00.000000Z>,
payment_method: "Credit Card",
updated_at: ~N[2018-01-02 08:00:00.000000],
user: #Ecto.Association.NotLoaded,
user_id: 1
}
]
तुलना
क्वेरी में क्रम जोड़ना दोनों टूल्स में सीधा है।
हालांकि Ecto उदाहरण पहले पैरामीटर के रूप में Invoice का उपयोग करता है, order_by फ़ंक्शन Ecto.Query स्ट्रक्ट्स को भी स्वीकार करता है, जो order_by फ़ंक्शन को संरचनाओं में उपयोग करने में सक्षम बनाता है, जैसे:
Invoice |> where([i], not is_nil(i.paid_at)) |> where([i], i.payment_method == "Paypal") |> order_by(desc: :inserted_at) |> Repo.all()
सीमित करना
सीमा के बिना डेटाबेस क्या होगा? एक आपदा। सौभाग्य से, ActiveRecord और Ecto दोनों लौटाए गए रिकॉर्ड की संख्या को सीमित करने में मदद करते हैं।
ActiveRecord
irb(main):004:0> Invoice.limit(2)
Invoice Load (0.2ms) SELECT "invoices".* FROM "invoices" LIMIT $1 [["LIMIT", 2]]
=> #, #]>
Ecto
iex(22)> limit(Invoice, 2) |> Repo.all()
[debug] QUERY OK source="invoices" db=3.6ms
SELECT i0."id", i0."payment_method", i0."paid_at", i0."user_id", i0."inserted_at", i0."updated_at" FROM "invoices" AS i0 LIMIT 2 []
[
%Financex.Accounts.Invoice{
__meta__: #Ecto.Schema.Metadata<:loaded, "invoices">,
id: 1,
inserted_at: ~N[2018-01-02 08:00:00.000000],
paid_at: #DateTime<2018-02-01 08:00:00.000000Z>,
payment_method: "Credit Card",
updated_at: ~N[2018-01-02 08:00:00.000000],
user: #Ecto.Association.NotLoaded,
user_id: 1
},
%Financex.Accounts.Invoice{
__meta__: #Ecto.Schema.Metadata<:loaded, "invoices">,
id: 2,
inserted_at: ~N[2018-01-03 08:00:00.000000],
paid_at: #DateTime<2018-02-01 08:00:00.000000Z>,
payment_method: "Paypal",
updated_at: ~N[2018-01-03 08:00:00.000000],
user: #Ecto.Association.NotLoaded,
user_id: 2
}
]
तुलना
ActiveRecord और Ecto दोनों के पास एक क्वेरी द्वारा लौटाए गए रिकॉर्ड की संख्या को सीमित करने का एक तरीका है।
Ecto का limit order_by के समान काम करता है, जो क्वेरी संरचनाओं के लिए उपयुक्त है।
एसोसिएशन
ActiveRecord और Ecto के अलग-अलग दृष्टिकोण हैं जब बात आती है कि एसोसिएशन को कैसे संभाला जाता है।
ActiveRecord
ActiveRecord में, आप एक मॉडल में परिभाषित किसी भी एसोसिएशन का उपयोग कर सकते हैं, बिना उसके बारे में कुछ विशेष किए, उदाहरण के लिए:
irb(main):012:0> user = User.find(2)
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]]
=> #
irb(main):013:0> user.invoices
Invoice Load (0.4ms) SELECT "invoices".* FROM "invoices" WHERE "invoices"."user_id" = $1 LIMIT $2 [["user_id", 2], ["LIMIT", 11]]
=> #]>
उपरोक्त उदाहरण दिखाता है कि जब user.invoices को कॉल किया जाता है तो हमें उपयोगकर्ता इनवॉइस की सूची मिल सकती है। ऐसा करते समय, ActiveRecord स्वचालित रूप से डेटाबेस को क्वेरी करता है और उपयोगकर्ता से जुड़े इनवॉइस को लोड करता है। जबकि यह दृष्टिकोण चीजों को आसान बनाता है, कम कोड लिखने या अतिरिक्त चरणों के बारे में चिंता करने के अर्थ में, यह एक समस्या हो सकती है यदि आप कई उपयोगकर्ताओं पर पुनरावृति कर रहे हैं और प्रत्येक उपयोगकर्ता के लिए इनवॉइस ला रहे हैं। इस मुद्दे को "N + 1 समस्या" के रूप में जाना जाता है।
ActiveRecord में, "N + 1 समस्या" के लिए प्रस्तावित समाधान includes विधि का उपयोग करना है:
irb(main):022:0> user = User.includes(:invoices).find(2)
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]]
Invoice Load (0.6ms) SELECT "invoices".* FROM "invoices" WHERE "invoices"."user_id" = $1 [["user_id", 2]]
=> #
irb(main):023:0> user.invoices
=> #]>
इस मामले में, ActiveRecord उपयोगकर्ता को लाते समय invoices एसोसिएशन को उत्सुकता से लोड करता है (दो SQL क्वेरीज़ में देखा गया)।
Ecto
जैसा कि आप पहले ही देख चुके होंगे, Ecto वास्तव में जादू या निहितता को पसंद नहीं करता। यह डेवलपर्स को अपने इरादों के बारे में स्पष्ट होने की आवश्यकता है।
आइए Ecto के साथ user.invoices का उपयोग करने के समान दृष्टिकोण का प्रयास करें:
iex(7)> user = Repo.get(User, 2)
[debug] QUERY OK source="users" db=18.3ms decode=0.6ms
SELECT u0."id", u0."full_name", u0."email", u0."inserted_at", u0."updated_at" FROM "users" AS u0 WHERE (u0."id" = $1) [2]
%Financex.Accounts.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
email: "barbara@gordon.test",
full_name: "Barbara Gordon",
id: 2,
inserted_at: ~N[2018-01-02 10:02:00.000000],
invoices: #Ecto.Association.NotLoaded,
updated_at: ~N[2018-01-02 10:02:00.000000]
}
iex(8)> user.invoices
#Ecto.Association.NotLoaded
परिणाम एक Ecto.Association.NotLoaded है। इतना उपयोगी नहीं।
इनवॉइस तक पहुंच प्राप्त करने के लिए, एक डेवलपर को Ecto को इसके बारे में बताने की आवश्यकता होती है, preload फ़ंक्शन का उपयोग करके:
iex(12)> user = preload(User, :invoices) |> Repo.get(2)
[debug] QUERY OK source="users" db=11.8ms
SELECT u0."id", u0."full_name", u0."email", u0."inserted_at", u0."updated_at" FROM "users" AS u0 WHERE (u0."id" = $1) [2]
[debug] QUERY OK source="invoices" db=4.2ms
SELECT i0."id", i0."payment_method", i0."paid_at", i0."user_id", i0."inserted_at", i0."updated_at", i0."user_id" FROM "invoices" AS i0 WHERE (i0."user_id" = $1) ORDER BY i0."user_id" [2]
%Financex.Accounts.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
email: "barbara@gordon.test",
full_name: "Barbara Gordon",
id: 2,
inserted_at: ~N[2018-01-02 10:02:00.000000],
invoices: [
%Financex.Accounts.Invoice{
__meta__: #Ecto.Schema.Metadata<:loaded, "invoices">,
id: 2,
inserted_at: ~N[2018-01-03 08:00:00.000000],
paid_at: #DateTime<2018-02-01 08:00:00.000000Z>,
payment_method: "Paypal",
updated_at: ~N[2018-01-03 08:00:00.000000],
user: #Ecto.Association.NotLoaded,
user_id: 2
}
],
updated_at: ~N[2018-01-02 10:02:00.000000]
}
iex(15)> user.invoices
[
%Financex.Accounts.Invoice{
__meta__: #Ecto.Schema.Metadata<:loaded, "invoices">,
id: 2,
inserted_at: ~N[2018-01-03 08:00:00.000000],
paid_at: #DateTime<2018-02-01 08:00:00.000000Z>,
payment_method: "Paypal",
updated_at: ~N[2018-01-03 08:00:00.000000],
user: #Ecto.Association.NotLoaded,
user_id: 2
}
]
ActiveRecord includes के समान, preload संबद्ध invoices को लाएगा, जो उन्हें user.invoices को कॉल करते समय उपलब्ध कराएगा।
तुलना
एक बार फिर, ActiveRecord और Ecto के बीच की लड़ाई एक ज्ञात बिंदु पर समाप्त होती है: स्पष्टता। दोनों टूल डेवलपर्स को एसोसिएशन को आसानी से एक्सेस करने में सक्षम बनाते हैं, लेकिन जहां ActiveRecord इसे कम वर्बोज़ बनाता है, इसका परिणाम अप्रत्याशित व्यवहार हो सकता है। Ecto WYSIWYG प्रकार के दृष्टिकोण का पालन करता है, जो केवल वही करता है जो डेवलपर द्वारा परिभाषित क्वेरी में देखा जाता है।
Rails सभी एप्लिकेशन लेयर्स के लिए कैशिंग रणनीतियों का उपयोग करने और बढ़ावा देने के लिए प्रसिद्ध है। एक उदाहरण "रूसी गुड़िया" कैशिंग दृष्टिकोण का उपयोग करने के बारे में है, जो अपने कैशिंग तंत्र के लिए "N + 1 समस्या" पर पूरी तरह से निर्भर करता है ताकि वह अपना जादू चला सके।
सत्यापन
ActiveRecord में मौजूद अधिकांश सत्यापन Ecto में भी उपलब्ध हैं। यहां सामान्य सत्यापन की एक सूची है और कैसे ActiveRecord और Ecto दोनों उन्हें परिभाषित करते हैं:
| ActiveRecord | Ecto |
|---|---|
validates :title, presence: true |
validate_required(changeset, [:title]) |
validates :email, confirmation: true |
validate_confirmation(changeset, :email) |
validates :email, format: {with: /@/} |
validate_format(changeset, :email, ~r/@/) |
validates :start, exclusion: {in: %w(a b)} |
validate_exclusion(changeset, :start, ~w(a b)) |
validates :start, inclusion: {in: %w(a b)} |
validate_inclusion(changeset, :start, ~w(a b)) |
validates :terms_of_service, acceptance: true |
validate_acceptance(changeset, :terms_of_service) |
validates :password, length: {is: 6} |
validate_length(changeset, :password, is: 6) |
validates :age, numericality: {equal_to: 1} |
validate_number(changeset, :age, equal_to: 1) |
समापन
वहां आपके पास है: आवश्यक सेब बनाम संतरे की तुलना।
ActiveRecord डेटाबेस क्वेरी करने में आसानी पर केंद्रित है। इसकी अधिकांश विशेषताएं मॉडल क्लासेज़ पर ही केंद्रित हैं, जिससे डेवलपर्स को डेटाबेस की गहरी समझ की आवश्यकता नहीं होती, न ही ऐसे ऑपरेशन के प्रभाव की। ActiveRecord डिफ़ॉल्ट रूप से बहुत सी चीजें निहित रूप से करता है। हालांकि यह शुरू करना आसान बनाता है, यह पर्दे के पीछे क्या हो रहा है इसे समझना कठिन बनाता है और यह केवल तभी काम करता है जब आप "ActiveRecord तरीके" का पालन करते हैं।
Ecto, दूसरी ओर, स्पष्टता की आवश्यकता होती है जिसके परिणामस्वरूप अधिक वर्बोज़ कोड होता है। एक लाभ के रूप में, सब कुछ स्पॉटलाइट में है, पर्दे के पीछे कुछ भी नहीं, और आप अपना खुद का तरीका निर्दिष्ट कर सकते हैं।
दोनों के अपने फायदे हैं जो आपके दृष्टिकोण और वरीयता पर निर्भर करते हैं। तो सेब और संतरे की तुलना करने के बाद, हम इस BAT-tle के अंत में आते हैं। लगभग भूल गया था कि आपको बैटगर्ल का कोडनेम (1989 - 2001) बताऊं .... Oracle था। लेकिन आइए इसमें न जाएं। 😉