Skip to content

Repository files navigation

Massive Record

Massive Record is a Ruby client for HBase. It provides a basic API through Thrift and an ORM with advanced features.

See introduction to HBase model architecture:
http://wiki.apache.org/hadoop/Hbase/HbaseArchitecture
Understanding terminology of Table / Row / Column family / Column / Cell:
http://jimbojw.com/wiki/index.php?title=Understanding_Hbase_and_BigTable

HBase requirement

MassiveRecord is following the Cloudera packages of HBase: http://www.cloudera.com

Currently, MassiveRecord is tested against HBase 0.90.3, which can be found at the following address: https://ccp.cloudera.com/display/SUPPORT/CDH3+Downloadable+Tarballs

Install HBase (OSX):
Download the package 'HBase 0.90.3+15.3' and extract it.
Start HBase using the following command:

path_to_hbase/bin/start-hbase.sh

Start Thrift (HBase service interface):

path_to_hbase/bin/hbase thrift -b 127.0.0.1 start

Installation

First of all: Please make sure you are using Ruby 1.9.2. For now, we are only ensuring that Massive Record works on that Ruby version, and we know it has some problems with 1.8.7.

gem install massive_record

Ruby on Rails

MassiveRecord is compatible with Rails 3.0. It is not yet fully compatible with 3.1 or any higher versions. Add the following Gems in your Gemfile:

gem 'massive_record'

Create an config/hbase.yml file with the following content:

defaults: &defaults
host: somewhere.compute.amazonaws.com # No 'http', it's a Thrift connection
port: 9090
development:
<<: *defaults
test:
<<: *defaults
production:
<<: *defaults

Usage

There are two ways for using the Massive Record library. At the highest level we have ORM. This is Active Model compliant and makes it easy to use. The second way of doing things is working directly against the adapter (simple API).

ORM

Both MassiveRecord::ORM::Table and MassiveRecord::ORM::Embedded do now have some functionality which you can expect from an ORM. This includes:

  • An initializer which takes attribute hash and assigns them to your object.
  • Write and read methods for the attributes
  • Validations, as you expect from an ActiveRecord.
  • Callbacks, as you expect from an ActiveRecord.
  • Information about changes on attributes.
  • Casting of attributes
  • Serialization of array / hashes
  • Timestamps like created_at and updated_at. Updated at will always be available, created_at must be defined. See example down:
  • Finder scopes. Like: Person.select(:only_columns_from_this_family).limit(10).collect(&:name)
  • Ability to set a default scope.
  • Time zone aware time attributes.
  • Basic instrumentation and logging of query times.
  • Attribute mass assignment security.

Tables also have:

  • Persistencey method calls like create, save and destroy (but they do not actually save things to hbase)
  • Easy access to adapter's connection via Person.connection
  • Easy access to adapter's hbase table via Person.table
  • Finder method, like Person.find("an_id"), Person.find("id1", "id2"), Person.all etc
  • Save / update methods
  • Auto-creation of table and column families on save if table does not exists.
  • Destroy records
  • Relations: Both references to other tables and simple embedded records. See MassiveRecord::ORM::Relations::Interface ClassMethods for documentation
  • Observable. See MassiveRecord::ORM::Observer. If you know how to use ActiveRecord's observer you know how to use this one.
  • IdentityMap (when enabled)

Here are some examples setting up models:

class Person < MassiveRecord::ORM::Table
references_one :boss, :class_name => "Person", :store_in => :info
references_one :attachment, :polymorphic => true
references_many :friends, :store_in => :info
references_many :blog_posts, :records_starts_from => :posts_start_id
embeds_many :addresses
default_scope select(:info)
column_family :info do
field :name
field :email
field :phone_number
field :points, :integer, :default => 0
field :date_of_birth, :date, :allow_nil => false # Defaults to today
field :newsletter, :boolean, :default => false
field :type # Used for single table inheritance
field :in_the_future, :time, :default => Proc.new { 2.hours.from_now }
field :hobbies, :array, :allow_nil => false # Default to empty array
timestamps # ..or field :created_at, :time
end
column_family :misc do
field :with_a_lot_of_uninteresting_data
end
attr_accessible :name, :email, :phone_number, :date_of_birth
validates_presence_of :name, :email
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
# Returns the id the scanner should start from in the BlogPost table
# to fetch blog posts related to this person
def posts_start_id
id+'-'
end
end
class Friend < Person
# This one will be stored in Person's table with it's type set to Friend.
# Calling Person.all will return object back as a Friend.
end
class PersonObserver < MassiveRecord::ORM::Observer
def after_create(person_created)
# Do something smart with that person
end
end
class Address < MassiveRecord::ORM::Embedded
embedded_in :person
field :street
field :number, :integer
field :nice_place, :boolean, :default => true
end
class BlogPost < MassiveRecord::ORM::Embedded
references_one :author, :class_name => "Person", :store_in => :info
field :title
field :content
private
# Set yourself an ID to your model
def default_id
"#{author_id}|#{Time.now.strftime("%Y-%m-%d-%k-%M")}"
end
end

Perform requests:

# Fetch an object
u = User.find("45")
# Blog posts associated
u.blog_posts
# Blog posts associated during May 2011
u.blog_posts(:starts_with => "45-2011-05") # user_id - year - month
# Blog posts from May 2011
u.blog_posts(:offset => "45-2011-05")
# Only five blog posts
u.blog_posts(:limit => 5)

You can find a small example application here: https://github.com/thhermansen/massive_record_test_app

Related gems

We have developed some gems which adds support for MassiveRecord. These are:

ORM Adapter

https://github.com/CompanyBook/orm_adapter Used by Devise. I guess we'll might release the code used to get Devise support in MR.

Database Cleaner

https://github.com/CompanyBook/database_cleaner User by for instance Cucumber and ourself with Rspec.

Sunspot Rails

https://github.com/CompanyBook/sunspot_massive_record Makes it easier to make things searchable with solr.

Wrapper (adapter) API

You can, if you'd like, work directly against the adapter. It is however adviced to use the ORM as the interface to the adapter is not yet very well defined.

# Init a new connection with HBase
conn = MassiveRecord::Wrapper::Connection.new(:host => 'localhost', :port => 9090)
conn.open
# OR init a connection using the config/hbase.yml file with Rails
conn = MassiveRecord::Wrapper::Base.connection
# Fetch tables name
conn.tables # => ["companies", "news", "webpages"]
# Init a table
table = MassiveRecord::Wrapper::Table.new(conn, :people)
# Add a column family
column = MassiveRecord::Wrapper::ColumnFamily.new(:info)
table.column_families.push(column)
# Or bulk add column families
table.create_column_families([:friends, :misc])
# Create the table
table.save # will raise an exception if the table already exists
# Fetch column families from the database
table.fetch_column_families # => [ColumnFamily#RTY4424, ColumnFamily#R475424, ColumnFamily#GHJ9424]
table.column_families.collect(&:name) # => ["info", "friends", "misc"]
# Add a new row
row = MassiveRecord::Wrapper::Row.new
row.id = "my_unique_id"
row.values = { :info => { :first_name => "H", :last_name => "Base", :email => "h@base.com" } }
row.table = table
row.save
# Fetch rows
table.first # => MassiveRecord#ID1
table.all(:limit => 10) # => [MassiveRecord#ID1, MassiveRecord#ID2, ...]
table.find("ID2") # => MassiveRecord#ID2
table.find(["ID1", "ID2"]) # => [MassiveRecord#ID1, MassiveRecord#ID2]
table.all(:limit => 3, :starts_with => "ID2") # => [MassiveRecord#ID2, MassiveRecord#ID3, MassiveRecord#ID4]
# Manipulate rows
table.first.destroy # => true
# Remove the table
table.destroy

Planned work

  • Cache the decoded values of attributes, not use the value_is_already_decoded?. This will fix possible problem with YAML as coder backend.
  • Implement other Adapters, for instance using jruby and the Java API.

Contribute

If you want to contribute feel free to fork this project :-) Make a feature branch, write test, implement and make a pull request.

Getting started

git clone git://github.com/CompanyBook/massive_record.git (or the address to your fork)
cd massive_record
bundle install

Next up you need to add a config.yml file inside of spec/ which contains something like: host: url.to-a.thrift.server port: 9090 table: massive_record_test_table

You should now be able to run rspec spec/

Play with it in the console

Checkout the massive_record project and install it as a Gem :

cd massive_record/
bundle console
ruby-1.9.2-p0 > Bundler.require
=> [
<Bundler::Dependency type=:runtime name="massive_record" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="thrift" requirements=">= 0.5.0">,
<Bundler::Dependency type=:runtime name="activesupport" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="activemodel" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="rspec" requirements=">= 2.1.0">
]
ruby-1.9.2-p0 > MassiveRecord::VERSION
=> "0.0.1" 

Clean HBase database between each test

We have created a helper module MassiveRecord::Rspec::SimpleDatabaseCleaner which, when included into rspec tests, will clean the database for ORM records between each test case. You can also take a look into spec/support/mock_massive_record_connection.rb for some functionality which will mock a hbase connection making it easier (faster) to test code where no real database is needed.

More Information and Resources

Thrift API

Ruby Library using the HBase Thrift API. http://wiki.apache.org/hadoop/Hbase/ThriftApi

The generated Ruby files can be found under lib/massive_record/thrift/
The whole API (CRUD and more) is present in the Client object (Apache::Hadoop::Hbase::Thrift::Hbase::Client).
The client can be easily initialized using the MassiveRecord connection :

conn = MassiveRecord::Wrapper::Connection.new(:host => 'localhost', :port => 9090)
conn.open
client = conn.client
# Do whatever you want with the client object

Q&A

How to add a new column family to an existing table?

# Connect to the HBase console on the server itself and enter the following code :
disable 'companies'
alter 'companies', { NAME => 'new_collumn_familiy' }
enable 'companies'

Copyright (c) 2011 Companybook, released under the MIT license

About

HBase ruby client

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - firebait/massive_record: HBase ruby client · GitHub
Skip to content

