• Stars
    star
    103
  • Rank 333,046 (Top 7 %)
  • Language
    Ruby
  • License
    MIT License
  • Created almost 13 years ago
  • Updated 12 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Acts as list mongoid implementation

Gem Version Build Status

Mongoid Orderable

Mongoid::Orderable is a ordered list implementation for your Mongoid 7+ projects.

Core Features

  • Sets a position index field on your documents which allows you to sort them in order.
  • Uses MongoDB's $inc operator to batch-update position.
  • Supports scope for position index, including changing scopes.
  • Supports multiple position indexes on the same document.
  • (Optional) Uses MongoDB transactions to ensure order integrity during concurrent updates.

Version Support

As of version 6.0.0, Mongoid::Orderable supports the following dependency versions:

  • Ruby 2.6+
  • Mongoid 7.0+
  • Rails 5.2+

For older versions, please use Mongoid::Orderable 5.x and earlier.

Transaction support requires MongoDB 4.2+ (4.4+ recommended.)

Usage

Getting Started

gem 'mongoid_orderable'

Include Mongoid::Orderable into your model and call orderable method. Embedded objects are automatically scoped to their parent.

class MyModel
  include Mongoid::Document
  include Mongoid::Orderable

  belongs_to :group
  belongs_to :drawer, class_name: "Office::Drawer",
             foreign_key: "legacy_drawer_key_id"

  orderable

  # if you set :scope as a symbol, it will resolve the foreign key from relation
  orderable scope: :drawer, field: :pos

  # if you set :scope as a string, it will use it as the field name for scope
  orderable scope: 'drawer', field: :pos

  # scope can also be a proc
  orderable scope: ->(doc) { where(group_id: doc.group_id) }

  # this one if you want specify indexes manually
  orderable index: false 

  # count position from zero as the top-most value (1 is the default value)
  orderable base: 0
end

You can also set default config values in an initializer, which will be applied when calling the orderable macro in a model.

# configs/initializers/mongoid_orderable.rb
Mongoid::Orderable.configure do |config|
  config.field = :pos
  config.base = 0
  config.index = false
end

Moving Position

item.move_to 2 # just change position
item.move_to! 2 # and save
item.move_to = 2 # assignable method

# symbol position
item.move_to :top
item.move_to :bottom
item.move_to :higher
item.move_to :lower

# generated methods
item.move_to_top
item.move_to_bottom
item.move_higher
item.move_lower

item.next_items # return a collection of items higher on the list
item.previous_items # return a collection of items lower on the list

item.next_item # returns the next item in the list
item.previous_item # returns the previous item in the list

Multiple Fields

You can also define multiple orderable fields for any class including the Mongoid::Orderable module.

class Book
  include Mongoid::Document
  include Mongoid::Orderable

  orderable base: 0
  orderable field: sno, as: :serial_no
end

The above defines two different orderable_fields on Book - position and serial_no. The following helpers are generated in this case:

book.move_#{field}_to
book.move_#{field}_to=
book.move_#{field}_to!

book.move_#{field}_to_top
book.move_#{field}_to_bottom
book.move_#{field}_higher
book.move_#{field}_lower

book.next_#{field}_items
book.previous_#{field}_items

book.next_#{field}_item
book.previous_#{field}_item

where #{field} is either position or serial_no.

When a model defines multiple orderable fields, the original helpers are also available and work on the first orderable field.

  @book1 = Book.create!
  @book2 = Book.create!
  @book2                 # => <Book _id: 53a16a2ba1bde4f746000001, serial_no: 1, position: 1>
  @book2.move_to! :top   # this will change the :position of the book to 0 (not serial_no)
  @book2                 # => <Book _id: 53a16a2ba1bde4f746000001, serial_no: 1, position: 0>

To specify any other orderable field as default pass the default: true option with orderable.

  orderable field: sno, as: :serial_no, default: true

Embedded Documents

class Question
  include Mongoid::Document
  include Mongoid::Orderable

  embedded_in :survey

  orderable
end

If you bulk import embedded documents without specifying their position, no field position will be written.

class Survey
  include Mongoid::Document

  embeds_many :questions, cascade_callbacks: true
end

To ensure the position is written correctly, you will need to set cascade_callbacks: true on the relation.

Disable Ordering

You can disable position tracking for specific documents using the :if and :unless options. This is in advanced scenarios where you want to control position manually for certain documents. In general, the disable condition should match a specific scope. Warning: If used improperly, this will cause your documents to become out-of-order.

class Book
  include Mongoid::Document
  include Mongoid::Orderable

  field :track_position, type: Boolean
  
  orderable if: :track_position, unless: -> { created_at < Date.parse('2020-01-01') }
