Activeadmin custom filter fails












1















In activeadmin, we need to apply a kind of complicated filter on the index page of our Score model. Namely, we want to get the results in the Score table such that their ExportOrders have the selected DistributionChain. We start from the following models:



class Score < ApplicationRecord
has_and_belongs_to_many :export_orders, join_table: :scores_export_orders
end

class ExportOrder < ApplicationRecord
belongs_to :distribution_chain
has_and_belongs_to_many :scores, join_table: :scores_export_orders
end

class DistributionChain < ApplicationRecord
has_many :export_orders
end


In the schema.rb (excerpt):



  create_table "scores_export_orders", id: false, force: :cascade do |t|
t.integer "score_id"
t.integer "export_order_id"
t.index ["export_order_id"], name: "index_scores_export_orders_on_export_order_id", using: :btree
t.index ["score_id"], name: "index_scores_export_orders_on_score_id", using: :btree
end

create_table "export_orders", force: :cascade do |t|
t.integer "distribution_chain_id"
end


In Score Activeadmin:



ActiveAdmin.register Score
filter :delivery_distr_chain, as: :select, collection: DistributionChain.all
end


The scope of the filter is defined in the score model:



class Score < ApplicationRecord
...
scope :delivery_distr_chain, -> (input) {
self.joins(:export_orders).where('export_orders.distribution_chain_id = ?', input)
}

def self.ransackable_scopes(auth_object = nil)
[:delivery_distr_chain]
end
end


Following is the error:



ActionView::Template::Error (PG::DuplicateAlias: ERROR:  table name "scores_export_orders" specified more than once
: SELECT COUNT(DISTINCT count_column) FROM (SELECT DISTINCT "scores"."id" AS count_column FROM "scores" INNER JOIN "scores_export_orders" ON "scores_export_orders"."score_id" = "scores"."id" INNER JOIN "export_orders" ON "export_orders"."id" = "scores_export_orders"."export_order_id" LEFT JOIN scores_export_orders ON scores.id = scores_export_orders.score_id WHERE (NOT (scores_export_orders.score_id IS NULL)) AND (export_orders.distribution_chain_id = '2') LIMIT $1 OFFSET $2) subquery_for_count):
1: insert_tag renderer_for(:index)