Repository files navigation

Massive Record

Massive Record is a Ruby client for HBase. It provides a basic API through Thrift and an ORM with advanced features.

See introduction to HBase model architecture:
http://wiki.apache.org/hadoop/Hbase/HbaseArchitecture
Understanding terminology of Table / Row / Column family / Column / Cell:
http://jimbojw.com/wiki/index.php?title=Understanding_Hbase_and_BigTable

HBase requirement

MassiveRecord is following the Cloudera packages of HBase: http://www.cloudera.com

Currently, MassiveRecord is tested against HBase 0.90.3, which can be found at the following address: https://ccp.cloudera.com/display/SUPPORT/CDH3+Downloadable+Tarballs

Install HBase (OSX):
Download the package 'HBase 0.90.3+15.3' and extract it.
Start HBase using the following command:

path_to_hbase/bin/start-hbase.sh

Start Thrift (HBase service interface):

path_to_hbase/bin/hbase thrift -b 127.0.0.1 start

Installation

First of all: Please make sure you are using Ruby 1.9.2. For now, we are only ensuring that Massive Record works on that Ruby version, and we know it has some problems with 1.8.7.

gem install massive_record

Ruby on Rails

MassiveRecord is compatible with Rails 3.0. It is not yet fully compatible with 3.1 or any higher versions. Add the following Gems in your Gemfile:

gem 'massive_record'

Create an config/hbase.yml file with the following content:

defaults: &defaults
host: somewhere.compute.amazonaws.com # No 'http', it's a Thrift connection
port: 9090
development:
<<: *defaults
test:
<<: *defaults
production:
<<: *defaults

Usage

There are two ways for using the Massive Record library. At the highest level we have ORM. This is Active Model compliant and makes it easy to use. The second way of doing things is working directly against the adapter (simple API).

ORM

Both MassiveRecord::ORM::Table and MassiveRecord::ORM::Embedded do now have some functionality which you can expect from an ORM. This includes:

  • An initializer which takes attribute hash and assigns them to your object.
  • Write and read methods for the attributes
  • Validations, as you expect from an ActiveRecord.
  • Callbacks, as you expect from an ActiveRecord.
  • Information about changes on attributes.
  • Casting of attributes
  • Serialization of array / hashes
  • Timestamps like created_at and updated_at. Updated at will always be available, created_at must be defined. See example down:
  • Finder scopes. Like: Person.select(:only_columns_from_this_family).limit(10).collect(&:name)
  • Ability to set a default scope.
  • Time zone aware time attributes.
  • Basic instrumentation and logging of query times.
  • Attribute mass assignment security.

Tables also have:

  • Persistencey method calls like create, save and destroy (but they do not actually save things to hbase)
  • Easy access to adapter's connection via Person.connection
  • Easy access to adapter's hbase table via Person.table
  • Finder method, like Person.find("an_id"), Person.find("id1", "id2"), Person.all etc
  • Save / update methods
  • Auto-creation of table and column families on save if table does not exists.
  • Destroy records
  • Relations: Both references to other tables and simple embedded records. See MassiveRecord::ORM::Relations::Interface ClassMethods for documentation
  • Observable. See MassiveRecord::ORM::Observer. If you know how to use ActiveRecord's observer you know how to use this one.
  • IdentityMap (when enabled)

Here are some examples setting up models:

class Person < MassiveRecord::ORM::Table
references_one :boss, :class_name => "Person", :store_in => :info
references_one :attachment, :polymorphic => true
references_many :friends, :store_in => :info
references_many :blog_posts, :records_starts_from => :posts_start_id
embeds_many :addresses
default_scope select(:info)
column_family :info do
field :name
field :email
field :phone_number
field :points, :integer, :default => 0
field :date_of_birth, :date, :allow_nil => false # Defaults to today
field :newsletter, :boolean, :default => false
field :type # Used for single table inheritance
field :in_the_future, :time, :default => Proc.new { 2.hours.from_now }
field :hobbies, :array, :allow_nil => false # Default to empty array
timestamps # ..or field :created_at, :time
end
column_family :misc do
field :with_a_lot_of_uninteresting_data
end
attr_accessible :name, :email, :phone_number, :date_of_birth
validates_presence_of :name, :email
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
# Returns the id the scanner should start from in the BlogPost table
# to fetch blog posts related to this person
def posts_start_id
id+'-'
end
end
class Friend < Person
# This one will be stored in Person's table with it's type set to Friend.
# Calling Person.all will return object back as a Friend.
end
class PersonObserver < MassiveRecord::ORM::Observer
def after_create(person_created)
# Do something smart with that person
end
end
class Address < MassiveRecord::ORM::Embedded
embedded_in :person
field :street
field :number, :integer
field :nice_place, :boolean, :default => true
end
class BlogPost < MassiveRecord::ORM::Embedded
references_one :author, :class_name => "Person", :store_in => :info
field :title
field :content
private
# Set yourself an ID to your model
def default_id
"#{author_id}|#{Time.now.strftime("%Y-%m-%d-%k-%M")}"
end
end

Perform requests:

# Fetch an object
u = User.find("45")
# Blog posts associated
u.blog_posts
# Blog posts associated during May 2011
u.blog_posts(:starts_with => "45-2011-05") # user_id - year - month
# Blog posts from May 2011
u.blog_posts(:offset => "45-2011-05")
# Only five blog posts
u.blog_posts(:limit => 5)

You can find a small example application here: https://github.com/thhermansen/massive_record_test_app

Related gems

We have developed some gems which adds support for MassiveRecord. These are:

ORM Adapter

https://github.com/CompanyBook/orm_adapter Used by Devise. I guess we'll might release the code used to get Devise support in MR.

Database Cleaner

https://github.com/CompanyBook/database_cleaner User by for instance Cucumber and ourself with Rspec.

Sunspot Rails

https://github.com/CompanyBook/sunspot_massive_record Makes it easier to make things searchable with solr.

Wrapper (adapter) API

You can, if you'd like, work directly against the adapter. It is however adviced to use the ORM as the interface to the adapter is not yet very well defined.

# Init a new connection with HBase
conn = MassiveRecord::Wrapper::Connection.new(:host => 'localhost', :port => 9090)
conn.open
# OR init a connection using the config/hbase.yml file with Rails
conn = MassiveRecord::Wrapper::Base.connection
# Fetch tables name
conn.tables # => ["companies", "news", "webpages"]
# Init a table
table = MassiveRecord::Wrapper::Table.new(conn, :people)
# Add a column family
column = MassiveRecord::Wrapper::ColumnFamily.new(:info)
table.column_families.push(column)
# Or bulk add column families
table.create_column_families([:friends, :misc])
# Create the table
table.save # will raise an exception if the table already exists
# Fetch column families from the database
table.fetch_column_families # => [ColumnFamily#RTY4424, ColumnFamily#R475424, ColumnFamily#GHJ9424]
table.column_families.collect(&:name) # => ["info", "friends", "misc"]
# Add a new row
row = MassiveRecord::Wrapper::Row.new
row.id = "my_unique_id"
row.values = { :info => { :first_name => "H", :last_name => "Base", :email => "h@base.com" } }
row.table = table
row.save
# Fetch rows
table.first # => MassiveRecord#ID1
table.all(:limit => 10) # => [MassiveRecord#ID1, MassiveRecord#ID2, ...]
table.find("ID2") # => MassiveRecord#ID2
table.find(["ID1", "ID2"]) # => [MassiveRecord#ID1, MassiveRecord#ID2]
table.all(:limit => 3, :starts_with => "ID2") # => [MassiveRecord#ID2, MassiveRecord#ID3, MassiveRecord#ID4]
# Manipulate rows
table.first.destroy # => true
# Remove the table
table.destroy

Planned work

  • Cache the decoded values of attributes, not use the value_is_already_decoded?. This will fix possible problem with YAML as coder backend.
  • Implement other Adapters, for instance using jruby and the Java API.

Contribute

If you want to contribute feel free to fork this project :-) Make a feature branch, write test, implement and make a pull request.

Getting started

git clone git://github.com/CompanyBook/massive_record.git (or the address to your fork)
cd massive_record
bundle install

Next up you need to add a config.yml file inside of spec/ which contains something like: host: url.to-a.thrift.server port: 9090 table: massive_record_test_table

You should now be able to run rspec spec/

Play with it in the console

Checkout the massive_record project and install it as a Gem :

cd massive_record/
bundle console
ruby-1.9.2-p0 > Bundler.require
=> [
<Bundler::Dependency type=:runtime name="massive_record" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="thrift" requirements=">= 0.5.0">,
<Bundler::Dependency type=:runtime name="activesupport" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="activemodel" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="rspec" requirements=">= 2.1.0">
]
ruby-1.9.2-p0 > MassiveRecord::VERSION
=> "0.0.1" 

Clean HBase database between each test

We have created a helper module MassiveRecord::Rspec::SimpleDatabaseCleaner which, when included into rspec tests, will clean the database for ORM records between each test case. You can also take a look into spec/support/mock_massive_record_connection.rb for some functionality which will mock a hbase connection making it easier (faster) to test code where no real database is needed.

More Information and Resources

Thrift API

Ruby Library using the HBase Thrift API. http://wiki.apache.org/hadoop/Hbase/ThriftApi

The generated Ruby files can be found under lib/massive_record/thrift/
The whole API (CRUD and more) is present in the Client object (Apache::Hadoop::Hbase::Thrift::Hbase::Client).
The client can be easily initialized using the MassiveRecord connection :

conn = MassiveRecord::Wrapper::Connection.new(:host => 'localhost', :port => 9090)
conn.open
client = conn.client
# Do whatever you want with the client object

Q&A

How to add a new column family to an existing table?

# Connect to the HBase console on the server itself and enter the following code :
disable 'companies'
alter 'companies', { NAME => 'new_collumn_familiy' }
enable 'companies'

Copyright (c) 2011 Companybook, released under the MIT license

About

HBase ruby client

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - firebait/massive_record: HBase ruby client · GitHub
Skip to content

