Rasti::DB
Database collections and relations
Installation
Add this line to your application's Gemfile:
gem 'rasti-db'
And then execute:
$ bundle
Or install it yourself as:
$ gem install rasti-db
Usage
Database connection
DB_1 = Sequel.connect ...
DB_2 = Sequel.connect ...
Database schema
DB_1.create_table :users do
primary_key :id
String :name, null: false, unique: true
end
DB_1.create_table :posts do
primary_key :id
String :title, null: false, unique: true
String :body, null: false
Integer :language_id, null: false, index: true
foreign_key :user_id, :users, null: false, index: true
end
DB_1.create_table :comments do
primary_key :id
String :text, null: false
foreign_key :user_id, :users, null: false, index: true
foreign_key :post_id, :posts, null: false, index: true
end
DB_1.create_table :categories do
primary_key :id
String :name, null: false, unique: true
end
DB_1.create_table :categories_posts do
foreign_key :category_id, :categories, null: false, index: true
foreign_key :post_id, :posts, null: false, index: true
primary_key [:category_id, :post_id]
end
DB_1.create_table :people do
String :document_number, null: false, primary_key: true
String :first_name, null: false
String :last_name, null: false
Date :birth_date, null: false
foreign_key :user_id, :users, null: false, unique: true
end
DB_1.create_table :languages_people do
Integer :language_id, null: false, index: true
foreign_key :document_number, :people, type: String, null: false, index: true
primary_key [:language_id, :document_number]
end
DB_2.create_table :languages do
primary_key :id
String :name, null: false, unique: true
end
Models
User = Rasti::DB::Model[:id, :name, :posts, :comments, :person]
Post = Rasti::DB::Model[:id, :title, :body, :user_id, :user, :comments, :categories]
Comment = Rasti::DB::Model[:id, :text, :user_id, :user, :post_id, :post]
Category = Rasti::DB::Model[:id, :name, :posts]
Person = Rasti::DB::Model[:document_number, :first_name, :last_name, :full_name, :birth_date, :user_id, :user]
Language = Rasti::DB::Model[:id, :name, :people]
Collections
class Users < Rasti::DB::Collection
one_to_many :posts
one_to_many :comments
one_to_one :person
end
class Posts < Rasti::DB::Collection
many_to_one :user
many_to_many :categories
one_to_many :comments
query :created_by do |user_id|
where user_id: user_id
end
query :entitled, -> (title) { where title: title }
query :commented_by do |user_id|
chainable do
dataset.join(qualify(:comments), post_id: :id)
.where(Sequel[:comments][:user_id] => user_id)
.select_all(:posts)
.distinct
end
end
end
class Comments < Rasti::DB::Collection
many_to_one :user
many_to_one :post
end
class Categories < Rasti::DB::Collection
many_to_many :posts
end
class People < Rasti::DB::Collection
set_collection_name :people
set_primary_key :document_number
set_foreign_key :document_number
set_model Person
many_to_one :user
many_to_many :languages
computed_attribute :full_name do
Rasti::DB::ComputedAttribute.new Sequel.join([:first_name, ' ', :last_name])
end
end
class Languages < Rasti::DB::Collection
set_data_source_name :other
one_to_many :posts
many_to_many :people, collection: People, relation_data_source_name: :default
end
environment = Rasti::DB::Environment.new default: Rasti::DB::DataSource.new(DB_1),
other: Rasti::DB::DataSource.new(DB_2, 'custom_schema')
users = Users.new environment
posts = Posts.new environment
comments = Comments.new environment
categories = Categories.new environment
people = People.new environment
Persistence
DB_1.transaction do
id = users.insert name: 'User 1'
users.update id, name: 'User updated'
users.delete id
users.bulk_insert [{name: 'User 1'}, {name: 'User 2'}]
users.bulk_update(name: 'User updated') { where id: [1,2] }
users.bulk_delete { where id: [1,2] }
posts.insert_relations 1, categories: [2,3]
posts.delete_relations 1, categories: [2,3]
end
Queries
posts.all # => [Post, ...]
posts.first # => Post
posts.count # => 1
posts.where(id: [1,2]) # => [Post, ...]
posts.where{id > 1}.limit(10).offset(20) } # => [Post, ...]
posts.where(id: [1,2]).raw # => [{id:1, ...}, {id:2, ...}]
posts.where(id: [1,2]).primary_keys # => [1,2]
posts.where(id: [1,2]).pluck(:id) # => [1,2]
posts.where(id: [1,2]).pluck(:id, :title) # => [[1, ...], [2, ...]]
posts.created_by(1) # => [Post, ...]
posts.created_by(1).entitled('...').commented_by(2) # => [Post, ...]
posts.with_categories([1,2]) # => [Post, ...]
posts.graph(:user, :language, :categories, 'comments.user') # => [Post(User, Language, [Categories, ...], [Comments(User)]), ...]
posts.join(:user).where(name: 'User 4') # => [Post, ...]
posts.select_attributes(:id, :title) # => [Post, ...]
posts.exclude_attributes(:id, :title) # => [Post, ...]
posts.all_attributes # => [Post, ...]
posts.graph('user.person').select_graph_attributes(user: [:id], 'user.person': [:last_name, :user_id]) # => [Post, ...]
posts.graph('user.person').exclude_graph_attributes(user: [:name], 'user.person': [:first_name, :last_name]) # => [Post, ...]
posts.graph('user.person').all_graph_attributes('user.person') # => [Post, ...]
posts.each { |post| do_something post } # Iterate posts loading all at first
posts.each(batch_size: 1000) { |post| do_something post } # Iterate posts loading in batches
posts.each_batch(size: 1000) { |posts| do_something posts } # Iterate batches of posts
Natural Query Language
posts.nql('id = 1') # => Equal
posts.nql('id != 1') # => Not equal
posts.nql('title: My post') # => Include
posts.nql('title !: My post') # => Not include
posts.nql('title ~ My post') # => Insensitive like
posts.nql('id > 1') # => Greater
posts.nql('id >= 1') # => Greater or equal
posts.nql('id < 10') # => Less
posts.nql('id <= 10') # => Less or equal
posts.nql('id = 1 | id = 2') # => Or
posts.nql('id > 1 & title: "My post"') # => And
posts.nql('(id > 3 & id < 10) | title: "My post"') # => Precedence
posts.nql('comments.user.person.document_number = 7') # => Nested
Development
Rasti::DB uses treetop to perform queries using natural language. To recompile the syntax, simply run the following command in lib/rasti/db/nql
:
tt syntax.treetop -o syntax.rb
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/gabynaiman/rasti-db.
License
The gem is available as open source under the terms of the MIT License.