activerecord (5.0.7) lib/active_record/connection_adapters/postgresql_adapter.rb:600:in `async_exec'


I sense that the query is not properly written. Can you tell us where we are failing?





EDIT: interesting to notice is the difference between executing the query in the rails console and within the scope in the Score model.



If I execute the same query in the rails console, it works without errors. The output of Score.joins(:export_orders).where('export_orders.distribution_chain_id = ?', '2').to_sql is:



"SELECT "scores".* FROM "scores" INNER JOIN "scores_export_orders" ON "scores_export_orders"."score_id" = "scores"."id" INNER JOIN "export_orders" ON "export_orders"."id" = "scores_export_orders"."export_order_id" WHERE (export_orders.distribution_chain_id = '2') ORDER BY "scores"."created_at" DESC"



whereas the exact same query fails in the scope in the Score model, and the .to_sql output is:



"SELECT DISTINCT "scores".* FROM "scores" INNER JOIN "scores_export_orders" ON "scores_export_orders"."score_id" = "scores"."id" INNER JOIN "export_orders" ON "export_orders"."id" = "scores_export_orders"."export_order_id" LEFT JOIN scores_export_orders ON scores.id = scores_export_orders.score_id WHERE (NOT (scores_export_orders.score_id IS NULL)) AND (export_orders.distribution_chain_id = '2') ORDER BY "scores"."id" desc"



The main difference lies in the left-join introduced by the scope: LEFT JOIN scores_export_orders ON scores.id = scores_export_orders.score_id, which lets specify the scores_export_orders table once more. I have no idea why this is introduced in there and not on the console...










share|improve this question





























    1















    In activeadmin, we need to apply a kind of complicated filter on the index page of our Score model. Namely, we want to get the results in the Score table such that their ExportOrders have the selected DistributionChain. We start from the following models:



    class Score < ApplicationRecord
    has_and_belongs_to_many :export_orders, join_table: :scores_export_orders
    end

    class ExportOrder < ApplicationRecord
    belongs_to :distribution_chain
    has_and_belongs_to_many :scores, join_table: :scores_export_orders
    end

    class DistributionChain < ApplicationRecord
    has_many :export_orders
    end


    In the schema.rb (excerpt):



      create_table "scores_export_orders", id: false, force: :cascade do |t|
    t.integer "score_id"
    t.integer "export_order_id"
    t.index ["export_order_id"], name: "index_scores_export_orders_on_export_order_id", using: :btree
    t.index ["score_id"], name: "index_scores_export_orders_on_score_id", using: :btree
    end

    create_table "export_orders", force: :cascade do |t|
    t.integer "distribution_chain_id"
    end


    In Score Activeadmin:



    ActiveAdmin.register Score
    filter :delivery_distr_chain, as: :select, collection: DistributionChain.all
    end


    The scope of the filter is defined in the score model:



    class Score < ApplicationRecord
    ...
    scope :delivery_distr_chain, -> (input) {
    self.joins(:export_orders).where('export_orders.distribution_chain_id = ?', input)
    }

    def self.ransackable_scopes(auth_object = nil)
    [:delivery_distr_chain]
    end
    end


    Following is the error:



    ActionView::Template::Error (PG::DuplicateAlias: ERROR:  table name "scores_export_orders" specified more than once
    : SELECT COUNT(DISTINCT count_column) FROM (SELECT DISTINCT "scores"."id" AS count_column FROM "scores" INNER JOIN "scores_export_orders" ON "scores_export_orders"."score_id" = "scores"."id" INNER JOIN "export_orders" ON "export_orders"."id" = "scores_export_orders"."export_order_id" LEFT JOIN scores_export_orders ON scores.id = scores_export_orders.score_id WHERE (NOT (scores_export_orders.score_id IS NULL)) AND (export_orders.distribution_chain_id = '2') LIMIT $1 OFFSET $2) subquery_for_count):
    1: insert_tag renderer_for(:index)

    activerecord (5.0.7) lib/active_record/connection_adapters/postgresql_adapter.rb:600:in `async_exec'


    I sense that the query is not properly written. Can you tell us where we are failing?





    EDIT: interesting to notice is the difference between executing the query in the rails console and within the scope in the Score model.



    If I execute the same query in the rails console, it works without errors. The output of Score.joins(:export_orders).where('export_orders.distribution_chain_id = ?', '2').to_sql is:



    "SELECT "scores".* FROM "scores" INNER JOIN "scores_export_orders" ON "scores_export_orders"."score_id" = "scores"."id" INNER JOIN "export_orders" ON "export_orders"."id" = "scores_export_orders"."export_order_id" WHERE (export_orders.distribution_chain_id = '2') ORDER BY "scores"."created_at" DESC"



    whereas the exact same query fails in the scope in the Score model, and the .to_sql output is:



    "SELECT DISTINCT "scores".* FROM "scores" INNER JOIN "scores_export_orders" ON "scores_export_orders"."score_id" = "scores"."id" INNER JOIN "export_orders" ON "export_orders"."id" = "scores_export_orders"."export_order_id" LEFT JOIN scores_export_orders ON scores.id = scores_export_orders.score_id WHERE (NOT (scores_export_orders.score_id IS NULL)) AND (export_orders.distribution_chain_id = '2') ORDER BY "scores"."id" desc"



    The main difference lies in the left-join introduced by the scope: LEFT JOIN scores_export_orders ON scores.id = scores_export_orders.score_id, which lets specify the scores_export_orders table once more. I have no idea why this is introduced in there and not on the console...










    share|improve this question



























      1












      1








      1








      In activeadmin, we need to apply a kind of complicated filter on the index page of our Score model. Namely, we want to get the results in the Score table such that their ExportOrders have the selected DistributionChain. We start from the following models:



      class Score < ApplicationRecord
      has_and_belongs_to_many :export_orders, join_table: :scores_export_orders
      end

      class ExportOrder < ApplicationRecord
      belongs_to :distribution_chain
      has_and_belongs_to_many :scores, join_table: :scores_export_orders
      end

      class DistributionChain < ApplicationRecord
      has_many :export_orders
      end


      In the schema.rb (excerpt):



        create_table "scores_export_orders", id: false, force: :cascade do |t|
      t.integer "score_id"
      t.integer "export_order_id"
      t.index ["export_order_id"], name: "index_scores_export_orders_on_export_order_id", using: :btree
      t.index ["score_id"], name: "index_scores_export_orders_on_score_id", using: :btree
      end

      create_table "export_orders", force: :cascade do |t|
      t.integer "distribution_chain_id"
      end


      In Score Activeadmin:



      ActiveAdmin.register Score
      filter :delivery_distr_chain, as: :select, collection: DistributionChain.all
      end


      The scope of the filter is defined in the score model:



      class Score < ApplicationRecord
      ...
      scope :delivery_distr_chain, -> (input) {
      self.joins(:export_orders).where('export_orders.distribution_chain_id = ?', input)
      }

      def self.ransackable_scopes(auth_object = nil)
      [:delivery_distr_chain]
      end
      end


      Following is the error:



      ActionView::Template::Error (PG::DuplicateAlias: ERROR:  table name "scores_export_orders" specified more than once
      : SELECT COUNT(DISTINCT count_column) FROM (SELECT DISTINCT "scores"."id" AS count_column FROM "scores" INNER JOIN "scores_export_orders" ON "scores_export_orders"."score_id" = "scores"."id" INNER JOIN "export_orders" ON "export_orders"."id" = "scores_export_orders"."export_order_id" LEFT JOIN scores_export_orders ON scores.id = scores_export_orders.score_id WHERE (NOT (scores_export_orders.score_id IS NULL)) AND (export_orders.distribution_chain_id = '2') LIMIT $1 OFFSET $2) subquery_for_count):
      1: insert_tag renderer_for(:index)

      activerecord (5.0.7) lib/active_record/connection_adapters/postgresql_adapter.rb:600:in `async_exec'


      I sense that the query is not properly written. Can you tell us where we are failing?





      EDIT: interesting to notice is the difference between executing the query in the rails console and within the scope in the Score model.



      If I execute the same query in the rails console, it works without errors. The output of Score.joins(:export_orders).where('export_orders.distribution_chain_id = ?', '2').to_sql is:



      "SELECT "scores".* FROM "scores" INNER JOIN "scores_export_orders" ON "scores_export_orders"."score_id" = "scores"."id" INNER JOIN "export_orders" ON "export_orders"."id" = "scores_export_orders"."export_order_id" WHERE (export_orders.distribution_chain_id = '2') ORDER BY "scores"."created_at" DESC"



      whereas the exact same query fails in the scope in the Score model, and the .to_sql output is:



      "SELECT DISTINCT "scores".* FROM "scores" INNER JOIN "scores_export_orders" ON "scores_export_orders"."score_id" = "scores"."id" INNER JOIN "export_orders" ON "export_orders"."id" = "scores_export_orders"."export_order_id" LEFT JOIN scores_export_orders ON scores.id = scores_export_orders.score_id WHERE (NOT (scores_export_orders.score_id IS NULL)) AND (export_orders.distribution_chain_id = '2') ORDER BY "scores"."id" desc"



      The main difference lies in the left-join introduced by the scope: LEFT JOIN scores_export_orders ON scores.id = scores_export_orders.score_id, which lets specify the scores_export_orders table once more. I have no idea why this is introduced in there and not on the console...










      share|improve this question
















      In activeadmin, we need to apply a kind of complicated filter on the index page of our Score model. Namely, we want to get the results in the Score table such that their ExportOrders have the selected DistributionChain. We start from the following models:



      class Score < ApplicationRecord
      has_and_belongs_to_many :export_orders, join_table: :scores_export_orders
      end

      class ExportOrder < ApplicationRecord
      belongs_to :distribution_chain
      has_and_belongs_to_many :scores, join_table: :scores_export_orders
      end

      class DistributionChain < ApplicationRecord
      has_many :export_orders
      end


      In the schema.rb (excerpt):



        create_table "scores_export_orders", id: false, force: :cascade do |t|
      t.integer "score_id"
      t.integer "export_order_id"
      t.index ["export_order_id"], name: "index_scores_export_orders_on_export_order_id", using: :btree
      t.index ["score_id"], name: "index_scores_export_orders_on_score_id", using: :btree
      end

      create_table "export_orders", force: :cascade do |t|
      t.integer "distribution_chain_id"
      end


      In Score Activeadmin:



      ActiveAdmin.register Score
      filter :delivery_distr_chain, as: :select, collection: DistributionChain.all
      end


      The scope of the filter is defined in the score model:



      class Score < ApplicationRecord
      ...
      scope :delivery_distr_chain, -> (input) {
      self.joins(:export_orders).where('export_orders.distribution_chain_id = ?', input)
      }

      def self.ransackable_scopes(auth_object = nil)
      [:delivery_distr_chain]
      end
      end


      Following is the error:



      ActionView::Template::Error (PG::DuplicateAlias: ERROR:  table name "scores_export_orders" specified more than once
      : SELECT COUNT(DISTINCT count_column) FROM (SELECT DISTINCT "scores"."id" AS count_column FROM "scores" INNER JOIN "scores_export_orders" ON "scores_export_orders"."score_id" = "scores"."id" INNER JOIN "export_orders" ON "export_orders"."id" = "scores_export_orders"."export_order_id" LEFT JOIN scores_export_orders ON scores.id = scores_export_orders.score_id WHERE (NOT (scores_export_orders.score_id IS NULL)) AND (export_orders.distribution_chain_id = '2') LIMIT $1 OFFSET $2) subquery_for_count):
      1: insert_tag renderer_for(:index)

      activerecord (5.0.7) lib/active_record/connection_adapters/postgresql_adapter.rb:600:in `async_exec'


      I sense that the query is not properly written. Can you tell us where we are failing?





      EDIT: interesting to notice is the difference between executing the query in the rails console and within the scope in the Score model.



      If I execute the same query in the rails console, it works without errors. The output of Score.joins(:export_orders).where('export_orders.distribution_chain_id = ?', '2').to_sql is:



      "SELECT "scores".* FROM "scores" INNER JOIN "scores_export_orders" ON "scores_export_orders"."score_id" = "scores"."id" INNER JOIN "export_orders" ON "export_orders"."id" = "scores_export_orders"."export_order_id" WHERE (export_orders.distribution_chain_id = '2') ORDER BY "scores"."created_at" DESC"



      whereas the exact same query fails in the scope in the Score model, and the .to_sql output is:



      "SELECT DISTINCT "scores".* FROM "scores" INNER JOIN "scores_export_orders" ON "scores_export_orders"."score_id" = "scores"."id" INNER JOIN "export_orders" ON "export_orders"."id" = "scores_export_orders"."export_order_id" LEFT JOIN scores_export_orders ON scores.id = scores_export_orders.score_id WHERE (NOT (scores_export_orders.score_id IS NULL)) AND (export_orders.distribution_chain_id = '2') ORDER BY "scores"."id" desc"



      The main difference lies in the left-join introduced by the scope: LEFT JOIN scores_export_orders ON scores.id = scores_export_orders.score_id, which lets specify the scores_export_orders table once more. I have no idea why this is introduced in there and not on the console...







      sql ruby-on-rails postgresql filter activeadmin






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 14 '18 at 16:43







      Simon

















      asked Nov 13 '18 at 18:55









      SimonSimon

      313214




      313214
























          1 Answer
          1






          active

          oldest

          votes


















          0














          Solved. I was not aware of the fact that I already declare a scope in my ActiveAdmin for Scores, which was the following:



          scope :exported, -> { joins("LEFT JOIN scores_export_orders ON scores.id = scores_export_orders.score_id").where.not("scores_export_orders.score_id IS NULL").distinct



          called in ActiveAdmin as



            controller do
          def scoped_collection
          end_of_association_chain.exported
          end
          ...
          end




          Now I've rewritten the code such that I have in my score model:



          class Score < ApplicationRecord
          ...
          scope :exported, -> { joins("INNER JOIN scores_export_orders ON scores.id = scores_export_orders.score_id INNER JOIN export_orders ON export_orders.id = scores_export_orders.export_order_id").where.not("scores_export_orders.score_id IS NULL").distinct }
          ...
          scope :delivery_distr_chain, -> (input) {
          self.where('export_orders.distribution_chain_id = ?', input)
          }

          def self.ransackable_scopes(auth_object = nil)
          [:delivery_distr_chain]
          end
          end


          so that I have the double join on the exported scope and I don't need to join again on the filter scope.






          share|improve this answer























            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53287766%2factiveadmin-custom-filter-fails%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            0














            Solved. I was not aware of the fact that I already declare a scope in my ActiveAdmin for Scores, which was the following:



            scope :exported, -> { joins("LEFT JOIN scores_export_orders ON scores.id = scores_export_orders.score_id").where.not("scores_export_orders.score_id IS NULL").distinct



            called in ActiveAdmin as



              controller do
            def scoped_collection
            end_of_association_chain.exported
            end
            ...
            end




            Now I've rewritten the code such that I have in my score model:



            class Score < ApplicationRecord
            ...
            scope :exported, -> { joins("INNER JOIN scores_export_orders ON scores.id = scores_export_orders.score_id INNER JOIN export_orders ON export_orders.id = scores_export_orders.export_order_id").where.not("scores_export_orders.score_id IS NULL").distinct }
            ...
            scope :delivery_distr_chain, -> (input) {
            self.where('export_orders.distribution_chain_id = ?', input)
            }

            def self.ransackable_scopes(auth_object = nil)
            [:delivery_distr_chain]
            end
            end


            so that I have the double join on the exported scope and I don't need to join again on the filter scope.






            share|improve this answer




























              0














              Solved. I was not aware of the fact that I already declare a scope in my ActiveAdmin for Scores, which was the following:



              scope :exported, -> { joins("LEFT JOIN scores_export_orders ON scores.id = scores_export_orders.score_id").where.not("scores_export_orders.score_id IS NULL").distinct



              called in ActiveAdmin as



                controller do
              def scoped_collection
              end_of_association_chain.exported
              end
              ...
              end




              Now I've rewritten the code such that I have in my score model:



              class Score < ApplicationRecord
              ...
              scope :exported, -> { joins("INNER JOIN scores_export_orders ON scores.id = scores_export_orders.score_id INNER JOIN export_orders ON export_orders.id = scores_export_orders.export_order_id").where.not("scores_export_orders.score_id IS NULL").distinct }
              ...
              scope :delivery_distr_chain, -> (input) {
              self.where('export_orders.distribution_chain_id = ?', input)
              }

              def self.ransackable_scopes(auth_object = nil)
              [:delivery_distr_chain]
              end
              end


              so that I have the double join on the exported scope and I don't need to join again on the filter scope.






              share|improve this answer


























                0












                0








                0







                Solved. I was not aware of the fact that I already declare a scope in my ActiveAdmin for Scores, which was the following:



                scope :exported, -> { joins("LEFT JOIN scores_export_orders ON scores.id = scores_export_orders.score_id").where.not("scores_export_orders.score_id IS NULL").distinct



                called in ActiveAdmin as



                  controller do
                def scoped_collection
                end_of_association_chain.exported
                end
                ...
                end




                Now I've rewritten the code such that I have in my score model:



                class Score < ApplicationRecord
                ...
                scope :exported, -> { joins("INNER JOIN scores_export_orders ON scores.id = scores_export_orders.score_id INNER JOIN export_orders ON export_orders.id = scores_export_orders.export_order_id").where.not("scores_export_orders.score_id IS NULL").distinct }
                ...
                scope :delivery_distr_chain, -> (input) {
                self.where('export_orders.distribution_chain_id = ?', input)
                }

                def self.ransackable_scopes(auth_object = nil)
                [:delivery_distr_chain]
                end
                end


                so that I have the double join on the exported scope and I don't need to join again on the filter scope.






                share|improve this answer













                Solved. I was not aware of the fact that I already declare a scope in my ActiveAdmin for Scores, which was the following:



                scope :exported, -> { joins("LEFT JOIN scores_export_orders ON scores.id = scores_export_orders.score_id").where.not("scores_export_orders.score_id IS NULL").distinct



                called in ActiveAdmin as



                  controller do
                def scoped_collection
                end_of_association_chain.exported
                end
                ...
                end




                Now I've rewritten the code such that I have in my score model:



                class Score < ApplicationRecord
                ...
                scope :exported, -> { joins("INNER JOIN scores_export_orders ON scores.id = scores_export_orders.score_id INNER JOIN export_orders ON export_orders.id = scores_export_orders.export_order_id").where.not("scores_export_orders.score_id IS NULL").distinct }
                ...
                scope :delivery_distr_chain, -> (input) {
                self.where('export_orders.distribution_chain_id = ?', input)
                }

                def self.ransackable_scopes(auth_object = nil)
                [:delivery_distr_chain]
                end
                end


                so that I have the double join on the exported scope and I don't need to join again on the filter scope.







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 15 '18 at 13:26









                SimonSimon

                313214




                313214






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53287766%2factiveadmin-custom-filter-fails%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    List item for chat from Array inside array React Native

                    Jo Brand

                    Thiostrepton