Repository files navigation

Massive Record

Massive Record is a Ruby client for HBase. It provides a basic API through Thrift and an ORM with advanced features.

See introduction to HBase model architecture:
http://wiki.apache.org/hadoop/Hbase/HbaseArchitecture
Understanding terminology of Table / Row / Column family / Column / Cell:
http://jimbojw.com/wiki/index.php?title=Understanding_Hbase_and_BigTable

HBase requirement

MassiveRecord is following the Cloudera packages of HBase: http://www.cloudera.com

Currently, MassiveRecord is tested against HBase 0.90.3, which can be found at the following address: https://ccp.cloudera.com/display/SUPPORT/CDH3+Downloadable+Tarballs

Install HBase (OSX):
Download the package 'HBase 0.90.3+15.3' and extract it.
Start HBase using the following command:

path_to_hbase/bin/start-hbase.sh

Start Thrift (HBase service interface):

path_to_hbase/bin/hbase thrift -b 127.0.0.1 start

Installation

First of all: Please make sure you are using Ruby 1.9.2. For now, we are only ensuring that Massive Record works on that Ruby version, and we know it has some problems with 1.8.7.

gem install massive_record

Ruby on Rails

MassiveRecord is compatible with Rails 3.0. It is not yet fully compatible with 3.1 or any higher versions. Add the following Gems in your Gemfile:

gem 'massive_record'

Create an config/hbase.yml file with the following content:

defaults: &defaults
host: somewhere.compute.amazonaws.com # No 'http', it's a Thrift connection
port: 9090
development:
<<: *defaults
test:
<<: *defaults
production:
<<: *defaults

Usage

There are two ways for using the Massive Record library. At the highest level we have ORM. This is Active Model compliant and makes it easy to use. The second way of doing things is working directly against the adapter (simple API).

ORM

Both MassiveRecord::ORM::Table and MassiveRecord::ORM::Embedded do now have some functionality which you can expect from an ORM. This includes:

  • An initializer which takes attribute hash and assigns them to your object.
  • Write and read methods for the attributes
  • Validations, as you expect from an ActiveRecord.
  • Callbacks, as you expect from an ActiveRecord.
  • Information about changes on attributes.
  • Casting of attributes
  • Serialization of array / hashes
  • Timestamps like created_at and updated_at. Updated at will always be available, created_at must be defined. See example down:
  • Finder scopes. Like: Person.select(:only_columns_from_this_family).limit(10).collect(&:name)
  • Ability to set a default scope.
  • Time zone aware time attributes.
  • Basic instrumentation and logging of query times.
  • Attribute mass assignment security.

Tables also have:

  • Persistencey method calls like create, save and destroy (but they do not actually save things to hbase)
  • Easy access to adapter's connection via Person.connection
  • Easy access to adapter's hbase table via Person.table
  • Finder method, like Person.find("an_id"), Person.find("id1", "id2"), Person.all etc
  • Save / update methods
  • Auto-creation of table and column families on save if table does not exists.
  • Destroy records
  • Relations: Both references to other tables and simple embedded records. See MassiveRecord::ORM::Relations::Interface ClassMethods for documentation
  • Observable. See MassiveRecord::ORM::Observer. If you know how to use ActiveRecord's observer you know how to use this one.
  • IdentityMap (when enabled)

Here are some examples setting up models:

class Person < MassiveRecord::ORM::Table
references_one :boss, :class_name => "Person", :store_in => :info
references_one :attachment, :polymorphic => true
references_many :friends, :store_in => :info
references_many :blog_posts, :records_starts_from => :posts_start_id
embeds_many :addresses
default_scope select(:info)
column_family :info do
field :name
field :email
field :phone_number
field :points, :integer, :default => 0
field :date_of_birth, :date, :allow_nil => false # Defaults to today
field :newsletter, :boolean, :default => false
field :type # Used for single table inheritance
field :in_the_future, :time, :default => Proc.new { 2.hours.from_now }
field :hobbies, :array, :allow_nil => false # Default to empty array
timestamps # ..or field :created_at, :time
end
column_family :misc do
field :with_a_lot_of_uninteresting_data
end
attr_accessible :name, :email, :phone_number, :date_of_birth
validates_presence_of :name, :email
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
# Returns the id the scanner should start from in the BlogPost table
# to fetch blog posts related to this person
def posts_start_id
id+'-'
end
end
class Friend < Person
# This one will be stored in Person's table with it's type set to Friend.
# Calling Person.all will return object back as a Friend.
end
class PersonObserver < MassiveRecord::ORM::Observer
def after_create(person_created)
# Do something smart with that person
end
end
class Address < MassiveRecord::ORM::Embedded
embedded_in :person
field :street
field :number, :integer
field :nice_place, :boolean, :default => true
end
class BlogPost < MassiveRecord::ORM::Embedded
references_one :author, :class_name => "Person", :store_in => :info
field :title
field :content
private
# Set yourself an ID to your model
def default_id
"#{author_id}|#{Time.now.strftime("%Y-%m-%d-%k-%M")}"
end
end

Perform requests:

# Fetch an object
u = User.find("45")
# Blog posts associated
u.blog_posts
# Blog posts associated during May 2011
u.blog_posts(:starts_with => "45-2011-05") # user_id - year - month
# Blog posts from May 2011
u.blog_posts(:offset => "45-2011-05")
# Only five blog posts
u.blog_posts(:limit => 5)

You can find a small example application here: https://github.com/thhermansen/massive_record_test_app

Related gems

We have developed some gems which adds support for MassiveRecord. These are:

ORM Adapter

https://github.com/CompanyBook/orm_adapter Used by Devise. I guess we'll might release the code used to get Devise support in MR.

Database Cleaner

https://github.com/CompanyBook/database_cleaner User by for instance Cucumber and ourself with Rspec.

Sunspot Rails

https://github.com/CompanyBook/sunspot_massive_record Makes it easier to make things searchable with solr.

Wrapper (adapter) API

You can, if you'd like, work directly against the adapter. It is however adviced to use the ORM as the interface to the adapter is not yet very well defined.

# Init a new connection with HBase
conn = MassiveRecord::Wrapper::Connection.new(:host => 'localhost', :port => 9090)
conn.open
# OR init a connection using the config/hbase.yml file with Rails
conn = MassiveRecord::Wrapper::Base.connection
# Fetch tables name
conn.tables # => ["companies", "news", "webpages"]
# Init a table
table = MassiveRecord::Wrapper::Table.new(conn, :people)
# Add a column family
column = MassiveRecord::Wrapper::ColumnFamily.new(:info)
table.column_families.push(column)
# Or bulk add column families
table.create_column_families([:friends, :misc])
# Create the table
table.save # will raise an exception if the table already exists
# Fetch column families from the database
table.fetch_column_families # => [ColumnFamily#RTY4424, ColumnFamily#R475424, ColumnFamily#GHJ9424]
table.column_families.collect(&:name) # => ["info", "friends", "misc"]
# Add a new row
row = MassiveRecord::Wrapper::Row.new
row.id = "my_unique_id"
row.values = { :info => { :first_name => "H", :last_name => "Base", :email => "h@base.com" } }
row.table = table
row.save
# Fetch rows
table.first # => MassiveRecord#ID1
table.all(:limit => 10) # => [MassiveRecord#ID1, MassiveRecord#ID2, ...]
table.find("ID2") # => MassiveRecord#ID2
table.find(["ID1", "ID2"]) # => [MassiveRecord#ID1, MassiveRecord#ID2]
table.all(:limit => 3, :starts_with => "ID2") # => [MassiveRecord#ID2, MassiveRecord#ID3, MassiveRecord#ID4]
# Manipulate rows
table.first.destroy # => true
# Remove the table
table.destroy

Planned work

  • Cache the decoded values of attributes, not use the value_is_already_decoded?. This will fix possible problem with YAML as coder backend.
  • Implement other Adapters, for instance using jruby and the Java API.

Contribute

If you want to contribute feel free to fork this project :-) Make a feature branch, write test, implement and make a pull request.

Getting started

git clone git://github.com/CompanyBook/massive_record.git (or the address to your fork)
cd massive_record
bundle install

Next up you need to add a config.yml file inside of spec/ which contains something like: host: url.to-a.thrift.server port: 9090 table: massive_record_test_table

You should now be able to run rspec spec/

Play with it in the console

Checkout the massive_record project and install it as a Gem :

cd massive_record/
bundle console
ruby-1.9.2-p0 > Bundler.require
=> [
<Bundler::Dependency type=:runtime name="massive_record" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="thrift" requirements=">= 0.5.0">,
<Bundler::Dependency type=:runtime name="activesupport" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="activemodel" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="rspec" requirements=">= 2.1.0">
]
ruby-1.9.2-p0 > MassiveRecord::VERSION
=> "0.0.1" 

Clean HBase database between each test

We have created a helper module MassiveRecord::Rspec::SimpleDatabaseCleaner which, when included into rspec tests, will clean the database for ORM records between each test case. You can also take a look into spec/support/mock_massive_record_connection.rb for some functionality which will mock a hbase connection making it easier (faster) to test code where no real database is needed.

More Information and Resources

Thrift API

Ruby Library using the HBase Thrift API. http://wiki.apache.org/hadoop/Hbase/ThriftApi

The generated Ruby files can be found under lib/massive_record/thrift/
The whole API (CRUD and more) is present in the Client object (Apache::Hadoop::Hbase::Thrift::Hbase::Client).
The client can be easily initialized using the MassiveRecord connection :

conn = MassiveRecord::Wrapper::Connection.new(:host => 'localhost', :port => 9090)
conn.open
client = conn.client
# Do whatever you want with the client object

Q&A

How to add a new column family to an existing table?

# Connect to the HBase console on the server itself and enter the following code :
disable 'companies'
alter 'companies', { NAME => 'new_collumn_familiy' }
enable 'companies'

Copyright (c) 2011 Companybook, released under the MIT license

About

HBase ruby client

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - firebait/massive_record: HBase ruby client · GitHub
Skip to content

