BB
(b_b) is SQL Query Builder for Google BigQuery
Install
Add the following line to Gemfile:
gem 'b_b'
and run bundle
from your shell.
To install the gem manually from your shell, run:
gem install b_b
Basic usage
(b_b) can build only SQL SELECT Statement.
BB.select("word", "corpus", "COUNT(word)").
from("publicdata:samples.shakespeare").
where(word_cont: "th").
group(:word, :corpus).
to_sql
# => "SELECT word, corpus, COUNT(word) FROM publicdata:samples.shakespeare WHERE (word CONTAINS 'th') GROUP BY word, corpus"
Query Reference of BigQuery's query syntax and functions is here.
Examples
L(b_b)z ==============33
SELECT clause
BB.select(:id, :name, :state).to_sql
# => "SELECT id, name, state"
BB.select("id", "name", "COUNT(*)").to_sql
# => "SELECT id, name, COUNT(*)"
FROM clause
BB.from("publicdata:samples.shakespeare").to_sql
# => "SELECT * FROM publicdata:samples.shakespeare"
BB.from("[applogs.events_20120501]", "[applogs.events_20120502]", "[applogs.events_20120503]").to_sql
# => "SELECT * FROM [applogs.events_20120501], [applogs.events_20120502], [applogs.events_20120503]"
BB.from("applogs.events_", on: Date.new(2012, 5, 1)).to_sql
# => "SELECT * FROM applogs.events_20120501"
BB.from("mydata.people", from: Date.new(2014, 3, 25), to: Date.new(2014, 3, 27)).to_sql
# => "SELECT * FROM TABLE_DATE_RANGE(mydata.people, TIMESTAMP('2014-03-25'), TIMESTAMP('2014-03-27'))"
BB.from(BB.from("publicdata:samples.shakespeare"), as: shakespeare).to_sql
# => "SELECT * FROM (SELECT * FROM publicdata:samples.shakespeare) AS shakespeare"
JOIN clause
BB.from(:customers, as: :t1).inner_join(:orders, as: :t2).on("t1.customer_id = t2.customer_id").to_sql
# => "SELECT * FROM customers AS t1 INNER JOIN orders AS t2 ON t1.customer_id = t2.customer_id"
BB.from(:customers, as: :t1).join_each(BB.select(:id, :name).from(:orders), as: :t2).on("t1.customer_id = t2.customer_id").to_sql
# => "SELECT * FROM customers AS t1 JOIN EACH (SELECT id, name FROM orders) AS t2 ON t1.customer_id = t2.customer_id"
WHERE clause
BB.where(id: 1..10, name: "donald", flag: false).to_sql
# => "WHERE (id BETWEEN 1 AND 10 AND name = 'donald' AND flag IS false)"
BB.where("id = ? OR name CONTAINS ?", 123, "john").to_sql
# => "WHERE (id = 123 OR name CONTAINS 'john')"
BB.where("id = :id AND name <> :name", id: 123, name: "trump").to_sql
# => "WHERE (id = 123 AND name <> 'trump')"
BB.where(id_gteq: 123, name_not_cont: "melania").to_sql
# => "WHERE (id >= 123 AND NOT name CONTAINS 'melania')"
BB.where(id: 123).or.where(id: 456).to_sql
# => "WHERE (id = 123) OR (id = 456)"
BB.not.where(id: 123).or.not.where(id: 456).to_sql
# => "WHERE (id <> 123) OR (id <> 456)"
BB.where(id: 123, name: "trump", reduce: :or).to_sql
# => "WHERE (id = 123 OR name = 'trump')"
OMIT RECORD IF clause
BB.omit_record_if("COUNT(payload.pages.page_name) <= ?", 80).to_sql
# => "OMIT RECORD IF (COUNT(payload.pages.page_name) <= 80)"
GROUP BY clause
BB.group(:age, :gender).to_sql
# => "GROUP BY age, gender"
BB.group("ROLLUP(year, is_male)").to_sql
# => "GROUP BY ROLLUP(year, is_male)"
BB.group_each(:age, :gender).to_sql
# => "GROUP EACH BY age, gender"
HAVING clause
BB.having(first_cont: "a", ngram_count_lt: 10000).to_sql
# => "HAVING (first CONTAINS 'a' AND ngram_count < 10000)"
BB.having("first CONTAINS ? AND negram_count < ?", "a", 10000).to_sql
# => "HAVING (first CONTAINS 'a' AND ngram_count < 10000)"
BB.having("first CONTAINS :first AND negram_count < :negram_count", first: "a", negram_count: 10000).to_sql
# => "HAVING (first CONTAINS 'a' AND ngram_count < 10000)"
BB.having(first: "a").or.not.having(first: "b").to_sql
# => "HAVING (first = 'a') OR (first <> 'b')"
ORDER BY clause
BB.order(:age, :gender).to_sql
# => "ORDER BY age, gender"
BB.order(age: :desc, gender: :asc).to_sql
# => "ORDER BY age DESC, gender ASC"
LIMIT clause
BB.limit(1000).to_sql
# => "LIMIT 1000"
BB.limit(1000).offset(500).to_sql
# => "LIMIT 1000 OFFSET 500"
Support
JOINS
Support methods:
- cross_join
- full_outer_join_each
- inner_join
- inner_join_each
- join
- join_each
- left_join
- left_join_each
- left_outer_join
- left_outer_join_each
- right_join
- right_join_each
- right_outer_join
- right_outer_join_each
Suffix of hash keys
For omit_record_if
, where
, having
:
suffix | means | alias | opposite | example |
---|---|---|---|---|
cont | contains |
contains , like
|
not_cont , not_contains , not_like
|
BB.where(name_cont: "banana") |
eq | equals |
eql , equals
|
not_eq , not_eql , not_equals
|
BB.where(id_not_eq: 123) |
gt | greater than | undefined | not_gt |
BB.where(id_not_gt: 123) |
gteq | greater than or equals to | undefined | not_gteq |
BB.where(id_not_gteq: 123) |
lt | less than | undefined | not_lt |
BB.where(id_not_lt: 123) |
lteq | less than or equals to | undefined | not_lteq |
BB.where(id_not_lteq: 123) |
Contributing
Here's a quick guide:
-
Fork the repo.
-
Create a thoughtfully-named branch for your changes (
git checkout -b my-new-feature
). -
Install the development dependencies by running
bundle install
. -
Begin by running the tests.
$ bundle exec rspec
-
Implement something.
-
Add tests for your changes.
-
Make the tests pass.
-
Commit your changes (
git commit -am 'Add feature/Fix bug/improve something'
) -
Push the branch up to your fork on GitHub (
git push origin my-new-feature
) and from GitHub submit a pull request to b_b'smaster
branch.
License
The gem is available as open source under the terms of the MIT License.