Couchbase ORM for Rails

Build Status

Rails integration

To generate config you can use rails generate couchbase_orm:config:

$ rails generate couchbase_orm:config dev_bucket dev_user dev_password
  => create  config/couchbase.yml

It will generate this config/couchbase.yml for you:

common: &common
  hosts: localhost
  username: dev_user
  password: dev_password

development:
  <<: *common
  bucket: dev_bucket

test:
  <<: *common
  bucket: dev_bucket_test

# set these environment variables on your production server
production:
hosts: <%= ENV['COUCHBASE_HOST'] || ENV['COUCHBASE_HOSTS'] %>
bucket: <%= ENV['COUCHBASE_BUCKET'] %>
username: <%= ENV['COUCHBASE_USER'] %>
password: <%= ENV['COUCHBASE_PASSWORD'] %>

Views are generated on application load if they don't exist or mismatch. This works fine in production however by default in development models are lazy loaded.

# config/environments/development.rb
config.eager_load = true

Examples

    require 'couchbase-orm'

    class Post < CouchbaseOrm::Base
      attribute :title, type: String
      attribute :body,  type: String
      attribute :draft, type: Boolean
    end

    p = Post.new(id: 'hello-world',
                 title: 'Hello world',
                 draft: true)
    p.save
    p = Post.find('hello-world')
    p.body = "Once upon the times...."
    p.save
    p.update(draft: false)
    Post.bucket.get('hello-world')  #=> {"title"=>"Hello world", "draft"=>false,
                                    #    "body"=>"Once upon the times...."}

You can also let the library generate the unique identifier for you:

    p = Post.create(title: 'How to generate ID',
                    body: 'Open up the editor...')
    p.id        #=> "post-abcDE34"

You can define connection options on per model basis:

    class Post < CouchbaseOrm::Base
      attribute :title, type: String
      attribute :body,  type: String
      attribute :draft, type: Boolean

      connect bucket: 'blog', password: ENV['BLOG_BUCKET_PASSWORD']
    end

Validations

There are all methods from ActiveModel::Validations accessible in context of rails application. You can also enforce types using ruby conversion methods

    class Comment < Couchbase::Model
      attribute :author, :body, type: String

      validates_presence_of :author, :body
    end

Views (aka Map/Reduce indexes)

Views are defined in the model and typically just emit an attribute that can then be used for filtering results or ordering.

    class Comment < CouchbaseOrm::Base
      attribute :author, :body, type: String
      view :all # => emits :id and will return all comments
      view :by_author, emit_key: :author

      # Generates two functions:
      # * the by_author view above
      # * def find_by_author(author); end
      index_view :author

      # You can make compound keys by passing an array to :emit_key
      # this allow to query by read/unread comments
      view :by_read, emit_key: [:user_id, :read]
      # this allow to query by view_count
      view :by_view_count, emit_key: [:user_id, :view_count]
       

      

      validates_presence_of :author, :body
    end

You can use Comment.find_by_author('name') to obtain all the comments by a particular author. The same thing, using the view directly would be: Comment.by_author(key: 'name')

When using a compound key, the usage is the same, you just give the full key :

   Comment.by_read(key: '["'+user_id+'",false]') # gives all unread comments for one particular user

   # or even a range !

   Comment.by_view_count(startkey: '["'+user_id+'",10]', endkey: '["'+user_id+'",20]') # gives all comments that have been seen more than 10 times but less than 20

Check this couchbase help page to learn more on what's possible with compound keys : https://developer.couchbase.com/documentation/server/3.x/admin/Views/views-translateSQL.html

Ex : Compound keys allows to decide the order of the results, and you can reverse it by passing descending: true

Associations and Indexes

There are common active record helpers available for use belongs_to and has_many

    class Comment < CouchbaseOrm::Base
        belongs_to :author
    end

    class Author < CouchbaseOrm::Base
        has_many :comments, dependent: :destroy

        # You can ensure an attribute is unique for this model
        attribute :email, type: String
        ensure_unique :email
    end

Performance Comparison with Couchbase-Ruby-Model

Basically we migrated an application from Couchbase Ruby Model to Couchbase-ORM (this project)

  • Rails 5 production
  • Puma as the webserver
  • Running on a 2015 Macbook Pro
  • Performance test: siege -c250 -r10 http://localhost:3000/auth/authority

The request above pulls the same database document each time and returns it. A simple O(1) operation.

Stat Couchbase Ruby Model Couchbase-ORM
Transactions 2500 hits 2500 hits
Elapsed time 12.24 secs 6.82 secs
Response time 0.88 secs 0.34 secs
Transaction rate 204.25 trans/sec 366.57 trans/sec
Request Code ruby-model-app couch-orm-app