Repository files navigation

Massive Record

Massive Record is a Ruby client for HBase. It provides a basic API through Thrift and an ORM with advanced features.

See introduction to HBase model architecture:
http://wiki.apache.org/hadoop/Hbase/HbaseArchitecture
Understanding terminology of Table / Row / Column family / Column / Cell:
http://jimbojw.com/wiki/index.php?title=Understanding_Hbase_and_BigTable

HBase requirement

MassiveRecord is following the Cloudera packages of HBase: http://www.cloudera.com

Currently, MassiveRecord is tested against HBase 0.90.3, which can be found at the following address: https://ccp.cloudera.com/display/SUPPORT/CDH3+Downloadable+Tarballs

Install HBase (OSX):
Download the package 'HBase 0.90.3+15.3' and extract it.
Start HBase using the following command:

path_to_hbase/bin/start-hbase.sh

Start Thrift (HBase service interface):

path_to_hbase/bin/hbase thrift -b 127.0.0.1 start

Installation

First of all: Please make sure you are using Ruby 1.9.2. For now, we are only ensuring that Massive Record works on that Ruby version, and we know it has some problems with 1.8.7.

gem install massive_record

Ruby on Rails

MassiveRecord is compatible with Rails 3.0. It is not yet fully compatible with 3.1 or any higher versions. Add the following Gems in your Gemfile:

gem 'massive_record'

Create an config/hbase.yml file with the following content:

defaults: &defaults
host: somewhere.compute.amazonaws.com # No 'http', it's a Thrift connection
port: 9090
development:
<<: *defaults
test:
<<: *defaults
production:
<<: *defaults

Usage

There are two ways for using the Massive Record library. At the highest level we have ORM. This is Active Model compliant and makes it easy to use. The second way of doing things is working directly against the adapter (simple API).

ORM

Both MassiveRecord::ORM::Table and MassiveRecord::ORM::Embedded do now have some functionality which you can expect from an ORM. This includes:

  • An initializer which takes attribute hash and assigns them to your object.
  • Write and read methods for the attributes
  • Validations, as you expect from an ActiveRecord.
  • Callbacks, as you expect from an ActiveRecord.
  • Information about changes on attributes.
  • Casting of attributes
  • Serialization of array / hashes
  • Timestamps like created_at and updated_at. Updated at will always be available, created_at must be defined. See example down:
  • Finder scopes. Like: Person.select(:only_columns_from_this_family).limit(10).collect(&:name)
  • Ability to set a default scope.
  • Time zone aware time attributes.
  • Basic instrumentation and logging of query times.
  • Attribute mass assignment security.

Tables also have:

  • Persistencey method calls like create, save and destroy (but they do not actually save things to hbase)
  • Easy access to adapter's connection via Person.connection
  • Easy access to adapter's hbase table via Person.table
  • Finder method, like Person.find("an_id"), Person.find("id1", "id2"), Person.all etc
  • Save / update methods
  • Auto-creation of table and column families on save if table does not exists.
  • Destroy records
  • Relations: Both references to other tables and simple embedded records. See MassiveRecord::ORM::Relations::Interface ClassMethods for documentation
  • Observable. See MassiveRecord::ORM::Observer. If you know how to use ActiveRecord's observer you know how to use this one.
  • IdentityMap (when enabled)

Here are some examples setting up models:

class Person < MassiveRecord::ORM::Table
references_one :boss, :class_name => "Person", :store_in => :info
references_one :attachment, :polymorphic => true
references_many :friends, :store_in => :info
references_many :blog_posts, :records_starts_from => :posts_start_id
embeds_many :addresses
default_scope select(:info)
column_family :info do
field :name
field :email
field :phone_number
field :points, :integer, :default => 0
field :date_of_birth, :date, :allow_nil => false # Defaults to today
field :newsletter, :boolean, :default => false
field :type # Used for single table inheritance
field :in_the_future, :time, :default => Proc.new { 2.hours.from_now }
field :hobbies, :array, :allow_nil => false # Default to empty array
timestamps # ..or field :created_at, :time
end
column_family :misc do
field :with_a_lot_of_uninteresting_data
end
attr_accessible :name, :email, :phone_number, :date_of_birth
validates_presence_of :name, :email
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
# Returns the id the scanner should start from in the BlogPost table
# to fetch blog posts related to this person
def posts_start_id
id+'-'
end
end
class Friend < Person
# This one will be stored in Person's table with it's type set to Friend.
# Calling Person.all will return object back as a Friend.
end
class PersonObserver < MassiveRecord::ORM::Observer
def after_create(person_created)
# Do something smart with that person
end
end
class Address < MassiveRecord::ORM::Embedded
embedded_in :person
field :street
field :number, :integer
field :nice_place, :boolean, :default => true
end
class BlogPost < MassiveRecord::ORM::Embedded
references_one :author, :class_name => "Person", :store_in => :info
field :title
field :content
private
# Set yourself an ID to your model
def default_id
"#{author_id}|#{Time.now.strftime("%Y-%m-%d-%k-%M")}"
end
end

Perform requests:

# Fetch an object
u = User.find("45")
# Blog posts associated
u.blog_posts
# Blog posts associated during May 2011
u.blog_posts(:starts_with => "45-2011-05") # user_id - year - month
# Blog posts from May 2011
u.blog_posts(:offset => "45-2011-05")
# Only five blog posts
u.blog_posts(:limit => 5)

You can find a small example application here: https://github.com/thhermansen/massive_record_test_app

Related gems

We have developed some gems which adds support for MassiveRecord. These are:

ORM Adapter

https://github.com/CompanyBook/orm_adapter Used by Devise. I guess we'll might release the code used to get Devise support in MR.

Database Cleaner

https://github.com/CompanyBook/database_cleaner User by for instance Cucumber and ourself with Rspec.

Sunspot Rails

https://github.com/CompanyBook/sunspot_massive_record Makes it easier to make things searchable with solr.

Wrapper (adapter) API

You can, if you'd like, work directly against the adapter. It is however adviced to use the ORM as the interface to the adapter is not yet very well defined.

# Init a new connection with HBase
conn = MassiveRecord::Wrapper::Connection.new(:host => 'localhost', :port => 9090)
conn.open
# OR init a connection using the config/hbase.yml file with Rails
conn = MassiveRecord::Wrapper::Base.connection
# Fetch tables name
conn.tables # => ["companies", "news", "webpages"]
# Init a table
table = MassiveRecord::Wrapper::Table.new(conn, :people)
# Add a column family
column = MassiveRecord::Wrapper::ColumnFamily.new(:info)
table.column_families.push(column)
# Or bulk add column families
table.create_column_families([:friends, :misc])
# Create the table
table.save # will raise an exception if the table already exists
# Fetch column families from the database
table.fetch_column_families # => [ColumnFamily#RTY4424, ColumnFamily#R475424, ColumnFamily#GHJ9424]
table.column_families.collect(&:name) # => ["info", "friends", "misc"]
# Add a new row
row = MassiveRecord::Wrapper::Row.new
row.id = "my_unique_id"
row.values = { :info => { :first_name => "H", :last_name => "Base", :email => "h@base.com" } }
row.table = table
row.save
# Fetch rows
table.first # => MassiveRecord#ID1
table.all(:limit => 10) # => [MassiveRecord#ID1, MassiveRecord#ID2, ...]
table.find("ID2") # => MassiveRecord#ID2
table.find(["ID1", "ID2"]) # => [MassiveRecord#ID1, MassiveRecord#ID2]
table.all(:limit => 3, :starts_with => "ID2") # => [MassiveRecord#ID2, MassiveRecord#ID3, MassiveRecord#ID4]
# Manipulate rows
table.first.destroy # => true
# Remove the table
table.destroy

Planned work

  • Cache the decoded values of attributes, not use the value_is_already_decoded?. This will fix possible problem with YAML as coder backend.
  • Implement other Adapters, for instance using jruby and the Java API.

Contribute

If you want to contribute feel free to fork this project :-) Make a feature branch, write test, implement and make a pull request.

Getting started

git clone git://github.com/CompanyBook/massive_record.git (or the address to your fork)
cd massive_record
bundle install

Next up you need to add a config.yml file inside of spec/ which contains something like: host: url.to-a.thrift.server port: 9090 table: massive_record_test_table

You should now be able to run rspec spec/

Play with it in the console

Checkout the massive_record project and install it as a Gem :

cd massive_record/
bundle console
ruby-1.9.2-p0 > Bundler.require
=> [
<Bundler::Dependency type=:runtime name="massive_record" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="thrift" requirements=">= 0.5.0">,
<Bundler::Dependency type=:runtime name="activesupport" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="activemodel" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="rspec" requirements=">= 2.1.0">
]
ruby-1.9.2-p0 > MassiveRecord::VERSION
=> "0.0.1" 

Clean HBase database between each test

We have created a helper module MassiveRecord::Rspec::SimpleDatabaseCleaner which, when included into rspec tests, will clean the database for ORM records between each test case. You can also take a look into spec/support/mock_massive_record_connection.rb for some functionality which will mock a hbase connection making it easier (faster) to test code where no real database is needed.

More Information and Resources

Thrift API

Ruby Library using the HBase Thrift API. http://wiki.apache.org/hadoop/Hbase/ThriftApi

The generated Ruby files can be found under lib/massive_record/thrift/
The whole API (CRUD and more) is present in the Client object (Apache::Hadoop::Hbase::Thrift::Hbase::Client).
The client can be easily initialized using the MassiveRecord connection :

conn = MassiveRecord::Wrapper::Connection.new(:host => 'localhost', :port => 9090)
conn.open
client = conn.client
# Do whatever you want with the client object

Q&A

How to add a new column family to an existing table?

# Connect to the HBase console on the server itself and enter the following code :
disable 'companies'
alter 'companies', { NAME => 'new_collumn_familiy' }
enable 'companies'

Copyright (c) 2011 Companybook, released under the MIT license

About

HBase ruby client

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - firebait/massive_record: HBase ruby client · GitHub
Skip to content

