{"id":16985444,"url":"https://github.com/fengb/arlj","last_synced_at":"2026-04-09T16:05:06.443Z","repository":{"id":23805040,"uuid":"27181196","full_name":"fengb/arlj","owner":"fengb","description":"ActiveRecord Left Join","archived":false,"fork":false,"pushed_at":"2015-03-15T00:19:11.000Z","size":276,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-03T11:55:34.377Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fengb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-11-26T14:45:36.000Z","updated_at":"2015-02-03T19:03:45.000Z","dependencies_parsed_at":"2022-08-22T04:50:59.479Z","dependency_job_id":null,"html_url":"https://github.com/fengb/arlj","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/fengb/arlj","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fengb%2Farlj","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fengb%2Farlj/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fengb%2Farlj/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fengb%2Farlj/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fengb","download_url":"https://codeload.github.com/fengb/arlj/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fengb%2Farlj/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267276992,"owners_count":24063221,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-26T02:00:08.937Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T02:43:30.614Z","updated_at":"2026-04-09T16:05:06.397Z","avatar_url":"https://github.com/fengb.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Arlj - ActiveRecord Left Join [![Travis CI](https://travis-ci.org/fengb/arlj.svg?branch=master)](https://travis-ci.org/fengb/arlj)\n\nMake left joins feel like first-class citizens in ActiveRecord.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'arlj'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install arlj\n\n## Usage\n\nLoad Arlj into your class:\n\n```ruby\nclass Parent \u003c ActiveRecord::Base\n  extend Arlj\nend\n```\n\nOr extend all of ActiveRecord models:\n\n```ruby\nActiveRecord::Base.extend Arlj\n```\n\nThen begin to left join!\n\n```ruby\nputs Parent.left_joins(:children).group('records.id').select('COUNT(children.id)').to_sql\n=\u003e SELECT COUNT(children.id)\n     FROM \"parents\"\n     LEFT OUTER JOIN \"children\"\n                  ON \"children\".\"parent_id\" = \"parents\".\"id\"\n    GROUP BY records.id\n```\n\n`left_joins` is purposely low level for maximum control.\n\nArlj has an aggregation method that is higher level and generally easier to use:\n\n```ruby\nParent.left_joins_aggregate(:children, 'COUNT(*)').to_sql\n=\u003e SELECT \"parents\".*\n     FROM \"parents\"\n     LEFT OUTER JOIN (SELECT \"children\".\"parent_id\"\n                           , COUNT(\"children\".\"id\") AS children_count\n                        FROM \"children\"\n                       GROUP BY \"children\".\"parent_id\") arlj_aggregate_children\n                  ON arlj_aggregate_children.\"parent_id\" = \"parents\".\"id\"\n```\n\nSupported aggregation functions are `COUNT()`, `SUM()`, `AVG()`, `MIN()`, and `MAX()`.\n\nThe aggregation column has a default name of `{table}_{function}_{column}` which\nis easily renamed:\n\n```ruby\nParent.left_joins_aggregate(:children, 'SUM(age)' =\u003e 'ekkekkekkekkeptangya').to_sql\n=\u003e SELECT \"parents\".*\n     FROM \"parents\"\n     LEFT OUTER JOIN (SELECT \"children\".\"parent_id\"\n                           , SUM(\"children\".\"age\") AS ekkekkekkekkeptangya\n                        FROM \"children\"\n                       GROUP BY \"children\".\"parent_id\") arlj_aggregate_children\n                  ON arlj_aggregate_children.\"parent_id\" = \"parents\".\"id\"\n```\n\nSince Arlj uses a sub-select, you can easily chain additional queries:\n\n```ruby\nParent.left_joins_aggregate(:children, 'COUNT(*)').select('children_count').to_sql\n=\u003e SELECT children_count\n     FROM \"parents\"\n     LEFT OUTER JOIN (SELECT \"children\".\"parent_id\"\n                           , COUNT(\"children\".\"id\") AS children_count\n                        FROM \"children\"\n                       GROUP BY \"children\".\"parent_id\") arlj_aggregate_children\n                  ON arlj_aggregate_children.\"parent_id\" = \"parents\".\"id\"\n```\n\nArlj also supports some basic where clauses:\n\n```ruby\nParent.left_joins_aggregate(:children, 'COUNT(*)', where: {age: 1..5}).to_sql\n=\u003e SELECT \"parents\".*\n     FROM \"parents\"\n     LEFT OUTER JOIN (SELECT \"children\".\"parent_id\"\n                           , COUNT(\"children\".\"id\") AS children_count\n                        FROM \"children\"\n                       WHERE (\"children\".\"age\" BETWEEN 1 AND 5)\n                       GROUP BY \"children\".\"parent_id\") arlj_aggregate_children\n                  ON arlj_aggregate_children.\"parent_id\" = \"parents\".\"id\"\n```\n\nIf you prefer, you may also use `arlj` and `arlj_aggregate` instead of\n`left_joins` and `left_joins_aggregate` respectively. To prevent potential\nnaming conflicts, use `Arlj::Base` instead:\n\n```ruby\nclass Parent \u003c ActiveRecord::Base\n  extend Arlj::Base\nend\n```\n\n**Arlj** has an experimental flag that uses the **memoist** gem to memoize the\ngenerated join SQL:\n\n```ruby\nArlj.memoize!\n```\n\nThis has not been proven to be faster.\n\n## Gotchas\n\n* Since `left_joins_aggregate` uses a sub-select for its aggregation, it can\n  underperform a better optimized query.\n\n* When `left_joins_aggregate` joins zero records, the aggregate column is NULL.\n  To operate correctly on these columns, please use `COALESCE(col, 0)`.\n\n## TODO\n\n* `left_joins(nested: :relations)`\n* `left_joins_aggregate([...], merge: User.active)`\n* `has_and_belongs_to_many`\n* `has_many :through =\u003e`\n\n## Contributing\n\n1. Fork it ( https://github.com/fengb/arlj/fork )\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create a new Pull Request\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffengb%2Farlj","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffengb%2Farlj","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffengb%2Farlj/lists"}