Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

ActiveRecordとEctoパート2

これは「ActiveRecordvs。Ecto」シリーズの第2部で、バットマンとバットガールがデータベースのクエリをめぐって争い、リンゴとオレンジを比較します。

ActiveRecordとEctoのパート1でデータベーススキーマと移行を調べた後、この投稿では、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 payment_method payed_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 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 フィールドの名前は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">

エクト

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を使用する場合、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]]
=> #<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">]>

エクト

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">]>

エクト

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への接続であるキーワードが使用されます 句。生成されたSQLクエリは非常に似ていますが、両方のツールがそこに到達する方法にはいくつかの重要な違いがあります。

ActiveRecordはpaid_at: nilを変換します paid_at IS NULLへの引数 SQLステートメントが自動的に。 Ectoを使用して同じ出力を取得するには、開発者はis_nil()を呼び出して、意図をより明確にする必要があります。 。

強調すべきもう1つの違いは、関数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が渡されます 引数として構造体。このアプローチにより、次のセクションの主題であるEctoでのクエリ作成が可能になります。

クエリ構成

データベースクエリの最も強力な側面の1つは、構成です。複数の条件を含む方法でクエリを記述します。

生のSQLクエリを作成している場合は、おそらく何らかの連結を使用することを意味します。 2つの条件があると想像してください:

  1. not_paid = 'paid_at IS NOT NULL'
  2. paid_with_paypal = 'payment_method = "Paypal"'

生のSQLを使用してこれら2つの条件を組み合わせるには、次のようなものを使用してそれらを連結する必要があることを意味します。

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">]>

エクト

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は開発者がクエリの作成にもう少し費やす必要があります。いつものように、バットガール(孤児、カサンドラ・カインのアイデンティティを持つミュート)または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]]
=> #<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">]>

エクト

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の例では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]]
=> #<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">]>

エクト

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を熱心にロードします ユーザーをフェッチするときの関連付け(示されている2つのSQLクエリに見られるように)

エクト

すでにお気づきかもしれませんが、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のようなアプローチに従います。これは、開発者が定義したクエリで見られるものだけを実行します。

Railsは、アプリケーションのすべての異なるレイヤーにキャッシュ戦略を使用および促進することでよく知られています。 1つの例は、「ロシア人形」キャッシングアプローチの使用に関するものです。このアプローチは、キャ​​ッシングメカニズムが魔法を実行するために「N+1問題」に完全に依存しています。

検証

ActiveRecordに存在するほとんどの検証は、Ectoでも利用できます。一般的な検証のリストと、ActiveRecordとEctoの両方がそれらを定義する方法は次のとおりです。

ActiveRecord エクト
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年)は....オラクルだったと言うのをほとんど忘れていました。しかし、それには入りません。 😉


  1. JavaScriptの2つの配列のisSubset

    2つのリテラル配列を受け取るJavaScript関数を作成する必要があります。関数は、これらのことを念頭に置いて、2番目の配列が最初の配列のサブセットであるかどうかを判断する必要があります- array1のすべての値はarray2で定義する必要があります array1に重複する値が存在する場合は、array2でも考慮に入れる必要があります。 たとえば、arr1 =[a、a]およびarr2=[b、 a]の場合、 aは最初に2回表示されますが、2番目に1回だけ表示されるため、isSubsetはfalseです。 例 このためのコードは-になります const isSubset =

  2. RDBMSの複合キー

    2つ以上の属性を持つ主キーは、複合キーと呼ばれます。 2つ以上の列の組み合わせです。 例 -にすることができます ここでの複合キーはOrderIDです およびProductID − {OrderID、ProductID} 別の例を見てみましょう- StudentID StudentEnrollNo StudentMarks StudentPercentage S001 0721722 570 90 S002 0721790 490 80 S003