Skip to content

A Couchbase ORM based on ActiveModel for Rails compatibility

License

Notifications You must be signed in to change notification settings

fab-girard/couchbase-orm

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

98 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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

N1ql

Like views, it's possible to use N1QL to process some requests used for filtering results or ordering.

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

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

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

      validates_presence_of :author, :body
    end

Whatever the record, it's possible to execute a N1QL request with:

Comment.bucket.n1ql.select('RAW meta(ui).id').from('bucket').where('author="my_value"').order_by('view_count DESC').results

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

By default, has_many uses a view for association, but you can define a type option to specify an association using N1QL instead:

    class Comment < CouchbaseOrm::Base
        belongs_to :author
    end

    class Author < CouchbaseOrm::Base
        has_many :comments, type: :n1ql, dependent: :destroy
    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

About

A Couchbase ORM based on ActiveModel for Rails compatibility

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Ruby 100.0%