Description
Are you tired of fixing N+1 issues? Does it feel unnatural to you to fix it case by case in places where you need the data?
We have a solution for you!
It has many benefits as well as integrations with ActiveRecord and ArLazyPreload.
Check them out!
N1Loader alternatives and similar gems
Based on the "ORM/ODM Extensions" category.
Alternatively, view n1_loader alternatives based on common mentions on social networks and blogs.
-
ActsAsTaggableOn
A tagging plugin for Rails applications that allows for custom tagging along dynamic contexts. -
Audited
Audited (formerly acts_as_audited) is an ORM extension that logs all changes to your Rails models. -
ActsAsParanoid
ActiveRecord plugin allowing you to hide and restore records without actually deleting them. -
Rails PG Extras
Rails PostgreSQL database performance insights. Locks, index usage, buffer cache hit ratios, vacuum stats and more. -
Acts As Commentable with Threading
Similar to acts_as_commentable; however, utilizes awesome_nested_set to provide threaded comments -
activerecord-multi-tenant
Rails/ActiveRecord support for distributed multi-tenant databases like Postgres+Citus -
ActsAsTree
ActsAsTree -- Extends ActiveRecord to add simple support for organizing items into parent–children relationships. -
activerecord_json_validator
🔩 ActiveRecord::JSONValidator makes it easy to validate JSON attributes against a JSON schema. -
ActiveImporter
DISCONTINUED. Define importers that load tabular data from spreadsheets or CSV files into any ActiveRecord-like ORM. -
PermenantRecords
Rails Plugin - soft-delete your ActiveRecord records. It's like an explicit version of ActsAsParanoid -
data_miner
Download, unpack from a ZIP/TAR/GZ/BZ2 archive, parse, correct, convert units and import Google Spreadsheets, XLS, ODS, XML, CSV, HTML, etc. into your ActiveRecord models. Uses RemoteTable gem internally. -
mini_record
ActiveRecord meets DataMapper, with MiniRecord you are be able to write schema inside your models.
Scout Monitoring - Performance metrics and, now, Logs Management Monitoring with Scout Monitoring
* Code Quality Rankings and insights are calculated and provided by Lumnify.
They vary from L1 to L5 with "L5" being the highest.
Do you think we are missing an alternative of N1Loader or a related project?
README
N1Loader
N1Loader is designed to provide a simple way for avoiding N+1 issues of any kind. For example, it can help with resolving N+1 for:
- database querying (most common case)
- 3rd party service calls
- complex calculations
- and many more
Toptal is hiring! Join as Freelancer or write me if you want to join Core team.
Support: ActiveRecord 5, 6, and 7.
Killer feature for GraphQL API
N1Loader in combination with ArLazyPreload is a killer feature for your GraphQL API. Give it a try now and see incredible results instantly! Check out the [example](examples/graphql.rb) and start benefiting from it in your projects!
gem 'n1_loader', require: 'n1_loader/ar_lazy_preload'
Enhance ActiveRecord
Are you working with well-known Rails application? Try it out and see how well N1Loader fulfills missing gaps when you can't define ActiveRecord associations! Check out the detailed [guide](guides/enhanced-activerecord.md) with examples or its [short version](examples/active_record_integration.rb).
gem 'n1_loader', require: 'n1_loader/active_record'
Are you ready to forget about N+1 once and for all? Install ArLazyPreload and see dreams come true!
gem 'n1_loader', require: 'n1_loader/ar_lazy_preload'
Standalone mode
Are you not working with ActiveRecord? N1Loader is ready to be used as standalone solution! ([full snippet](examples/core.rb))
gem 'n1_loader'
How to use it?
N1Loader provides DSL that allows you to define N+1 ready loaders that can be injected into your objects in a way that you can avoid N+1 issues.
Disclaimer: examples below are working but designed to show N1Loader potentials only. In real live applications, N1Loader can be applied anywhere and in more [elegant way](examples/isolated_loader.rb).
Let's look at simple example below ([full snippet](examples/active_record_integration.rb)):
class User < ActiveRecord::Base
has_many :payments
n1_optimized :payments_total do |users|
total_per_user =
Payment.group(:user_id)
.where(user: users)
.sum(:amount)
.tap { |h| h.default = 0 }
users.each do |user|
total = total_per_user[user.id]
fulfill(user, total)
end
end
end
class Payment < ActiveRecord::Base
belongs_to :user
validates :amount, presence: true
end
# A user has many payments.
# Assuming, we want to know for group of users, what is a total of their payments, we can do the following:
# Has N+1 issue
p User.all.map { |user| user.payments.sum(&:amount) }
# Has no N+1 but we load too many data that we don't actually need
p User.all.includes(:payments).map { |user| user.payments.sum(&:amount) }
# Has no N+1 and we load only what we need
p User.all.includes(:payments_total).map { |user| user.payments_total }
Let's assume now, that we want to calculate the total of payments for the given period for a group of users. N1Loader can do that as well! ([full snippet](examples/arguments_support.rb))
class User < ActiveRecord::Base
has_many :payments
n1_optimized :payments_total do
argument :from
argument :to
def perform(users)
total_per_user =
Payment
.group(:user_id)
.where(created_at: from..to)
.where(user: users)
.sum(:amount)
.tap { |h| h.default = 0 }
users.each do |user|
total = total_per_user[user.id]
fulfill(user, total)
end
end
end
end
class Payment < ActiveRecord::Base
belongs_to :user
validates :amount, presence: true
end
# Has N+1
p User.all.map { |user| user.payments.select { |payment| payment.created_at >= from && payment.created_at <= to }.sum(&:amount) }
# Has no N+1 but we load too many data that we don't need
p User.all.includes(:payments).map { |user| user.payments.select { |payment| payment.created_at >= from && payment.created_at <= to }.sum(&:amount) }
# Has no N+1 and calculation is the most efficient
p User.all.includes(:payments_total).map { |user| user.payments_total(from: from, to: to) }
Features and benefits
- N1Loader doesn't use Promises which means it's easy to debug
- Doesn't require injection to objects, can be used in [isolation](examples/isolated_loader.rb)
- Loads data [lazily](examples/lazy_loading.rb)
- Loaders can be [shared](examples/shared_loader.rb) between multiple classes
- Loaded data can be [re-fetched](examples/reloading.rb)
- Loader can be optimized for [single cases](examples/single_case.rb)
- Loader support [arguments](examples/arguments_support.rb)
- Has [integration](examples/active_record_integration.rb) with ActiveRecord which makes it brilliant
- Has [integration](examples/ar_lazy_integration.rb) with ArLazyPreload which makes it excellent
Funding
Open Collective Backers
You're an individual who wants to support the project with a monthly donation. Your logo will be available on the Github page. [Become a backer]
Open Collective Sponsors
You're an organization that wants to support the project with a monthly donation. Your logo will be available on the Github page. [Become a sponsor]
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/djezzzl/n1_loader. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](CODE_OF_CONDUCT.md).
License
The gem is available as open source under the terms of the MIT License.
Code of Conduct
Everyone interacting in the N1Loader project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](CODE_OF_CONDUCT.md).
Changelog
N1Loader's changelog is available [here](CHANGELOG.md).
Copyright
Copyright (c) Evgeniy Demin. See [LICENSE.txt](LICENSE.txt) for further details.
*Note that all licence references and agreements mentioned in the N1Loader README section above
are relevant to that project's source code only.