Repository files navigation

Massive Record

Massive Record is a Ruby client for HBase. It provides a basic API through Thrift and an ORM with advanced features.

See introduction to HBase model architecture:
http://wiki.apache.org/hadoop/Hbase/HbaseArchitecture
Understanding terminology of Table / Row / Column family / Column / Cell:
http://jimbojw.com/wiki/index.php?title=Understanding_Hbase_and_BigTable

HBase requirement

MassiveRecord is following the Cloudera packages of HBase: http://www.cloudera.com

Currently, MassiveRecord is tested against HBase 0.90.3, which can be found at the following address: https://ccp.cloudera.com/display/SUPPORT/CDH3+Downloadable+Tarballs

Install HBase (OSX):
Download the package 'HBase 0.90.3+15.3' and extract it.
Start HBase using the following command:

path_to_hbase/bin/start-hbase.sh

Start Thrift (HBase service interface):

path_to_hbase/bin/hbase thrift -b 127.0.0.1 start

Installation

First of all: Please make sure you are using Ruby 1.9.2. For now, we are only ensuring that Massive Record works on that Ruby version, and we know it has some problems with 1.8.7.

gem install massive_record

Ruby on Rails

MassiveRecord is compatible with Rails 3.0. It is not yet fully compatible with 3.1 or any higher versions. Add the following Gems in your Gemfile:

gem 'massive_record'

Create an config/hbase.yml file with the following content:

defaults: &defaults
host: somewhere.compute.amazonaws.com # No 'http', it's a Thrift connection
port: 9090
development:
<<: *defaults
test:
<<: *defaults
production:
<<: *defaults

Usage

There are two ways for using the Massive Record library. At the highest level we have ORM. This is Active Model compliant and makes it easy to use. The second way of doing things is working directly against the adapter (simple API).

ORM

Both MassiveRecord::ORM::Table and MassiveRecord::ORM::Embedded do now have some functionality which you can expect from an ORM. This includes:

  • An initializer which takes attribute hash and assigns them to your object.
  • Write and read methods for the attributes
  • Validations, as you expect from an ActiveRecord.
  • Callbacks, as you expect from an ActiveRecord.
  • Information about changes on attributes.
  • Casting of attributes
  • Serialization of array / hashes
  • Timestamps like created_at and updated_at. Updated at will always be available, created_at must be defined. See example down:
  • Finder scopes. Like: Person.select(:only_columns_from_this_family).limit(10).collect(&:name)
  • Ability to set a default scope.
  • Time zone aware time attributes.
  • Basic instrumentation and logging of query times.
  • Attribute mass assignment security.

Tables also have:

  • Persistencey method calls like create, save and destroy (but they do not actually save things to hbase)
  • Easy access to adapter's connection via Person.connection
  • Easy access to adapter's hbase table via Person.table
  • Finder method, like Person.find("an_id"), Person.find("id1", "id2"), Person.all etc
  • Save / update methods
  • Auto-creation of table and column families on save if table does not exists.
  • Destroy records
  • Relations: Both references to other tables and simple embedded records. See MassiveRecord::ORM::Relations::Interface ClassMethods for documentation
  • Observable. See MassiveRecord::ORM::Observer. If you know how to use ActiveRecord's observer you know how to use this one.
  • IdentityMap (when enabled)

Here are some examples setting up models:

class Person < MassiveRecord::ORM::Table
references_one :boss, :class_name => "Person", :store_in => :info
references_one :attachment, :polymorphic => true
references_many :friends, :store_in => :info
references_many :blog_posts, :records_starts_from => :posts_start_id
embeds_many :addresses
default_scope select(:info)
column_family :info do
field :name
field :email
field :phone_number
field :points, :integer, :default => 0
field :date_of_birth, :date, :allow_nil => false # Defaults to today
field :newsletter, :boolean, :default => false
field :type # Used for single table inheritance
field :in_the_future, :time, :default => Proc.new { 2.hours.from_now }
field :hobbies, :array, :allow_nil => false # Default to empty array
timestamps # ..or field :created_at, :time
end
column_family :misc do
field :with_a_lot_of_uninteresting_data
end
attr_accessible :name, :email, :phone_number, :date_of_birth
validates_presence_of :name, :email
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
# Returns the id the scanner should start from in the BlogPost table
# to fetch blog posts related to this person
def posts_start_id
id+'-'
end
end
class Friend < Person
# This one will be stored in Person's table with it's type set to Friend.
# Calling Person.all will return object back as a Friend.
end
class PersonObserver < MassiveRecord::ORM::Observer
def after_create(person_created)
# Do something smart with that person
end
end
class Address < MassiveRecord::ORM::Embedded
embedded_in :person
field :street
field :number, :integer
field :nice_place, :boolean, :default => true
end
class BlogPost < MassiveRecord::ORM::Embedded
references_one :author, :class_name => "Person", :store_in => :info
field :title
field :content
private
# Set yourself an ID to your model
def default_id
"#{author_id}|#{Time.now.strftime("%Y-%m-%d-%k-%M")}"
end
end

Perform requests:

# Fetch an object
u = User.find("45")
# Blog posts associated
u.blog_posts
# Blog posts associated during May 2011
u.blog_posts(:starts_with => "45-2011-05") # user_id - year - month
# Blog posts from May 2011
u.blog_posts(:offset => "45-2011-05")
# Only five blog posts
u.blog_posts(:limit => 5)

You can find a small example application here: https://github.com/thhermansen/massive_record_test_app

Related gems

We have developed some gems which adds support for MassiveRecord. These are:

ORM Adapter

https://github.com/CompanyBook/orm_adapter Used by Devise. I guess we'll might release the code used to get Devise support in MR.

Database Cleaner

https://github.com/CompanyBook/database_cleaner User by for instance Cucumber and ourself with Rspec.

Sunspot Rails

https://github.com/CompanyBook/sunspot_massive_record Makes it easier to make things searchable with solr.

Wrapper (adapter) API

You can, if you'd like, work directly against the adapter. It is however adviced to use the ORM as the interface to the adapter is not yet very well defined.

# Init a new connection with HBase
conn = MassiveRecord::Wrapper::Connection.new(:host => 'localhost', :port => 9090)
conn.open
# OR init a connection using the config/hbase.yml file with Rails
conn = MassiveRecord::Wrapper::Base.connection
# Fetch tables name
conn.tables # => ["companies", "news", "webpages"]
# Init a table
table = MassiveRecord::Wrapper::Table.new(conn, :people)
# Add a column family
column = MassiveRecord::Wrapper::ColumnFamily.new(:info)
table.column_families.push(column)
# Or bulk add column families
table.create_column_families([:friends, :misc])
# Create the table
table.save # will raise an exception if the table already exists
# Fetch column families from the database
table.fetch_column_families # => [ColumnFamily#RTY4424, ColumnFamily#R475424, ColumnFamily#GHJ9424]
table.column_families.collect(&:name) # => ["info", "friends", "misc"]
# Add a new row
row = MassiveRecord::Wrapper::Row.new
row.id = "my_unique_id"
row.values = { :info => { :first_name => "H", :last_name => "Base", :email => "h@base.com" } }
row.table = table
row.save
# Fetch rows
table.first # => MassiveRecord#ID1
table.all(:limit => 10) # => [MassiveRecord#ID1, MassiveRecord#ID2, ...]
table.find("ID2") # => MassiveRecord#ID2
table.find(["ID1", "ID2"]) # => [MassiveRecord#ID1, MassiveRecord#ID2]
table.all(:limit => 3, :starts_with => "ID2") # => [MassiveRecord#ID2, MassiveRecord#ID3, MassiveRecord#ID4]
# Manipulate rows
table.first.destroy # => true
# Remove the table
table.destroy

Planned work

  • Cache the decoded values of attributes, not use the value_is_already_decoded?. This will fix possible problem with YAML as coder backend.
  • Implement other Adapters, for instance using jruby and the Java API.

Contribute

If you want to contribute feel free to fork this project :-) Make a feature branch, write test, implement and make a pull request.

Getting started

git clone git://github.com/CompanyBook/massive_record.git (or the address to your fork)
cd massive_record
bundle install

Next up you need to add a config.yml file inside of spec/ which contains something like: host: url.to-a.thrift.server port: 9090 table: massive_record_test_table

You should now be able to run rspec spec/

Play with it in the console

Checkout the massive_record project and install it as a Gem :

cd massive_record/
bundle console
ruby-1.9.2-p0 > Bundler.require
=> [
<Bundler::Dependency type=:runtime name="massive_record" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="thrift" requirements=">= 0.5.0">,
<Bundler::Dependency type=:runtime name="activesupport" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="activemodel" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="rspec" requirements=">= 2.1.0">
]
ruby-1.9.2-p0 > MassiveRecord::VERSION
=> "0.0.1" 

Clean HBase database between each test

We have created a helper module MassiveRecord::Rspec::SimpleDatabaseCleaner which, when included into rspec tests, will clean the database for ORM records between each test case. You can also take a look into spec/support/mock_massive_record_connection.rb for some functionality which will mock a hbase connection making it easier (faster) to test code where no real database is needed.

More Information and Resources

Thrift API

Ruby Library using the HBase Thrift API. http://wiki.apache.org/hadoop/Hbase/ThriftApi

The generated Ruby files can be found under lib/massive_record/thrift/
The whole API (CRUD and more) is present in the Client object (Apache::Hadoop::Hbase::Thrift::Hbase::Client).
The client can be easily initialized using the MassiveRecord connection :

conn = MassiveRecord::Wrapper::Connection.new(:host => 'localhost', :port => 9090)
conn.open
client = conn.client
# Do whatever you want with the client object

Q&A

How to add a new column family to an existing table?

# Connect to the HBase console on the server itself and enter the following code :
disable 'companies'
alter 'companies', { NAME => 'new_collumn_familiy' }
enable 'companies'

Copyright (c) 2011 Companybook, released under the MIT license

About

HBase ruby client

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - firebait/massive_record: HBase ruby client · GitHub
Skip to content

