এটি "ActiveRecord বনাম Ecto" সিরিজের দ্বিতীয় অংশ, যেখানে ব্যাটম্যান এবং ব্যাটগার্ল ডাটাবেস অনুসন্ধানের জন্য লড়াই করে এবং আমরা আপেল এবং কমলার তুলনা করি।
ActiveRecord বনাম Ecto পার্ট ওয়ানে ডাটাবেস স্কিমা এবং মাইগ্রেশন খোঁজার পরে, এই পোস্টটি কভার করে যে কিভাবে ActiveRecord এবং Ecto উভয়ই ডেভেলপারদের ডেটাবেস অনুসন্ধান করতে সক্ষম করে এবং একই প্রয়োজনীয়তার সাথে কাজ করার সময় ActiveRecord এবং Ecto উভয়েরই তুলনা করা হয়। পথে, আমরা ব্যাটগার্লের 1989-2011 পরিচয়ও খুঁজে বের করব৷
বীজ ডেটা
চল শুরু করি! এই সিরিজের প্রথম পোস্টে সংজ্ঞায়িত ডাটাবেস কাঠামোর উপর ভিত্তি করে, ধরে নিন users
এবং invoices
সারণীতে নিম্নলিখিত ডেটা সংরক্ষিত থাকে:
ব্যবহারকারীরা
id | পূর্ণ_নাম | ইমেল | created_at* | updated_at |
---|---|---|---|---|
1 | বেট কেন | [email protected] | 2018-01-01 10:01:00 | 2018-01-01 10:01:00 |
2 | বারবারা গর্ডন | [email protected] | 2018-01-02 10:02:00 | 2018-01-02 10:02:00 |
3 | ক্যাসান্দ্রা কেইন | [email protected] | 2018-01-03 10:03:00 | 2018-01-03 10:03:00 |
4 | স্টেফানি ব্রাউন | [email protected] | 2018-01-04 10:04:00 | 2018-01-04 10:04:00 |
* ActiveRecord এর created_at
ক্ষেত্রের নাম inserted_at
ডিফল্টরূপে Ecto-তে।
চালান
id | user_id | পেমেন্ট_পদ্ধতি | paid_at | created_at* | updated_at |
---|---|---|---|---|---|
1 | 1 | ক্রেডিট কার্ড | 2018-02-01 08:00:00 | 2018-01-02 08:00:00 | 2018-01-02 08:00:00 |
2 | 2 | পেপাল | 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
ক্ষেত্রের নাম inserted_at
ডিফল্টরূপে Ecto-তে।
এই পোস্টের মাধ্যমে সম্পাদিত প্রশ্নগুলি অনুমান করে যে উপরের ডেটা ডাটাবেসে সংরক্ষিত আছে, তাই এটি পড়ার সময় এই তথ্যটি মনে রাখবেন৷
এর প্রাথমিক কী ব্যবহার করে আইটেম খুঁজুন
এর প্রাথমিক কী ব্যবহার করে ডাটাবেস থেকে একটি রেকর্ড পাওয়ার সাথে শুরু করা যাক।
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]]
=> #<User id: 1, full_name: "Bette Kane", email: "[email protected]", created_at: "2018-01-01 10:01:00", updated_at: "2018-01-01 10:01:00">
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: "[email protected]",
full_name: "Bette Kane",
id: 1,
inserted_at: ~N[2018-01-01 10:01:00.000000],
invoices: #Ecto.Association.NotLoaded<association :invoices is not loaded>,
updated_at: ~N[2018-01-01 10:01:00.000000]
}
তুলনা
উভয় ক্ষেত্রেই বেশ মিল। ActiveRecord নির্ভর করে find
এর উপর User
ক্লাস পদ্ধতি মডেল ক্লাস। এর মানে হল প্রতিটি ActiveRecord চাইল্ড ক্লাসের নিজস্ব find
আছে এটিতে পদ্ধতি।
Ecto একটি ভিন্ন পদ্ধতি ব্যবহার করে, ম্যাপিং স্তর এবং ডোমেনের মধ্যে মধ্যস্থতাকারী হিসাবে রিপোজিটরি ধারণার উপর নির্ভর করে। Ecto ব্যবহার করার সময়, users
কিভাবে নিজেকে খুঁজে বের করতে হয় সে সম্পর্কে মডিউলের কোন জ্ঞান নেই। এই ধরনের দায়িত্ব Repo
-এ উপস্থিত রয়েছে মডিউল, যা এটিকে ডাটাস্টোরের নীচে ম্যাপ করতে সক্ষম, যা আমাদের ক্ষেত্রে পোস্টগ্রেস।
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]]
=> #<ActiveRecord::Relation [#<User id: 1, full_name: "Bette Kane", email: "[email protected]", created_at: "2018-01-01 10:01:00", updated_at: "2018-01-01 10:01:00">, #<User id: 2, full_name: "Barbara Gordon", email: "[email protected]", created_at: "2018-01-02 10:02:00", updated_at: "2018-01-02 10:02:00">, #<User id: 3, full_name: "Cassandra Cain", email: "[email protected]", created_at: "2018-01-03 10:03:00", updated_at: "2018-01-03 10:03:00">, #<User id: 4, full_name: "Stephanie Brown", email: "[email protected]", created_at: "2018-01-04 10:04:00", updated_at: "2018-01-04 10:04:00">]>
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: "[email protected]",
full_name: "Bette Kane",
id: 1,
inserted_at: ~N[2018-01-01 10:01:00.000000],
invoices: #Ecto.Association.NotLoaded<association :invoices is not loaded>,
updated_at: ~N[2018-01-01 10:01:00.000000]
},
%Financex.Accounts.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
email: "[email protected]",
full_name: "Barbara Gordon",
id: 2,
inserted_at: ~N[2018-01-02 10:02:00.000000],
invoices: #Ecto.Association.NotLoaded<association :invoices is not loaded>,
updated_at: ~N[2018-01-02 10:02:00.000000]
},
%Financex.Accounts.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
email: "[email protected]",
full_name: "Cassandra Cain",
id: 3,
inserted_at: ~N[2018-01-03 10:03:00.000000],
invoices: #Ecto.Association.NotLoaded<association :invoices is not loaded>,
updated_at: ~N[2018-01-03 10:03:00.000000]
},
%Financex.Accounts.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
email: "[email protected]",
full_name: "Stephanie Brown",
id: 4,
inserted_at: ~N[2018-01-04 10:04:00.000000],
invoices: #Ecto.Association.NotLoaded<association :invoices is not loaded>,
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]]
=> #<ActiveRecord::Relation [#<Invoice id: 3, user_id: 3, payment_method: nil, paid_at: nil, created_at: "2018-01-04 08:00:00", updated_at: "2018-01-04 08:00:00">, #<Invoice id: 4, user_id: 4, payment_method: nil, paid_at: nil, created_at: "2018-01-05 08:00:00", updated_at: "2018-01-05 08:00:00">]>
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<association :user is not loaded>,
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<association :user is not loaded>,
user_id: 4
}
]
তুলনা
উভয় উদাহরণেই, where
কীওয়ার্ড ব্যবহার করা হয়, যা SQL WHERE
-এর সাথে সংযোগ ধারা যদিও জেনারেট করা এসকিউএল ক্যোয়ারীগুলো অনেকটা একই রকম, উভয় টুল কিভাবে সেখানে পৌঁছায় তাতে কিছু গুরুত্বপূর্ণ পার্থক্য রয়েছে।
ActiveRecord paid_at: nil
রূপান্তরিত করে paid_at IS NULL
-এর যুক্তি স্বয়ংক্রিয়ভাবে SQL বিবৃতি। Ecto ব্যবহার করে একই আউটপুট পেতে, বিকাশকারীদের is_nil()
কল করে তাদের অভিপ্রায় সম্পর্কে আরও স্পষ্ট হতে হবে .
হাইলাইট করা আরেকটি পার্থক্য হল ফাংশন where
এর "বিশুদ্ধ" আচরণ ইক্টোতে where
কল করার সময় একা ফাংশন, এটি ডাটাবেসের সাথে ইন্টারঅ্যাক্ট করে না। where
এর রিটার্ন ফাংশন হল একটি Ecto.Query
গঠন:
iex(20)> where(Invoice, [i], is_nil(i.paid_at))
#Ecto.Query<from i in Financex.Accounts.Invoice, where: is_nil(i.paid_at)>
ডাটাবেসটি তখনই স্পর্শ করা হয় যখন Repo.all()
ফাংশন বলা হয়, Ecto.Query
পাস করে আর্গুমেন্ট হিসাবে struct. এই পদ্ধতিটি Ecto-তে ক্যোয়ারী কম্পোজিশনের অনুমতি দেয়, যা পরবর্তী বিভাগের বিষয়।
কোয়েরি রচনা
ডাটাবেস প্রশ্নের সবচেয়ে শক্তিশালী দিকগুলির মধ্যে একটি হল রচনা। এটি একটি ক্যোয়ারীকে এমনভাবে বর্ণনা করে যাতে একাধিক শর্ত থাকে৷
৷আপনি যদি কাঁচা এসকিউএল কোয়েরি তৈরি করে থাকেন, তাহলে এর মানে হল আপনি সম্ভবত কোনো ধরনের সংমিশ্রণ ব্যবহার করবেন। কল্পনা করুন আপনার দুটি শর্ত আছে:
not_paid = 'paid_at IS NOT NULL'
paid_with_paypal = 'payment_method = "Paypal"'
কাঁচা এসকিউএল ব্যবহার করে এই দুটি শর্ত একত্রিত করার জন্য, এর অর্থ হল আপনাকে অনুরূপ কিছু ব্যবহার করে সেগুলিকে সংযুক্ত করতে হবে:
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]]
=> #<ActiveRecord::Relation [#<Invoice id: 2, user_id: 2, payment_method: "Paypal", paid_at: "2018-02-01 08:00:00", created_at: "2018-01-03 08:00:00", updated_at: "2018-01-03 08:00:00">]>
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<association :user is not loaded>,
user_id: 2
}
]
তুলনা
উভয় প্রশ্নই একই প্রশ্নের উত্তর দিচ্ছে:"কোন চালানগুলি অর্থপ্রদান করা হয়েছিল এবং Paypal ব্যবহার করা হয়েছিল?"৷
ইতিমধ্যে প্রত্যাশিত হিসাবে, ActiveRecord ক্যোয়ারী রচনা করার একটি আরও সংক্ষিপ্ত উপায় অফার করে (উদাহরণস্বরূপ), যখন Ecto-এর জন্য ডেভেলপারদের ক্যোয়ারী লেখার জন্য একটু বেশি খরচ করতে হবে। যথারীতি, ব্যাটগার্ল (অনাথ, ক্যাসান্দ্রা কেইন পরিচয়ের সাথে একজন নিঃশব্দ) বা অ্যাক্টিভরেকর্ড শব্দের মতো নয়৷
উপরে দেখানো 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]]
=> #<ActiveRecord::Relation [#<Invoice id: 4, user_id: 4, payment_method: nil, paid_at: nil, created_at: "2018-01-05 08:00:00", updated_at: "2018-01-05 08:00:00">, #<Invoice id: 3, user_id: 3, payment_method: nil, paid_at: nil, created_at: "2018-01-04 08:00:00", updated_at: "2018-01-04 08:00:00">, #<Invoice id: 2, user_id: 2, payment_method: "Paypal", paid_at: "2018-02-01 08:00:00", created_at: "2018-01-03 08:00:00", updated_at: "2018-01-03 08:00:00">, #<Invoice id: 1, user_id: 1, payment_method: "Credit Card", paid_at: "2018-02-01 08:00:00", created_at: "2018-01-02 08:00:00", updated_at: "2018-01-02 08:00:00">]>
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<association :user is not loaded>,
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<association :user is not loaded>,
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<association :user is not loaded>,
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<association :user is not loaded>,
user_id: 1
}
]
তুলনা
একটি ক্যোয়ারীতে ক্রম যোগ করা উভয় টুলেই সোজা।
যদিও Ecto উদাহরণটি একটি invoices
ব্যবহার করে প্রথম প্যারামিটার হিসাবে, order_by
ফাংশন Ecto.Query
ও গ্রহণ করে structs, যা 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]]
=> #<ActiveRecord::Relation [#<Invoice id: 1, user_id: 1, payment_method: "Credit Card", paid_at: "2018-02-01 08:00:00", created_at: "2018-01-02 08:00:00", updated_at: "2018-01-02 08:00:00">, #<Invoice id: 2, user_id: 2, payment_method: "Paypal", paid_at: "2018-02-01 08:00:00", created_at: "2018-01-03 08:00:00", updated_at: "2018-01-03 08:00:00">]>
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<association :user is not loaded>,
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<association :user is not loaded>,
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]]
=> #<User id: 2, full_name: "Barbara Gordon", email: "[email protected]", created_at: "2018-01-02 10:02:00", updated_at: "2018-01-02 10:02:00">
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]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Invoice id: 2, user_id: 2, payment_method: "Paypal", paid_at: "2018-02-01 08:00:00", created_at: "2018-01-03 08:00:00", updated_at: "2018-01-03 08:00:00">]>
উপরের উদাহরণটি দেখায় যে 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]]
=> #<User id: 2, full_name: "Barbara Gordon", email: "[email protected]", created_at: "2018-01-02 10:02:00", updated_at: "2018-01-02 10:02:00">
irb(main):023:0> user.invoices
=> #<ActiveRecord::Associations::CollectionProxy [#<Invoice id: 2, user_id: 2, payment_method: "Paypal", paid_at: "2018-02-01 08:00:00", created_at: "2018-01-03 08:00:00", updated_at: "2018-01-03 08:00:00">]>
এই ক্ষেত্রে, ActiveRecord invoices
লোড করে ব্যবহারকারীকে আনার সময় অ্যাসোসিয়েশন (যেমন দুটি এসকিউএল কোয়েরিতে দেখানো হয়েছে)।
Ecto
আপনি ইতিমধ্যে লক্ষ্য করেছেন যে, Ecto সত্যিই যাদু বা অন্তর্নিহিততা পছন্দ করে না। এর জন্য ডেভেলপারদের তাদের উদ্দেশ্য সম্পর্কে স্পষ্ট হতে হবে।
চলুন user.invoices
ব্যবহার করার একই পদ্ধতির চেষ্টা করা যাক Ecto এর সাথে:
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: "[email protected]",
full_name: "Barbara Gordon",
id: 2,
inserted_at: ~N[2018-01-02 10:02:00.000000],
invoices: #Ecto.Association.NotLoaded<association :invoices is not loaded>,
updated_at: ~N[2018-01-02 10:02:00.000000]
}
iex(8)> user.invoices
#Ecto.Association.NotLoaded<association :invoices is not loaded>
ফলাফল হল একটি Ecto.Association.NotLoaded
. তেমন দরকারী নয়৷
চালানগুলিতে অ্যাক্সেস পেতে, একজন বিকাশকারীকে preload
ব্যবহার করে Ecto কে সে সম্পর্কে জানাতে হবে ফাংশন:
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: "[email protected]",
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<association :user is not loaded>,
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<association :user is not loaded>,
user_id: 2
}
]
একইভাবে ActiveRecord includes
, যুক্ত invoices
আনয়ন সহ প্রিলোড , যা user.invoices
কল করার সময় তাদের উপলব্ধ করবে .
তুলনা
আবারও, ActiveRecord এবং Ecto-এর মধ্যে যুদ্ধ একটি পরিচিত-বিন্দু দিয়ে শেষ হয়:স্পষ্টতা। উভয় সরঞ্জামই ডেভেলপারদের সহজে অ্যাসোসিয়েশন অ্যাক্সেস করতে সক্ষম করে, কিন্তু যখন ActiveRecord এটিকে কম ভার্বোস করে তোলে, তখন এর ফলাফলে অপ্রত্যাশিত আচরণ হতে পারে। Ecto WYSIWYG ধরনের পদ্ধতি অনুসরণ করে, যা শুধুমাত্র ডেভেলপার দ্বারা সংজ্ঞায়িত প্রশ্নে যা দেখা যায় তা করে।
অ্যাপ্লিকেশনের বিভিন্ন স্তরে ক্যাশিং কৌশলগুলি ব্যবহার এবং প্রচার করার জন্য রেলগুলি সুপরিচিত। একটি উদাহরণ হল "রাশিয়ান পুতুল" ক্যাশিং পদ্ধতির ব্যবহার সম্পর্কে, যা সম্পূর্ণরূপে "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) ছিল .... ওরাকল। তবে এর মধ্যে না যাওয়া যাক। 😉