June 21, 2010
Rails 3 is in final finishing stage (currently in beta4) and therefore I released new Oracle enhanced adapter version 1.3.0 which I was working on during last months.
Rails 3 compatibility
The major enhancement is that Oracle enhanced adapter is now compatible with Rails 3. To achieve that I also developed Oracle SQL compiler for Arel gem which is used now by ActiveRecord to generate SQL statements. When using Oracle enhanced adapter with Rails 3 you will notice several major changes:
- Table and column names are always quoted and in uppercase to avoid the need for checking Oracle reserved words.
Post.allwill generate query
SELECT "POSTS".* FROM "POSTS"
- Better support for limit and offset options (when possible just ROWNUM condition in WHERE clause is used without using subqueries).
Post.limit(1)) will generate query
SELECT "POSTS".* FROM "POSTS" WHERE ROWNUM <= 1
select * from (select raw_sql_.*, rownum raw_rnum_ from (SELECT "EMPLOYEES".* FROM "EMPLOYEES") raw_sql_ where rownum <= 2) where raw_rnum_ > 1
When using Oracle enhanced adapter with current version of Rails 3 and Arel it is necessary to turn on table and column caching option in all environments as otherwise Arel gem will cause very many SQL queries on data dictionary tables on each request. To achieve that you need to include in some initializer file:
ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.cache_columns = true
I have published simple Rails 3 demo application using Rails 3 and Oracle enhanced adapter. You can take a look at Gemfile and Oracle initializer file to see examples how to configure Oracle enhanced adapter with Rails 3.
Rails 2.3 compatibility
Oracle enhanced adapter version 1.3.0 is still compatible with Rails 2.3 (I am testing it against Rails 2.3.5 and 2.3.8) and it is recommended to upgrade if you are on Rails 2.3 and plan to upgrade to Rails 3.0 later. But if you are still on Rails 2.2 or earlier then there might be issues with Oracle enhanced adapter 1.3.0 as I am using some Rails methods which appeared just in Rails 2.3 – so in this case it might be safer to stay on previous Oracle enhanced adapter version 1.2.4 until you upgrade to latest Rails version.
Oracle CONTEXT index support
Every edition of Oracle database includes Oracle Text option for free which provides different full text indexing capabilities. Therefore in Oracle database case you don’t need external full text indexing and searching engines which can simplify your application deployment architecture.
The most commonly used index type is CONTEXT index which can be used for efficient full text search. Most of CONTEXT index creation examples show how to create simple full text index on one table and one column. But if you want to create more complex full text indexes on multiple columns or even on multiple tables and columns then you need to write your custom procedures and custom index refreshing logic.
Therefore to make creation of more complex full text indexes easier I have created additional add_context_index and remove_context_index methods that can be used in migrations and which creates additional stored procedures and triggers when needed in standardized way.
This is how you can create simple single column index:
add_context_index :posts, :title
And you can perform search using this index with
This is how you create index on several columns (which will generate additional stored procedure for providing XML document with specified columns to indexer):
add_context_index :posts, [:title, :body]
And you can search either in all columns or specify in which column you want to search (as first argument you need to specify first column name as this is the column which is referenced during index creation):
Post.contains(:title, 'word') Post.contains(:title, 'word within title') Post.contains(:title, 'word within body')
See Oracle Text documentation for syntax that you can use in CONTAINS function in SELECT WHERE clause.
You can also specify some dummy main column name when creating multiple column index as well as specify to update index automatically after each commit (as otherwise you need to synchronize index manually or schedule periodic update):
add_context_index :posts, [:title, :body], :index_column => :all_text, :sync => 'ON COMMIT' Post.contains(:all_text, 'word')
Or you can specify that index should be updated when specified columns are updated (e.g. in ActiveRecord you can specify to trigger index update when created_at or updated_at columns are updated). Otherwise index is updated only when main index column is updated.
add_context_index :posts, [:title, :body], :index_column => :all_text, :sync => 'ON COMMIT', :index_column_trigger_on => [:created_at, :updated_at]
And you can even create index on multiple tables by providing SELECT statements which should be used to fetch necessary columns from related tables:
add_context_index :posts, [:title, :body, # specify aliases always with AS keyword "SELECT comments.author AS comment_author, comments.body AS comment_body FROM comments WHERE comments.post_id = :id" ], :name => 'post_and_comments_index', :index_column => :all_text, :index_column_trigger_on => [:updated_at, :comments_count], :sync => 'ON COMMIT' # search in any table columns Post.contains(:all_text, 'word') # search in specified column Post.contains(:all_text, "aaa within title") Post.contains(:all_text, "bbb within comment_author")
In terms of Oracle Text performance in most cases it is good enough (typical response in not more that hundreds of milliseconds). But from my experience it is still slower compared to dedicated full text search engines like Sphinx. So in case if Oracle Text performance is not good enough (if you need all search operations return in tens of milliseconds) then you probably need to evaluate dedicated search engines like Sphinx or Lucene.
As always you can install Oracle enhanced adapter on any Ruby platform (Ruby 1.8.7 or Ruby 1.9.1/1.9.2 or JRuby) with
gem install activerecord-oracle_enhanced-adapter