Repository files navigation

Massive Record

Massive Record is a Ruby client for HBase. It provides a basic API through Thrift and an ORM with advanced features.

See introduction to HBase model architecture:
http://wiki.apache.org/hadoop/Hbase/HbaseArchitecture
Understanding terminology of Table / Row / Column family / Column / Cell:
http://jimbojw.com/wiki/index.php?title=Understanding_Hbase_and_BigTable

HBase requirement

MassiveRecord is following the Cloudera packages of HBase: http://www.cloudera.com

Currently, MassiveRecord is tested against HBase 0.90.3, which can be found at the following address: https://ccp.cloudera.com/display/SUPPORT/CDH3+Downloadable+Tarballs

Install HBase (OSX):
Download the package 'HBase 0.90.3+15.3' and extract it.
Start HBase using the following command:

path_to_hbase/bin/start-hbase.sh

Start Thrift (HBase service interface):

path_to_hbase/bin/hbase thrift -b 127.0.0.1 start

Installation

First of all: Please make sure you are using Ruby 1.9.2. For now, we are only ensuring that Massive Record works on that Ruby version, and we know it has some problems with 1.8.7.

gem install massive_record

Ruby on Rails

MassiveRecord is compatible with Rails 3.0. It is not yet fully compatible with 3.1 or any higher versions. Add the following Gems in your Gemfile:

gem 'massive_record'

Create an config/hbase.yml file with the following content:

defaults: &defaults
host: somewhere.compute.amazonaws.com # No 'http', it's a Thrift connection
port: 9090
development:
<<: *defaults
test:
<<: *defaults
production:
<<: *defaults

Usage

There are two ways for using the Massive Record library. At the highest level we have ORM. This is Active Model compliant and makes it easy to use. The second way of doing things is working directly against the adapter (simple API).

ORM

Both MassiveRecord::ORM::Table and MassiveRecord::ORM::Embedded do now have some functionality which you can expect from an ORM. This includes:

  • An initializer which takes attribute hash and assigns them to your object.
  • Write and read methods for the attributes
  • Validations, as you expect from an ActiveRecord.
  • Callbacks, as you expect from an ActiveRecord.
  • Information about changes on attributes.
  • Casting of attributes
  • Serialization of array / hashes
  • Timestamps like created_at and updated_at. Updated at will always be available, created_at must be defined. See example down:
  • Finder scopes. Like: Person.select(:only_columns_from_this_family).limit(10).collect(&:name)
  • Ability to set a default scope.
  • Time zone aware time attributes.
  • Basic instrumentation and logging of query times.
  • Attribute mass assignment security.

Tables also have:

  • Persistencey method calls like create, save and destroy (but they do not actually save things to hbase)
  • Easy access to adapter's connection via Person.connection
  • Easy access to adapter's hbase table via Person.table
  • Finder method, like Person.find("an_id"), Person.find("id1", "id2"), Person.all etc
  • Save / update methods
  • Auto-creation of table and column families on save if table does not exists.
  • Destroy records
  • Relations: Both references to other tables and simple embedded records. See MassiveRecord::ORM::Relations::Interface ClassMethods for documentation
  • Observable. See MassiveRecord::ORM::Observer. If you know how to use ActiveRecord's observer you know how to use this one.
  • IdentityMap (when enabled)

Here are some examples setting up models:

class Person < MassiveRecord::ORM::Table
references_one :boss, :class_name => "Person", :store_in => :info
references_one :attachment, :polymorphic => true
references_many :friends, :store_in => :info
references_many :blog_posts, :records_starts_from => :posts_start_id
embeds_many :addresses
default_scope select(:info)
column_family :info do
field :name
field :email
field :phone_number
field :points, :integer, :default => 0
field :date_of_birth, :date, :allow_nil => false # Defaults to today
field :newsletter, :boolean, :default => false
field :type # Used for single table inheritance
field :in_the_future, :time, :default => Proc.new { 2.hours.from_now }
field :hobbies, :array, :allow_nil => false # Default to empty array
timestamps # ..or field :created_at, :time
end
column_family :misc do
field :with_a_lot_of_uninteresting_data
end
attr_accessible :name, :email, :phone_number, :date_of_birth
validates_presence_of :name, :email
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
# Returns the id the scanner should start from in the BlogPost table
# to fetch blog posts related to this person
def posts_start_id
id+'-'
end
end
class Friend < Person
# This one will be stored in Person's table with it's type set to Friend.
# Calling Person.all will return object back as a Friend.
end
class PersonObserver < MassiveRecord::ORM::Observer
def after_create(person_created)
# Do something smart with that person
end
end
class Address < MassiveRecord::ORM::Embedded
embedded_in :person
field :street
field :number, :integer
field :nice_place, :boolean, :default => true
end
class BlogPost < MassiveRecord::ORM::Embedded
references_one :author, :class_name => "Person", :store_in => :info
field :title
field :content
private
# Set yourself an ID to your model
def default_id
"#{author_id}|#{Time.now.strftime("%Y-%m-%d-%k-%M")}"
end
end

Perform requests:

# Fetch an object
u = User.find("45")
# Blog posts associated
u.blog_posts
# Blog posts associated during May 2011
u.blog_posts(:starts_with => "45-2011-05") # user_id - year - month
# Blog posts from May 2011
u.blog_posts(:offset => "45-2011-05")
# Only five blog posts
u.blog_posts(:limit => 5)

You can find a small example application here: https://github.com/thhermansen/massive_record_test_app

Related gems

We have developed some gems which adds support for MassiveRecord. These are:

ORM Adapter

https://github.com/CompanyBook/orm_adapter Used by Devise. I guess we'll might release the code used to get Devise support in MR.

Database Cleaner

https://github.com/CompanyBook/database_cleaner User by for instance Cucumber and ourself with Rspec.

Sunspot Rails

https://github.com/CompanyBook/sunspot_massive_record Makes it easier to make things searchable with solr.

Wrapper (adapter) API

You can, if you'd like, work directly against the adapter. It is however adviced to use the ORM as the interface to the adapter is not yet very well defined.

# Init a new connection with HBase
conn = MassiveRecord::Wrapper::Connection.new(:host => 'localhost', :port => 9090)
conn.open
# OR init a connection using the config/hbase.yml file with Rails
conn = MassiveRecord::Wrapper::Base.connection
# Fetch tables name
conn.tables # => ["companies", "news", "webpages"]
# Init a table
table = MassiveRecord::Wrapper::Table.new(conn, :people)
# Add a column family
column = MassiveRecord::Wrapper::ColumnFamily.new(:info)
table.column_families.push(column)
# Or bulk add column families
table.create_column_families([:friends, :misc])
# Create the table
table.save # will raise an exception if the table already exists
# Fetch column families from the database
table.fetch_column_families # => [ColumnFamily#RTY4424, ColumnFamily#R475424, ColumnFamily#GHJ9424]
table.column_families.collect(&:name) # => ["info", "friends", "misc"]
# Add a new row
row = MassiveRecord::Wrapper::Row.new
row.id = "my_unique_id"
row.values = { :info => { :first_name => "H", :last_name => "Base", :email => "h@base.com" } }
row.table = table
row.save
# Fetch rows
table.first # => MassiveRecord#ID1
table.all(:limit => 10) # => [MassiveRecord#ID1, MassiveRecord#ID2, ...]
table.find("ID2") # => MassiveRecord#ID2
table.find(["ID1", "ID2"]) # => [MassiveRecord#ID1, MassiveRecord#ID2]
table.all(:limit => 3, :starts_with => "ID2") # => [MassiveRecord#ID2, MassiveRecord#ID3, MassiveRecord#ID4]
# Manipulate rows
table.first.destroy # => true
# Remove the table
table.destroy

Planned work

  • Cache the decoded values of attributes, not use the value_is_already_decoded?. This will fix possible problem with YAML as coder backend.
  • Implement other Adapters, for instance using jruby and the Java API.

Contribute

If you want to contribute feel free to fork this project :-) Make a feature branch, write test, implement and make a pull request.

Getting started

git clone git://github.com/CompanyBook/massive_record.git (or the address to your fork)
cd massive_record
bundle install

Next up you need to add a config.yml file inside of spec/ which contains something like: host: url.to-a.thrift.server port: 9090 table: massive_record_test_table

You should now be able to run rspec spec/

Play with it in the console

Checkout the massive_record project and install it as a Gem :

cd massive_record/
bundle console
ruby-1.9.2-p0 > Bundler.require
=> [
<Bundler::Dependency type=:runtime name="massive_record" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="thrift" requirements=">= 0.5.0">,
<Bundler::Dependency type=:runtime name="activesupport" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="activemodel" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="rspec" requirements=">= 2.1.0">
]
ruby-1.9.2-p0 > MassiveRecord::VERSION
=> "0.0.1" 

Clean HBase database between each test

We have created a helper module MassiveRecord::Rspec::SimpleDatabaseCleaner which, when included into rspec tests, will clean the database for ORM records between each test case. You can also take a look into spec/support/mock_massive_record_connection.rb for some functionality which will mock a hbase connection making it easier (faster) to test code where no real database is needed.

More Information and Resources

Thrift API

Ruby Library using the HBase Thrift API. http://wiki.apache.org/hadoop/Hbase/ThriftApi

The generated Ruby files can be found under lib/massive_record/thrift/
The whole API (CRUD and more) is present in the Client object (Apache::Hadoop::Hbase::Thrift::Hbase::Client).
The client can be easily initialized using the MassiveRecord connection :

conn = MassiveRecord::Wrapper::Connection.new(:host => 'localhost', :port => 9090)
conn.open
client = conn.client
# Do whatever you want with the client object

Q&A

How to add a new column family to an existing table?

# Connect to the HBase console on the server itself and enter the following code :
disable 'companies'
alter 'companies', { NAME => 'new_collumn_familiy' }
enable 'companies'

Copyright (c) 2011 Companybook, released under the MIT license

About

HBase ruby client

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - firebait/massive_record: HBase ruby client · GitHub
Skip to content

