Skip to content

Commit

Permalink
Refactored Lucene #push; added Lucene module specs
Browse files Browse the repository at this point in the history
  • Loading branch information
henrypoydar committed Aug 30, 2009
1 parent 23037f5 commit c6aba28
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 40 deletions.
6 changes: 4 additions & 2 deletions README.md
Expand Up @@ -136,11 +136,13 @@ If you want to support Lucene full-text searching of CouchDB documents, enable s

CouchRestRails.use_lucene = true

The Lucene design documents are stored alongside the views:
The Lucene design documents per database are stored alongside the views:

db/couch/<database_name>/lucene
|-- <design_document_name>
|-- <lucene_search>
|-- <lucene_search>.js
|-- <lucene_search>.js
|-- ...

You can also customize this path:

Expand Down
103 changes: 65 additions & 38 deletions lib/couch_rest_rails/lucene.rb
Expand Up @@ -4,58 +4,85 @@ module CouchRestRails
module Lucene
extend self

# Push index/search views to CouchDB
def push(database, design_doc)
result = []
result << "database = #{database} :: design_doc = #{design_doc}"
db_dir = File.join(RAILS_ROOT, CouchRestRails.lucene_path, database)
return "Database '#{database}' does not exist" unless (database == "*" || File.exist?(db_dir))

Dir[db_dir].each do |db|
return "Lucene directory '#{db}/lucene' does not exist." unless File.exist?("#{db}/lucene")

# CouchDB checks
db_name = COUCHDB_CONFIG[:db_prefix] + File.basename(db) + COUCHDB_CONFIG[:db_suffix]
begin
CouchRest.get("#{COUCHDB_CONFIG[:host_path]}/#{db_name}")
rescue => err
return "CouchDB database '#{db_name}' does not exist. Create it first. (#{err})"

# Push Lucene searches to couchdb
def push(database_name = '*', opts = {})

CouchRestRails.process_database_method(database_name) do |db, response|

full_db_name = [COUCHDB_CONFIG[:db_prefix], File.basename(db), COUCHDB_CONFIG[:db_suffix]].join
full_db_path = [COUCHDB_CONFIG[:host_path], '/', full_db_name].join

# Default to push all Lucene searches for the given database
search_name = opts[:search_name] || '*'

# Default to load searches from all design documents
design_doc_name = opts[:design_doc_name] || '*'

# Check for CouchDB database
if !COUCHDB_SERVER.databases.include?(full_db_name)
response << "Database #{db} (#{full_db_name}) does not exist"
next
end

# Update CouchDB designs
db_con = CouchRest.database("#{COUCHDB_CONFIG[:host_path]}/#{db_name}")
Dir.glob(File.join(db, 'lucene', design_doc)).each do |doc|
design_doc_name = File.basename(doc)
# Check for views directory
unless File.exist?(File.join(RAILS_ROOT, CouchRestRails.lucene_path, db))
response << "Lucene directory (#{CouchRestRails.lucene_path}/#{db}) does not exist"
next
end

# Load lucene definition views from FS
index_views = assemble_lucene(doc)
couchdb_design = db_con.get("_design/#{design_doc_name}") rescue nil
# Assemble searches for each design document
db_conn = CouchRest.database(full_db_path)

# Update CouchDB's design with fulltext definition files
if index_views.blank?
result << "No search was found in #{doc}/#{design_doc_name}"
else
# Create the design doc, and/or update fulltext property
couchdb_design = { '_id' => "_design/#{design_doc_name}" } if couchdb_design.nil?
couchdb_design['fulltext'] = index_views
Dir.glob(File.join(RAILS_ROOT, CouchRestRails.lucene_path, db, "lucene", design_doc_name)).each do |doc|

db_con.save_doc(couchdb_design)
result << "Added lucene fulltext views to #{design_doc_name}: #{index_views.keys.join(', ')}"
searches = {}
couchdb_design_doc = db_conn.get("_design/#{File.basename(doc)}") rescue nil

searches = assemble_lucene_searches(doc)

# Warn if overwriting existing search
if couchdb_design_doc && couchdb_design_doc['fulltext']
searches.keys.each do |search|
if couchdb_design_doc['fulltext'][search]
response << "Overwriting existing Lucene search '#{search}' in _design/#{File.basename(doc)}"
end
end
end

# Save or update
if couchdb_design_doc.nil?
couchdb_design_doc = {
"_id" => "_design/#{File.basename(doc)}",
'language' => 'javascript',
'fulltext' => searches
}
else
if couchdb_design_doc['fulltext']
# Merge with existing searches
searches = couchdb_design_doc['fulltext'].merge!(searches)
end
couchdb_design_doc['fulltext'] = searches
end
db_conn.save_doc(couchdb_design_doc)

response << "Pushed Lucene searches to #{full_db_name}/_design/#{File.basename(doc)}: #{searches.keys.join(', ')}"

end

end
result.join("\n")

end

# Assemble views from file-system path lucene_doc_path
def assemble_lucene(lucene_doc_path)
search = {}
# Assemble Lucene searches
def assemble_lucene_searches(lucene_doc_path)
searches = {}
Dir.glob(File.join(lucene_doc_path, '*')).each do |search_file|
search_name = File.basename(search_file).sub(/\.js$/, '')
search[search_name] = JSON.parse(IO.read(search_file).gsub(/\n/, ''))
searches[search_name] = JSON.parse(IO.read(search_file).gsub(/\n/, ''))
end
search
searches
end

end
end
49 changes: 49 additions & 0 deletions spec/lib/couch_rest_rails/lucene_spec.rb
@@ -0,0 +1,49 @@
require File.dirname(__FILE__) + '/../../spec_helper'

describe CouchRestRails::Views do

before :each do
setup_foo_bars
CouchRestRails.lucene_path = 'vendor/plugins/couchrest-rails/spec/mock/lucene'
end

after :all do
cleanup_foo_bars
end

describe '#push' do

it "should push the Lucene search in CouchRestRails.lucene_path to a design document for the specified database" do
res = CouchRestRails::Database.delete('foo')
res = CouchRestRails::Database.create('foo')
res = CouchRestRails::Fixtures.load('foo')
res = CouchRestRails::Lucene.push('foo')
db = CouchRest.database(@foo_db_url)
db.get("_design/default")['fulltext'].should_not be_blank
end

it "should replace existing searches but issue a warning" do
CouchRestRails::Tests.setup('foo')
CouchRestRails::Lucene.push('foo')
res = CouchRestRails::Lucene.push('foo')
res.should =~ /Overwriting/
end

it "should push the Lucene searches in CouchRestRails.lucene_path to a design document for all databases if * or no argument is passed" do
class CouchRestRailsTestDocumentFoo < CouchRestRails::Document
use_database :foo
end
class CouchRestRailsTestDocumentBar < CouchRestRails::Document
use_database :bar
end
CouchRestRails::Tests.setup
CouchRestRails::Lucene.push
dbf = CouchRest.database(@foo_db_url)
dbb = CouchRest.database(@bar_db_url)
dbf.get("_design/default")['fulltext'].should_not be_blank
dbb.get("_design/default")['fulltext'].should_not be_blank
end

end

end
1 change: 1 addition & 0 deletions spec/mock/lucene/bar/lucene/default/search.js
@@ -0,0 +1 @@
{ "bar": "rab"}
1 change: 1 addition & 0 deletions spec/mock/lucene/foo/lucene/default/search.js
@@ -0,0 +1 @@
{ "foo": "oof"}

0 comments on commit c6aba28

Please sign in to comment.