“ Loading latest tweet”
You got NoSQL in my Postgres! Using Hstore in Rails
Heroku just announced their support of hstore in their dedicated Postgres 9.1 instances. Hstore is a schema less key value store inside of PostgreSQL that allows us to store data like hashes directly inside of a column. It’s great for when you don’t know exactly what types of attributes you need to store on a model, or if you need to support many different attributes for the same model.
A good example is storing attributes for a Product model. We might start out only selling books, which have an author, number of pages, but then transition over to selling laptops which have cpu speed and display resolution. Using Hstore allows us to easily store all these values without having to make a bunch mostly blank columns.
To get started with Rails and hstore you can watch the screencast below or visit the hstore example app running on Heroku.
More on Hstore
Hstore in Rails functions much like serializing hashes, except that we can query our data much faster since hstore is a native data type. It is supported natively in Rails 4, but until then we’ll need to use the activerecord-postgres-hstore gem.
Getting Started
You will need a version of PostgreSQL locally that supports the hstore extension. I recommend installing postgres using homebrew on OS X. Once you’ve done that you can enable hstore usage by running this in Postgres
CREATE EXTENSION hstore;
You can put this in a migration if you prefer
class SetupHstore < ActiveRecord::Migration
def self.up
execute "CREATE EXTENSION hstore"
end
def self.down
execute "DROP EXTENSION hstore"
end
end
Once that is done you will need to create a column with a type of hstore, here we are giving our Product model a column called data
with hstore type.
class CreateProducts < ActiveRecord::Migration
def change
create_table :products do |t|
t.string :name
t.hstore :data
t.timestamps
end
end
end
Once we’ve done that we can now store any type of attributes in the data column.
Product.create(:name => "Geek Love: A Novel", :data => {'author' => 'Katherine Dunn', 'pages' => 368, 'category' => 'fiction'})
Product.last.data['category'] # => 'fiction'
Querying
Not only does hstore allow us to store arbitrary keys and values it allows us to quickly query them.
# Find all products that have a key of 'author' in data
Product.where("data ? :key", :key => 'author')
# Find all products that have a 'pages' and '368' key value pair in data
Product.where("data @> (:key => :value)", :key => 'pages', :value => '368')
# Find all products that don't have a key value pair 'pages' and '999' in data
Product.where("not data @> (:key => :value)", :key => 'pages', :value => '999')
# Find all products having key 'author' and value like 'ba' in data
Product.where("data -> :key LIKE :value", :key => 'author, :value => "%Kat%")
More information available in the Postgres hstore docs.
Though like a normal column if you query it frequently, you can get even more speed by adding an index. You can do this using one of two indexes that also speed up full text searches. They’re GiST (Generalized Search Tree) or GIN (Generalized Inverted iNdex). Which sill speed up queries using the @>
and ?
postgres operators.
class Index < ActiveRecord::Migration
def up
execute "CREATE INDEX products_gin_data ON products USING GIN(data)"
end
def down
execute "DROP INDEX products_gin_data"
end
end
Use It
Try out the hstore example app, clone the Github repo, and let me know what cool things you build on twitter @schneems.
Thanks
Special thanks to Aaron Patterson and Joel Hoffman for their work with hstore & Rails4, to the team at Softa for writing this gem, & and the team at Heroku for their contributions to Postgres, and supporting this feature.
- 1 day ago
- 3 notes
-
Share
Tweet
-
- heroku
- postgres
- hstore
- rails
- ruby on rails
Get Down with Heroku at SXSW this year register now. It’s gonna be awesome.
- 1 week ago
- 1 note
-
Share
Tweet
-
- SXSW
- heroku
- party
I love doing screencasts, but hate the way my MBP mic sounds, so I got a Rode Podcaster mic. You can see the difference in this quick video. All sound is raw and un-edited. I’m pretty happy with the purchase :)
- 1 week ago
-
Share
Tweet
Test Drive: Induction - The Everything Database Client for Mac
Join me for a quick demo of Induction, the latest project by @Mattt from Heroku. Induction will let you view your data-stores including: PostgreSQL, MySQL, SQLite, Redis, MongoDB, and so much more. The alpha product also lets you run queries, and visualize data.
Induction is completely free and open source, for more info visit the website or clone the repo.
- 1 week ago
- 1 note
-
Share
Tweet
-
- osx
- induction
- database
- nosql
- mac
- client
Give Tumblr Some Code <3 with Prettify.js
Lets admit it, code and Tumblr don’t exactly get along right now. Sure you can write your posts in markdown, but there isn’t really an out of the box experience for syntax highlighting in code blocks.
In the past I’ve resorted to using Gists to store code in my blog, but this can be a hassle to manage, especially if you need to modify your code later. One day I noticed that one of my favorite blogs The Changelog has great code highlighting on their Tumblr blog. With a little view source-ing and a little googling I eventually found out that the best way to add code highlighting to your blog is through a javascript library called prettify.js
. I followed these directions by blairvanderhoof, and came up with something not too bad. While his instructions are good, I made a few modifications. Lets take a look.
Link to the prettify.js CDN in your html, by customizing and putting this in right before the
<body>
tag:<script type="text/javascript" src="/img/spacer.gif">
Add an the ‘prettyprint’ class to all
<pre>
tags that contain a<code>
tag. Then initialize theprettyPrint()
javascript method by adding this in your HTML:$(document).ready( function(){ $('code').parent('pre').addClass('prettyprint'); prettyPrint(); });
Pick a prettify theme. I chose the Desert theme. Add that style to the
Add Custom CSS
under the advanced section, or directly to your HTML./* desert scheme ported from vim to google prettify */ pre { display: block; background-color: #333; overflow:scroll; px; } pre .nocode { background-color: none; color: #000 } pre .str { color: #ffa0a0 } /* string - pink */ pre .kwd { color: #f0e68c; font-weight: bold } pre .com { color: #87ceeb } /* comment - skyblue */ pre .typ { color: #98fb98 } /* type - lightgreen */ pre .lit { color: #cd5c5c } /* literal - darkred */ pre .pun { color: #fff } /* punctuation */ pre .pln { color: #fff } /* plaintext */ pre .tag { color: #f0e68c; font-weight: bold } /* html/xml tag - lightyellow */ pre .atn { color: #bdb76b; font-weight: bold } /* attribute name - khaki */ pre .atv { color: #ffa0a0 } /* attribute value - pink */ pre .dec { color: #98fb98 } /* decimal - lightgreen */ /* Specify class=linenums on a pre to get line numbering */ ol.linenums { margin-top: 0; margin-bottom: 0; color: #AEAEAE } /* IE indents via margin-left */ li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8 { list-style-type: none } /* Alternate shading for lines */ li.L1,li.L3,li.L5,li.L7,li.L9 { } @media print { pre { background-color: none } pre .str, code .str { color: #060 } pre .kwd, code .kwd { color: #006; font-weight: bold } pre .com, code .com { color: #600; font-style: italic } pre .typ, code .typ { color: #404; font-weight: bold } pre .lit, code .lit { color: #044 } pre .pun, code .pun { color: #440 } pre .pln, code .pln { color: #000 } pre .tag, code .tag { color: #006; font-weight: bold } pre .atn, code .