Repository files navigation

Massive Record

Massive Record is a Ruby client for HBase. It provides a basic API through Thrift and an ORM with advanced features.

See introduction to HBase model architecture:
http://wiki.apache.org/hadoop/Hbase/HbaseArchitecture
Understanding terminology of Table / Row / Column family / Column / Cell:
http://jimbojw.com/wiki/index.php?title=Understanding_Hbase_and_BigTable

HBase requirement

MassiveRecord is following the Cloudera packages of HBase: http://www.cloudera.com

Currently, MassiveRecord is tested against HBase 0.90.3, which can be found at the following address: https://ccp.cloudera.com/display/SUPPORT/CDH3+Downloadable+Tarballs

Install HBase (OSX):
Download the package 'HBase 0.90.3+15.3' and extract it.
Start HBase using the following command:

path_to_hbase/bin/start-hbase.sh

Start Thrift (HBase service interface):

path_to_hbase/bin/hbase thrift -b 127.0.0.1 start

Installation

First of all: Please make sure you are using Ruby 1.9.2. For now, we are only ensuring that Massive Record works on that Ruby version, and we know it has some problems with 1.8.7.

gem install massive_record

Ruby on Rails

MassiveRecord is compatible with Rails 3.0. It is not yet fully compatible with 3.1 or any higher versions. Add the following Gems in your Gemfile:

gem 'massive_record'

Create an config/hbase.yml file with the following content:

defaults: &defaults
host: somewhere.compute.amazonaws.com # No 'http', it's a Thrift connection
port: 9090
development:
<<: *defaults
test:
<<: *defaults
production:
<<: *defaults

Usage

There are two ways for using the Massive Record library. At the highest level we have ORM. This is Active Model compliant and makes it easy to use. The second way of doing things is working directly against the adapter (simple API).

ORM

Both MassiveRecord::ORM::Table and MassiveRecord::ORM::Embedded do now have some functionality which you can expect from an ORM. This includes:

  • An initializer which takes attribute hash and assigns them to your object.
  • Write and read methods for the attributes
  • Validations, as you expect from an ActiveRecord.
  • Callbacks, as you expect from an ActiveRecord.
  • Information about changes on attributes.
  • Casting of attributes
  • Serialization of array / hashes
  • Timestamps like created_at and updated_at. Updated at will always be available, created_at must be defined. See example down:
  • Finder scopes. Like: Person.select(:only_columns_from_this_family).limit(10).collect(&:name)
  • Ability to set a default scope.
  • Time zone aware time attributes.
  • Basic instrumentation and logging of query times.
  • Attribute mass assignment security.

Tables also have:

  • Persistencey method calls like create, save and destroy (but they do not actually save things to hbase)
  • Easy access to adapter's connection via Person.connection
  • Easy access to adapter's hbase table via Person.table
  • Finder method, like Person.find("an_id"), Person.find("id1", "id2"), Person.all etc
  • Save / update methods
  • Auto-creation of table and column families on save if table does not exists.
  • Destroy records
  • Relations: Both references to other tables and simple embedded records. See MassiveRecord::ORM::Relations::Interface ClassMethods for documentation
  • Observable. See MassiveRecord::ORM::Observer. If you know how to use ActiveRecord's observer you know how to use this one.
  • IdentityMap (when enabled)

Here are some examples setting up models:

class Person < MassiveRecord::ORM::Table
references_one :boss, :class_name => "Person", :store_in => :info
references_one :attachment, :polymorphic => true
references_many :friends, :store_in => :info
references_many :blog_posts, :records_starts_from => :posts_start_id
embeds_many :addresses
default_scope select(:info)
column_family :info do
field :name
field :email
field :phone_number
field :points, :integer, :default => 0
field :date_of_birth, :date, :allow_nil => false # Defaults to today
field :newsletter, :boolean, :default => false
field :type # Used for single table inheritance
field :in_the_future, :time, :default => Proc.new { 2.hours.from_now }
field :hobbies, :array, :allow_nil => false # Default to empty array
timestamps # ..or field :created_at, :time
end
column_family :misc do
field :with_a_lot_of_uninteresting_data
end
attr_accessible :name, :email, :phone_number, :date_of_birth
validates_presence_of :name, :email
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
# Returns the id the scanner should start from in the BlogPost table
# to fetch blog posts related to this person
def posts_start_id
id+'-'
end
end
class Friend < Person
# This one will be stored in Person's table with it's type set to Friend.
# Calling Person.all will return object back as a Friend.
end
class PersonObserver < MassiveRecord::ORM::Observer
def after_create(person_created)
# Do something smart with that person
end
end
class Address < MassiveRecord::ORM::Embedded
embedded_in :person
field :street
field :number, :integer
field :nice_place, :boolean, :default => true
end
class BlogPost < MassiveRecord::ORM::Embedded
references_one :author, :class_name => "Person", :store_in => :info
field :title
field :content
private
# Set yourself an ID to your model
def default_id
"#{author_id}|#{Time.now.strftime("%Y-%m-%d-%k-%M")}"
end
end

Perform requests:

# Fetch an object
u = User.find("45")
# Blog posts associated
u.blog_posts
# Blog posts associated during May 2011
u.blog_posts(:starts_with => "45-2011-05") # user_id - year - month
# Blog posts from May 2011
u.blog_posts(:offset => "45-2011-05")
# Only five blog posts
u.blog_posts(:limit => 5)

You can find a small example application here: https://github.com/thhermansen/massive_record_test_app

Related gems

We have developed some gems which adds support for MassiveRecord. These are:

ORM Adapter

https://github.com/CompanyBook/orm_adapter Used by Devise. I guess we'll might release the code used to get Devise support in MR.

Database Cleaner

https://github.com/CompanyBook/database_cleaner User by for instance Cucumber and ourself with Rspec.

Sunspot Rails

https://github.com/CompanyBook/sunspot_massive_record Makes it easier to make things searchable with solr.

Wrapper (adapter) API

You can, if you'd like, work directly against the adapter. It is however adviced to use the ORM as the interface to the adapter is not yet very well defined.

# Init a new connection with HBase
conn = MassiveRecord::Wrapper::Connection.new(:host => 'localhost', :port => 9090)
conn.open
# OR init a connection using the config/hbase.yml file with Rails
conn = MassiveRecord::Wrapper::Base.connection
# Fetch tables name
conn.tables # => ["companies", "news", "webpages"]
# Init a table
table = MassiveRecord::Wrapper::Table.new(conn, :people)
# Add a column family
column = MassiveRecord::Wrapper::ColumnFamily.new(:info)
table.column_families.push(column)
# Or bulk add column families
table.create_column_families([:friends, :misc])
# Create the table
table.save # will raise an exception if the table already exists
# Fetch column families from the database
table.fetch_column_families # => [ColumnFamily#RTY4424, ColumnFamily#R475424, ColumnFamily#GHJ9424]
table.column_families.collect(&:name) # => ["info", "friends", "misc"]
# Add a new row
row = MassiveRecord::Wrapper::Row.new
row.id = "my_unique_id"
row.values = { :info => { :first_name => "H", :last_name => "Base", :email => "h@base.com" } }
row.table = table
row.save
# Fetch rows
table.first # => MassiveRecord#ID1
table.all(:limit => 10) # => [MassiveRecord#ID1, MassiveRecord#ID2, ...]
table.find("ID2") # => MassiveRecord#ID2
table.find(["ID1", "ID2"]) # => [MassiveRecord#ID1, MassiveRecord#ID2]
table.all(:limit => 3, :starts_with => "ID2") # => [MassiveRecord#ID2, MassiveRecord#ID3, MassiveRecord#ID4]
# Manipulate rows
table.first.destroy # => true
# Remove the table
table.destroy

Planned work

  • Cache the decoded values of attributes, not use the value_is_already_decoded?. This will fix possible problem with YAML as coder backend.
  • Implement other Adapters, for instance using jruby and the Java API.

Contribute

If you want to contribute feel free to fork this project :-) Make a feature branch, write test, implement and make a pull request.

Getting started

git clone git://github.com/CompanyBook/massive_record.git (or the address to your fork)
cd massive_record
bundle install

Next up you need to add a config.yml file inside of spec/ which contains something like: host: url.to-a.thrift.server port: 9090 table: massive_record_test_table

You should now be able to run rspec spec/

Play with it in the console

Checkout the massive_record project and install it as a Gem :

cd massive_record/
bundle console
ruby-1.9.2-p0 > Bundler.require
=> [
<Bundler::Dependency type=:runtime name="massive_record" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="thrift" requirements=">= 0.5.0">,
<Bundler::Dependency type=:runtime name="activesupport" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="activemodel" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="rspec" requirements=">= 2.1.0">
]
ruby-1.9.2-p0 > MassiveRecord::VERSION
=> "0.0.1" 

Clean HBase database between each test

We have created a helper module MassiveRecord::Rspec::SimpleDatabaseCleaner which, when included into rspec tests, will clean the database for ORM records between each test case. You can also take a look into spec/support/mock_massive_record_connection.rb for some functionality which will mock a hbase connection making it easier (faster) to test code where no real database is needed.

More Information and Resources

Thrift API

Ruby Library using the HBase Thrift API. http://wiki.apache.org/hadoop/Hbase/ThriftApi

The generated Ruby files can be found under lib/massive_record/thrift/
The whole API (CRUD and more) is present in the Client object (Apache::Hadoop::Hbase::Thrift::Hbase::Client).
The client can be easily initialized using the MassiveRecord connection :

conn = MassiveRecord::Wrapper::Connection.new(:host => 'localhost', :port => 9090)
conn.open
client = conn.client
# Do whatever you want with the client object

Q&A

How to add a new column family to an existing table?

# Connect to the HBase console on the server itself and enter the following code :
disable 'companies'
alter 'companies', { NAME => 'new_collumn_familiy' }
enable 'companies'

Copyright (c) 2011 Companybook, released under the MIT license

About

HBase ruby client

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - firebait/massive_record: HBase ruby client · GitHub
Skip to content

