window.pipedriveLeadboosterConfig={です。 ベース:'leadbooster-chat.pipedrive.com'、 companyId:11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2、 } ;(function () { var w = window もし (w.LeadBooster) {なら console.warn('LeadBooster already exists') } else { w.LeadBooster = { {. q: [], on: function (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: 関数 (n) { { this.q.push({ t: 'o', n: n, h: h }) this.q.push({ t: 't', n: n }) }, } } })() クエリオブジェクトによるコードの最適化 - The Codest
The Codest
  • 会社概要
  • サービス
    • ソフトウェア開発
      • フロントエンド開発
      • バックエンド開発
    • Staff Augmentation
      • フロントエンド開発者
      • バックエンド開発者
      • データエンジニア
      • クラウドエンジニア
      • QAエンジニア
      • その他
    • アドバイザリー
      • 監査&コンサルティング
  • 産業
    • フィンテック&バンキング
    • E-commerce
    • アドテック
    • ヘルステック
    • 製造業
    • 物流
    • 自動車
    • アイオーティー
  • 価値
    • CEO
    • CTO
    • デリバリー・マネージャー
  • チーム
  • Case Studies
  • ノウハウ
    • ブログ
    • ミートアップ
    • ウェビナー
    • リソース
採用情報 連絡先
  • 会社概要
  • サービス
    • ソフトウェア開発
      • フロントエンド開発
      • バックエンド開発
    • Staff Augmentation
      • フロントエンド開発者
      • バックエンド開発者
      • データエンジニア
      • クラウドエンジニア
      • QAエンジニア
      • その他
    • アドバイザリー
      • 監査&コンサルティング
  • 価値
    • CEO
    • CTO
    • デリバリー・マネージャー
  • チーム
  • Case Studies
  • ノウハウ
    • ブログ
    • ミートアップ
    • ウェビナー
    • リソース
採用情報 連絡先
戻る矢印 戻る
2019-03-08
ソフトウェア開発

クエリオブジェクトによるコードの最適化

The Codest

トマシュ・シュカラデク

開発アーキテクト

仕事で、オーバーロードされたモデルやコントローラ内の膨大な数の呼び出しに何度も遭遇している可能性はかなり高いでしょう。この記事では、Rails環境での知識に基づいて、この問題の簡単な解決策を提案します。

railsアプリケーションの非常に重要な側面は、冗長な依存関係の数を最小限に抑えることです。そのため、Rails環境全体が最近、サービスオブジェクトのアプローチとPORO(Pure Old Ruby Object)メソッドの使用を推進しています。このようなソリューションの使用方法については、以下を参照してください。 これ.この記事では、ステップ・バイ・ステップでコンセプトを解き、問題に適応させていく。

問題点

仮想的なアプリケーションでは、複雑なトランザクションシステムを扱っている。各トランザクションを表す我々のモデルは、データを取得するのに役立つスコープのセットを持っています。これは1つの場所で見つけることができるため、素晴らしい仕事の円滑化です。しかし、これは長くは続かない。アプリケーションの開発とともに プロジェクト はますます複雑になっている。スコープはもはや単純な "WHERE "参照ではなくなり、データが不足し、リレーションシップをロードし始める。しばらくすると、それはミラーの複雑なシステムを思い起こさせる。さらに悪いことに、複数行のラムダをどうやればいいのかわからない!

以下に、すでに拡張されたアプリケーション・モデルを示す。決済システムのトランザクションが格納されています。下の例でわかるように

クラス Transaction  { where(visible: true) }.

  scope(:active, lambda do
    結合(<<-SQL
      LEFT OUTER JOIN source ON transactions.source_id = source.id
      AND source.accepted_at IS NOT NULL
    SQL
  end)
終了

モデルもそうですが、プロジェクトの規模が大きくなるにつれて、コントローラーも大きくなっていきます。下の例を見てみよう:

class TransactionsController < ApplicationController
  def index
    @transactions = Transaction.for_publishers
                                   .active
                                   .visible
                                   .joins("LEFT JOIN withdrawal_items ON withdrawal_items.transaction_id = transactions.id")
                                   .joins("LEFT JOIN withdrawals ON withdrawals.id = withdrawal_items.withdrawal_id OR
 (withdrawals.id = source.resource_id AND source.resource_type = 'Withdrawal')")
                                   .order(:created_at)
                                   .page(params[:page])
                                   .per(params[:page])
    トランザクション = apply_filters(@transactions)
  終了
終了

ここでは、何行もの連鎖メソッドと、多くの場所で実行したくない追加の結合が並んでいるのがわかる。添付されたデータは後でapply_filtersメソッドで使われ、GETパラメータに基づいて適切なデータフィルタリングを追加する。もちろん、これらの参照のいくつかをスコープに移すことはできるが、実際に解決しようとしているのはこの問題ではないだろうか?

ソリューション

私たちが抱えている問題はすでに分かっているのだから、これを解決しなければならない。冒頭のリファレンスに基づき、ここではPOROアプローチを使用する。この正確なケースでは、このアプローチはクエリーオブジェクトと呼ばれ、サービスオブジェクトのコンセプトを発展させたものである。

プロジェクトのappsディレクトリにservicesという新しいディレクトリを作りましょう。そこに トランザクションクエリ.

クラス TransactionsQuery
終了

次のステップとして、オブジェクトのデフォルト・コール・パスを作成するイニシャライザーを作成する必要があります。

クラス TransactionsQuery
  def initialize(scope = Transaction.all)
    スコープ = スコープ
  終了
終わり

このおかげで、リレーションシップをアクティブレコードから施設に転送することができます。これで、提示されたコントローラでのみ必要とされる、すべてのスコープをクラスに転送することができます。

クラス TransactionsQuery
  def initialize(scope = Transaction.all)
    スコープ = スコープ
  終了

  プライベート

  def active(scope)
    scope.joins(<<-SQL
      LEFT OUTER JOIN source ON transactions.source_id = source.id
      AND source.accepted_at IS NOT NULL
    SQL
  終了

  def visible(scope)
    scope.where(visible: true)
  終了

  def for_publishers(scope)
    scope.select("transactions.*")
         .joins(:account)
         .where("accounts.owner_type = 'Publisher'")
         .joins("JOIN publishers ON owner_id = publishers.id")
  終了
終了

最も重要な部分、つまりデータを1つの文字列に集め、インターフェイスをパブリックにすることがまだできていない。すべてをくっつけるメソッドの名前は「コール」とする。

本当に重要なのは、@scopeインスタンス変数を使うことだ。

クラス TransactionsQuery
  ...
  def call
    可視(@スコープ)
        .then(&method(:active))
        .then(&method(:for_publishers))
        .order(:created_at)
  終了

  非公開
  ...
終了

クラス全体は次のように発表している:

クラス TransactionsQuery
  def initialize(scope = Transaction.all)
    スコープ = スコープ
  終了

  def call
    visible(@scope)
        .then(&method(:active))
        .then(&method(:for_publishers))
        .order(:created_at)
  終了

  プライベート

  def active(scope)
    scope.joins(<<-SQL
      LEFT OUTER JOIN source ON transactions.source_id = source.id
      AND source.accepted_at IS NOT NULL
    SQL
  終了

  def visible(scope)
    scope.where(visible: true)
  終了

  def for_publishers(scope)
    scope.select("transactions.*")
         .joins(:account)
         .where("accounts.owner_type = 'Publisher'")
         .joins("JOIN publishers ON owner_id = publishers.id")
  終了
終了

クリーンアップ後、モデルの見た目は確実に軽くなった。ここでは、データの検証と他のモデルとの関係だけに集中する。

クラス Transaction < ActiveRecord::Base
  所属 :口座
  has_one :引き出し項目
終了

コントローラはすでに私たちの解決策を実装しています。追加のクエリはすべて別のクラスに移動しました。しかし、モデルになかった呼び出しは未解決のままです。いくつかの変更を経て、インデックスアクションは次のようになりました:

class TransactionsController < ApplicationController
  def index
    @transactions = TransactionsQuery.new
                                     .call
                                     .joins("LEFT JOIN withdrawal_items ON withdrawal_items.accounting_event_id = transactions.id")
                                     .joins("LEFT JOIN withdrawals ON withdrawals.id = withdrawal_items.withdrawal_id OR
 (withdrawals.id = source.resource_id AND source.resource_type = 'Withdrawal')")
                                     .order(:created_at)
                                     .page(params[:page])
                                     .per(params[:page])
    トランザクション = apply_filters(@transactions)
  終了
終了

ソリューション

グッドプラクティスと慣例を実施する場合、与えられた問題の類似した発生をすべて置き換えることが良いアイデアかもしれません。したがって、SQLクエリをインデックス・アクションから別のクエリ・オブジェクトに移動します。これを TransactionsFilterableQuery クラスを用意する。私たちが準備する授業スタイルは、以下の授業で紹介するスタイルに似ています。 トランザクションクエリ.その一環として コード と呼ばれる複数行の文字列を使用して、より直感的に大規模な SQL クエリを記録できるようになります。 ヘレドック 解決策は以下にある:

クラス TransactionsFilterableQuery
  def initialize(scope = Transaction.all)
    スコープ = スコープ
  終了

  def call
    withdrawal(@scope).then(&method(:withdrawal_items))
  終了

  プライベート

  def withdrawal(scope)
    scope.joins(<<-SQL
      LEFT JOIN withdrawals ON withdrawals.id = withdrawal_items.withdrawal_id OR
      (withdrawals.id = source.resource_id AND source.resource_type = 'Withdrawal')
    SQL
  終了

  def withdrawal_items(scope)
    scope.joins(<<-SQL
      LEFT JOIN withdrawal_items ON withdrawal_items.accounting_event_id = transactions.id
    SQL
  終了
終了

コントローラーに変更があった場合は、クエリー・オブジェクトを追加することで行数を減らす。重要なのは、ページ分割を担当する部分以外はすべて切り離すことだ。

class TransactionsController < ApplicationController
  def index
    TransactionsFilterableQuery.new(scope).call.then do |scope|.
      TransactionsFilterableQuery.new(scope).call
    end.page(params[:page]).per(params[:page])

    トランザクション = apply_filters(@transactions)
  end
エンド

概要

クエリオブジェクトはSQLクエリを書くアプローチを大きく変える。ActiveRecordでは、すべてのビジネス・ロジックとデータベース・ロジックが一箇所にまとまっているため、モデル内にすべてのビジネス・ロジックとデータベース・ロジックを配置することが非常に簡単です。これは、小規模なアプリケーションでは非常に有効です。プロジェクトの複雑さが増すにつれて、ロジックを他の場所に設定していきます。同じクエリオブジェクトで、メンバーのクエリを特定の問題にグループ化することができます。

このおかげで、コードの継承が容易になり、またアヒルの型付けのおかげで、他のモデルでもこれらの解決策を使うことができる。この解決策の欠点は、コードの量が多くなり、責任が細分化されることである。しかし、このような課題に挑戦するかどうかは、私たち次第であり、太ったモデルにどれだけ悩まされるかは私たち次第である。

関連記事

ソフトウェア開発

将来を見据えたウェブ・アプリケーションの構築:The Codestのエキスパート・チームによる洞察

The Codestが、最先端技術を駆使してスケーラブルでインタラクティブなウェブアプリケーションを作成し、あらゆるプラットフォームでシームレスなユーザー体験を提供することにどのように秀でているかをご覧ください。The Codestの専門知識がどのようにデジタルトランスフォーメーションとビジネス...

ザ・コデスト
ソフトウェア開発

ラトビアを拠点とするソフトウェア開発企業トップ10社

ラトビアのトップソフトウェア開発企業とその革新的なソリューションについて、最新記事でご紹介します。ラトビアの技術リーダーたちがあなたのビジネスをどのように向上させるかをご覧ください。

thecodest
エンタープライズ&スケールアップ・ソリューション

Javaソフトウェア開発の要点:アウトソーシングを成功させるためのガイド

outsourcingのJavaソフトウェア開発を成功させるために不可欠なこのガイドを読んで、The Codestで効率性を高め、専門知識にアクセスし、プロジェクトを成功に導きましょう。

thecodest
ソフトウェア開発

ポーランドにおけるアウトソーシングの究極ガイド

ポーランドのoutsourcingの急増は、経済、教育、技術の進歩がITの成長とビジネス・フレンドリーな環境を促進していることによる。

ザ・コデスト
エンタープライズ&スケールアップ・ソリューション

IT監査ツール&テクニック完全ガイド

IT監査は、安全かつ効率的で、コンプライアンスに準拠したシステムを保証します。その重要性については、記事全文をお読みください。

The Codest
ヤクブ・ヤクボヴィッチ CTO & 共同創設者

ナレッジベースを購読して、IT部門の専門知識を常に最新の状態に保ちましょう。

    会社概要

    The Codest - ポーランドに技術拠点を持つ国際的なソフトウェア開発会社。

    イギリス - 本社

    • オフィス 303B, 182-184 High Street North E6 2JA
      イギリス、ロンドン

    ポーランド - ローカル・テック・ハブ

    • ファブリチュナ・オフィスパーク、アレハ
      ポコジュ18、31-564クラクフ
    • ブレイン・エンバシー, コンストルクトースカ
      11, 02-673 Warsaw, Poland

      The Codest

    • ホーム
    • 会社概要
    • サービス
    • Case Studies
    • ノウハウ
    • 採用情報
    • 辞書

      サービス

    • アドバイザリー
    • ソフトウェア開発
    • バックエンド開発
    • フロントエンド開発
    • Staff Augmentation
    • バックエンド開発者
    • クラウドエンジニア
    • データエンジニア
    • その他
    • QAエンジニア

      リソース

    • 外部ソフトウェア開発パートナーとの協力に関する事実と神話
    • 米国から欧州へ:アメリカの新興企業がヨーロッパへの移転を決断する理由
    • テックオフショア開発ハブの比較:テックオフショア ヨーロッパ(ポーランド)、ASEAN(フィリピン)、ユーラシア(トルコ)
    • CTOとCIOの課題は?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • ウェブサイト利用規約

    著作権 © 2025 by The Codest。無断複写・転載を禁じます。

    jaJapanese
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian ko_KRKorean es_ESSpanish nl_NLDutch etEstonian elGreek jaJapanese