end

Transaction Support

By default, Mongoid Orderable does not guarantee ordering consistency when doing multiple concurrent updates on documents. This means that instead of having positions 1, 2, 3, 4, 5, after running your system in production at scale your position data will become corrupted, e.g. 1, 1, 4, 4, 6. To remedy this, this Mongoid Orderable can use MongoDB transactions

Prerequisites

Configuration

You may enable transactions on both the global and model configs:

Mongoid::Orderable.configure do |config|
  config.use_transactions = true       # default: false
  config.transaction_max_retries = 10  # default: 10
end

class MyModel
  orderable :position, use_transactions: false
end

When two transactions are attempted at the same time, database-level WriteConflict failures may result and retries will be attempted. After transaction_max_retries has been exceeded, a Mongoid::Orderable::Errors::TransactionFailed error will be raised.

Locks

When using transactions, Mongoid Orderable creates a collection mongoid_orderable_locks which is used to store temporary lock objects. Lock collections use a TTL index which auto-deletes objects older than 1 day.

You can change the lock collection name globally or per model:

Mongoid::Orderable.configure do |config|
  config.lock_collection = "my_locks"  # default: "mongoid_orderable_locks"
end

class MyModel
  orderable :position, lock_collection: "my_model_locks"
end

MongoDB 4.2 Support

In MongoDB 4.2, collections cannot be created within transactions. Therefore, you will need to manually run the following command once to initialize the lock collection:

Mongoid::Orderable::Models::Lock.create!

This step is not necessary when using MongoDB 4.4+.

Contributing

Please fork the project on Github and raise a pull request including passing specs.

Copyright & License

Copyright (c) 2011 Arkadiy Zabazhanov, Johnny Shields, and contributors.

MIT license, see LICENSE for details.

More Repositories

1

mongoid-rspec

RSpec matchers and macros for Mongoid.
Ruby
498
star
2

mongoid-slug

Generates a URL slug/permalink based on fields in a Mongoid-based model.
Ruby
493
star
3

mongoid-history

Multi-user non-linear history tracking, auditing, undo, redo for mongoid.
Ruby
392
star
4

mongoid_search

Simple full text search for Mongoid ORM
Ruby
319
star
5

moped

A MongoDB driver for Ruby
Ruby
202
star
6

mongoid_fulltext

An n-gram-based full-text search implementation for the Mongoid ODM.
Ruby
150
star
7

mongoid-site

Source code for mongoid.org
HTML
141
star
8

echo

The Mongoid Demo Application
Ruby
128
star
9

kiqstand

Mongoid 3 Middleware for Sidekiq
Ruby
97
star
10

mongoid-grid_fs

A pure Mongoid/Moped implementation of the MongoDB GridFS specification.
Ruby
83
star
11

mongoid-locker

Document-level locking for MongoDB via Mongoid
Ruby
77
star
12

mongoid-cached-json

Effective caching for nested JSON models.
Ruby
74
star
13

origin

A Ruby DSL for building MongoDB queries
Ruby
62
star
14

mongoid-scroll

Mongoid extension that enables infinite scrolling with MongoDB.
Ruby
60
star
15

mongo_session_store

MongoSessionStore is a Rails-compatible session store for MongoDB using either Mongoid or the MongoDB Ruby Driver. It also allows for custom Mongo session store that works with any (or no!) Mongo ODM
Ruby
56
star
16

evolver

Database Schema Evolution for MongoDB
Ruby
44
star
17

mongoid-demo

Demo applications for Mongoid
Ruby
19
star
18

mongoid-observers

Mongoid observer (removed in Mongoid 4.0)
Ruby
19
star
19

mongoid.github.io

Mongoid community site.
HTML
15
star
20

scooter

An Asynchronous (Non-Blocking) MongoDB driver for the JVM
Scala
15
star
21

mongoid-shell

Derive shell commands from Mongoid sessions and configuration options.
Ruby
14
star
22

moped-turbo

Moped turbocharger. (C extensions for Moped)
Ruby
13
star
23

mongoid-tag-collectible

Easily maintain a collection of Tag instances with aggregate counts from your model's tags.
Ruby
9
star
24

delayed_job_shallow_mongoid

When the object or arg to a delayed_job is a Mongoid document, store only a small stub of the object instead of the full serialization.
Ruby
8
star
25

echo-goliath

Simple Sample of Using Moped and Goliath.
Ruby
7
star
26

mongoid-compatibility

Compatibility helpers for the many Mongoid versions.
Ruby
7
star
27

scourge

1
star