Repository files navigation

Massive Record

Massive Record is a Ruby client for HBase. It provides a basic API through Thrift and an ORM with advanced features.

See introduction to HBase model architecture:
http://wiki.apache.org/hadoop/Hbase/HbaseArchitecture
Understanding terminology of Table / Row / Column family / Column / Cell:
http://jimbojw.com/wiki/index.php?title=Understanding_Hbase_and_BigTable

HBase requirement

MassiveRecord is following the Cloudera packages of HBase: http://www.cloudera.com

Currently, MassiveRecord is tested against HBase 0.90.3, which can be found at the following address: https://ccp.cloudera.com/display/SUPPORT/CDH3+Downloadable+Tarballs

Install HBase (OSX):
Download the package 'HBase 0.90.3+15.3' and extract it.
Start HBase using the following command:

path_to_hbase/bin/start-hbase.sh

Start Thrift (HBase service interface):

path_to_hbase/bin/hbase thrift -b 127.0.0.1 start

Installation

First of all: Please make sure you are using Ruby 1.9.2. For now, we are only ensuring that Massive Record works on that Ruby version, and we know it has some problems with 1.8.7.

gem install massive_record

Ruby on Rails

MassiveRecord is compatible with Rails 3.0. It is not yet fully compatible with 3.1 or any higher versions. Add the following Gems in your Gemfile:

gem 'massive_record'

Create an config/hbase.yml file with the following content:

defaults: &defaults
host: somewhere.compute.amazonaws.com # No 'http', it's a Thrift connection
port: 9090
development:
<<: *defaults
test:
<<: *defaults
production:
<<: *defaults

Usage

There are two ways for using the Massive Record library. At the highest level we have ORM. This is Active Model compliant and makes it easy to use. The second way of doing things is working directly against the adapter (simple API).

ORM

Both MassiveRecord::ORM::Table and MassiveRecord::ORM::Embedded do now have some functionality which you can expect from an ORM. This includes:

  • An initializer which takes attribute hash and assigns them to your object.
  • Write and read methods for the attributes
  • Validations, as you expect from an ActiveRecord.
  • Callbacks, as you expect from an ActiveRecord.
  • Information about changes on attributes.
  • Casting of attributes
  • Serialization of array / hashes
  • Timestamps like created_at and updated_at. Updated at will always be available, created_at must be defined. See example down:
  • Finder scopes. Like: Person.select(:only_columns_from_this_family).limit(10).collect(&:name)
  • Ability to set a default scope.
  • Time zone aware time attributes.
  • Basic instrumentation and logging of query times.
  • Attribute mass assignment security.

Tables also have:

  • Persistencey method calls like create, save and destroy (but they do not actually save things to hbase)
  • Easy access to adapter's connection via Person.connection
  • Easy access to adapter's hbase table via Person.table
  • Finder method, like Person.find("an_id"), Person.find("id1", "id2"), Person.all etc
  • Save / update methods
  • Auto-creation of table and column families on save if table does not exists.
  • Destroy records
  • Relations: Both references to other tables and simple embedded records. See MassiveRecord::ORM::Relations::Interface ClassMethods for documentation
  • Observable. See MassiveRecord::ORM::Observer. If you know how to use ActiveRecord's observer you know how to use this one.
  • IdentityMap (when enabled)

Here are some examples setting up models:

class Person < MassiveRecord::ORM::Table
references_one :boss, :class_name => "Person", :store_in => :info
references_one :attachment, :polymorphic => true
references_many :friends, :store_in => :info
references_many :blog_posts, :records_starts_from => :posts_start_id
embeds_many :addresses
default_scope select(:info)
column_family :info do
field :name
field :email
field :phone_number
field :points, :integer, :default => 0
field :date_of_birth, :date, :allow_nil => false # Defaults to today
field :newsletter, :boolean, :default => false
field :type # Used for single table inheritance
field :in_the_future, :time, :default => Proc.new { 2.hours.from_now }
field :hobbies, :array, :allow_nil => false # Default to empty array
timestamps # ..or field :created_at, :time
end
column_family :misc do
field :with_a_lot_of_uninteresting_data
end
attr_accessible :name, :email, :phone_number, :date_of_birth
validates_presence_of :name, :email
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
# Returns the id the scanner should start from in the BlogPost table
# to fetch blog posts related to this person
def posts_start_id
id+'-'
end
end
class Friend < Person
# This one will be stored in Person's table with it's type set to Friend.
# Calling Person.all will return object back as a Friend.
end
class PersonObserver < MassiveRecord::ORM::Observer
def after_create(person_created)
# Do something smart with that person
end
end
class Address < MassiveRecord::ORM::Embedded
embedded_in :person
field :street
field :number, :integer
field :nice_place, :boolean, :default => true
end
class BlogPost < MassiveRecord::ORM::Embedded
references_one :author, :class_name => "Person", :store_in => :info
field :title
field :content
private
# Set yourself an ID to your model
def default_id
"#{author_id}|#{Time.now.strftime("%Y-%m-%d-%k-%M")}"
end
end

Perform requests:

# Fetch an object
u = User.find("45")
# Blog posts associated
u.blog_posts
# Blog posts associated during May 2011
u.blog_posts(:starts_with => "45-2011-05") # user_id - year - month
# Blog posts from May 2011
u.blog_posts(:offset => "45-2011-05")
# Only five blog posts
u.blog_posts(:limit => 5)

You can find a small example application here: https://github.com/thhermansen/massive_record_test_app

Related gems

We have developed some gems which adds support for MassiveRecord. These are:

ORM Adapter

https://github.com/CompanyBook/orm_adapter Used by Devise. I guess we'll might release the code used to get Devise support in MR.

Database Cleaner

https://github.com/CompanyBook/database_cleaner User by for instance Cucumber and ourself with Rspec.

Sunspot Rails

https://github.com/CompanyBook/sunspot_massive_record Makes it easier to make things searchable with solr.

Wrapper (adapter) API

You can, if you'd like, work directly against the adapter. It is however adviced to use the ORM as the interface to the adapter is not yet very well defined.

# Init a new connection with HBase
conn = MassiveRecord::Wrapper::Connection.new(:host => 'localhost', :port => 9090)
conn.open
# OR init a connection using the config/hbase.yml file with Rails
conn = MassiveRecord::Wrapper::Base.connection
# Fetch tables name
conn.tables # => ["companies", "news", "webpages"]
# Init a table
table = MassiveRecord::Wrapper::Table.new(conn, :people)
# Add a column family
column = MassiveRecord::Wrapper::ColumnFamily.new(:info)
table.column_families.push(column)
# Or bulk add column families
table.create_column_families([:friends, :misc])
# Create the table
table.save # will raise an exception if the table already exists
# Fetch column families from the database
table.fetch_column_families # => [ColumnFamily#RTY4424, ColumnFamily#R475424, ColumnFamily#GHJ9424]
table.column_families.collect(&:name) # => ["info", "friends", "misc"]
# Add a new row
row = MassiveRecord::Wrapper::Row.new
row.id = "my_unique_id"
row.values = { :info => { :first_name => "H", :last_name => "Base", :email => "h@base.com" } }
row.table = table
row.save
# Fetch rows
table.first # => MassiveRecord#ID1
table.all(:limit => 10) # => [MassiveRecord#ID1, MassiveRecord#ID2, ...]
table.find("ID2") # => MassiveRecord#ID2
table.find(["ID1", "ID2"]) # => [MassiveRecord#ID1, MassiveRecord#ID2]
table.all(:limit => 3, :starts_with => "ID2") # => [MassiveRecord#ID2, MassiveRecord#ID3, MassiveRecord#ID4]
# Manipulate rows
table.first.destroy # => true
# Remove the table
table.destroy

Planned work

  • Cache the decoded values of attributes, not use the value_is_already_decoded?. This will fix possible problem with YAML as coder backend.
  • Implement other Adapters, for instance using jruby and the Java API.

Contribute

If you want to contribute feel free to fork this project :-) Make a feature branch, write test, implement and make a pull request.

Getting started

git clone git://github.com/CompanyBook/massive_record.git (or the address to your fork)
cd massive_record
bundle install

Next up you need to add a config.yml file inside of spec/ which contains something like: host: url.to-a.thrift.server port: 9090 table: massive_record_test_table

You should now be able to run rspec spec/

Play with it in the console

Checkout the massive_record project and install it as a Gem :

cd massive_record/
bundle console
ruby-1.9.2-p0 > Bundler.require
=> [
<Bundler::Dependency type=:runtime name="massive_record" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="thrift" requirements=">= 0.5.0">,
<Bundler::Dependency type=:runtime name="activesupport" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="activemodel" requirements=">= 0">,
<Bundler::Dependency type=:runtime name="rspec" requirements=">= 2.1.0">
]
ruby-1.9.2-p0 > MassiveRecord::VERSION
=> "0.0.1" 

Clean HBase database between each test

We have created a helper module MassiveRecord::Rspec::SimpleDatabaseCleaner which, when included into rspec tests, will clean the database for ORM records between each test case. You can also take a look into spec/support/mock_massive_record_connection.rb for some functionality which will mock a hbase connection making it easier (faster) to test code where no real database is needed.

More Information and Resources

Thrift API

Ruby Library using the HBase Thrift API. http://wiki.apache.org/hadoop/Hbase/ThriftApi

The generated Ruby files can be found under lib/massive_record/thrift/
The whole API (CRUD and more) is present in the Client object (Apache::Hadoop::Hbase::Thrift::Hbase::Client).
The client can be easily initialized using the MassiveRecord connection :

conn = MassiveRecord::Wrapper::Connection.new(:host => 'localhost', :port => 9090)
conn.open
client = conn.client
# Do whatever you want with the client object

Q&A

How to add a new column family to an existing table?

# Connect to the HBase console on the server itself and enter the following code :
disable 'companies'
alter 'companies', { NAME => 'new_collumn_familiy' }
enable 'companies'

Copyright (c) 2011 Companybook, released under the MIT license

About

HBase ruby client

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors