Permalink
Browse files

First commit. Basic indexing of stemmed terms in.

  • Loading branch information...
0 parents commit 173efd040bab5764f24ce04ddb7e8bae71880756 @jnunemaker committed Dec 19, 2010
Showing with 270 additions and 0 deletions.
  1. +3 −0 .gitignore
  2. +7 −0 Gemfile
  3. +44 −0 Gemfile.lock
  4. +7 −0 Rakefile
  5. +23 −0 hunt.gemspec
  6. +33 −0 lib/hunt.rb
  7. +21 −0 lib/hunt/util.rb
  8. +3 −0 lib/hunt/version.rb
  9. +17 −0 spec/helper.rb
  10. +39 −0 spec/hunt/util_spec.rb
  11. +73 −0 spec/hunt_spec.rb
@@ -0,0 +1,3 @@
+pkg/*
+*.gem
+.bundle
@@ -0,0 +1,7 @@
+source "http://rubygems.org"
+
+gem 'bson_ext', '1.1.5', :require => false
+
+gemspec
+
+gem 'i18n'
@@ -0,0 +1,44 @@
+PATH
+ remote: .
+ specs:
+ hunt (0.0.1)
+ fast-stemmer (~> 1.0)
+
+GEM
+ remote: http://rubygems.org/
+ specs:
+ activesupport (3.0.3)
+ bson (1.1.5)
+ bson_ext (1.1.5)
+ diff-lcs (1.1.2)
+ fast-stemmer (1.0.0)
+ i18n (0.4.2)
+ jnunemaker-validatable (1.8.4)
+ activesupport (>= 2.3.4)
+ mongo (1.1.5)
+ bson (>= 1.1.5)
+ mongo_mapper (0.8.6)
+ activesupport (>= 2.3.4)
+ jnunemaker-validatable (~> 1.8.4)
+ plucky (~> 0.3.6)
+ plucky (0.3.6)
+ mongo (~> 1.1)
+ rspec (2.3.0)
+ rspec-core (~> 2.3.0)
+ rspec-expectations (~> 2.3.0)
+ rspec-mocks (~> 2.3.0)
+ rspec-core (2.3.1)
+ rspec-expectations (2.3.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.3.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ bson_ext (= 1.1.5)
+ fast-stemmer (~> 1.0)
+ hunt!
+ i18n
+ mongo_mapper (~> 0.8)
+ rspec (~> 2.3)
@@ -0,0 +1,7 @@
+require 'bundler'
+Bundler::GemHelper.install_tasks
+
+require 'rspec/core/rake_task'
+RSpec::Core::RakeTask.new
+
+task :default => :spec
@@ -0,0 +1,23 @@
+# -*- encoding: utf-8 -*-
+$:.push File.expand_path("../lib", __FILE__)
+require "hunt/version"
+
+Gem::Specification.new do |s|
+ s.name = "hunt"
+ s.version = Hunt::VERSION
+ s.platform = Gem::Platform::RUBY
+ s.authors = ["John Nunemaker"]
+ s.email = ["nunemaker@gmail.com"]
+ s.homepage = "http://github.com/jnunemaker/hunt"
+ s.summary = %q{Really basic search for MongoMapper models.}
+ s.description = %q{Really basic search for MongoMapper models.}
+
+ s.add_dependency 'fast-stemmer', '~> 1.0'
+ s.add_development_dependency 'mongo_mapper', '~> 0.8'
+ s.add_development_dependency 'rspec', '~> 2.3'
+
+ s.files = `git ls-files`.split("\n")
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+ s.require_paths = ["lib"]
+end
@@ -0,0 +1,33 @@
+require 'fast_stemmer'
+require 'hunt/util'
+
+module Hunt
+ def self.configure(model)
+ model.before_save(:index_search_terms)
+ end
+
+ module ClassMethods
+ def search_keys
+ @search_keys ||= []
+ end
+
+ def searches(*keys)
+ key(:searches, Hash)
+ @search_keys = keys
+ end
+ end
+
+ module InstanceMethods
+ def search_keys
+ self.class.search_keys
+ end
+
+ def merged_search_key_values
+ search_keys.map { |key| send(key) }.flatten.join(' ')
+ end
+
+ def index_search_terms
+ self.searches['default'] = Util.to_words(merged_search_key_values)
+ end
+ end
+end
@@ -0,0 +1,21 @@
+module Hunt
+ module Util
+ def strip_puncuation(value)
+ value.to_s.gsub(/[^a-zA-Z0-9]/, '')
+ end
+
+ def stem(word)
+ Stemmer.stem_word(word)
+ end
+
+ def to_words(value)
+ value.
+ squeeze(' ').
+ split(' ').
+ map { |word| strip_puncuation(word.downcase) }.
+ reject { |word| word.blank? }
+ end
+
+ extend self
+ end
+end
@@ -0,0 +1,3 @@
+module Hunt
+ VERSION = "0.0.1"
+end
@@ -0,0 +1,17 @@
+$:.unshift(File.expand_path('../../lib', __FILE__))
+
+require 'rubygems'
+require 'bundler'
+
+Bundler.require(:default, :development)
+
+require 'hunt'
+
+MongoMapper.connection = Mongo::Connection.new
+MongoMapper.database = 'hunt-test'
+
+Rspec.configure do |c|
+ c.before(:each) do
+ MongoMapper.database.collections.each(&:remove)
+ end
+end
@@ -0,0 +1,39 @@
+require 'helper'
+
+describe Hunt::Util do
+ describe ".strip_puncuation" do
+ it "removes punctuation" do
+ Hunt::Util.strip_puncuation('woot!').should == 'woot'
+ end
+ end
+
+ describe ".stem" do
+ it "stems word" do
+ Hunt::Util.stem('kissing').should == 'kiss'
+ Hunt::Util.stem('hello').should == 'hello'
+ Hunt::Util.stem('barfing').should == 'barf'
+ end
+ end
+
+ describe ".to_words" do
+ it "converts string to array of words" do
+ Hunt::Util.to_words('this is my sentence').should == %w(this is my sentence)
+ end
+
+ it "squeezes multiple spaces" do
+ Hunt::Util.to_words('this is my sentence').should == %w(this is my sentence)
+ end
+
+ it "removes punctuation" do
+ Hunt::Util.to_words('my first sentence.').should == %w(my first sentence)
+ end
+
+ it "removes blanks from removed punctuation" do
+ Hunt::Util.to_words('my first sentence & second').should == %w(my first sentence second)
+ end
+
+ it "lowercases each word" do
+ Hunt::Util.to_words('My First Sentence').should == %w(my first sentence)
+ end
+ end
+end
@@ -0,0 +1,73 @@
+require 'helper'
+
+class Note
+ include MongoMapper::Document
+
+ plugin Hunt
+
+ key :title, String
+ key :body, String
+ key :tags, Array
+end
+
+describe Hunt do
+ it "adds searches key to model to store search terms" do
+ Note.searches(:title)
+ Note.new.should respond_to(:searches)
+ Note.new.should respond_to(:searches=)
+ end
+
+ context "Searching on one field" do
+ before(:each) do
+ Note.searches(:title)
+ @note = Note.create(:title => 'Woot for MongoDB!')
+ end
+
+ let(:note) { @note }
+
+ it "indexes terms on create" do
+ note.searches['default'].should == %w(woot for mongodb)
+ end
+
+ it "indexes terms on update" do
+ note.update_attributes(:title => 'Another woot')
+ note.searches['default'].should == %w(another woot)
+ end
+ end
+
+ context "Searching on multiple fields" do
+ before(:each) do
+ Note.searches(:title, :body, :tags)
+ @note = Note.create(:title => 'Woot for MongoDB!', :body => 'This is my body.')
+ end
+
+ let(:note) { @note }
+
+ it "indexes merged terms on create" do
+ note.searches['default'].should == %w(woot for mongodb this is my body)
+ end
+
+ it "indexes merged terms on update" do
+ note.update_attributes(:title => 'Another woot', :body => 'An updated body.')
+ note.searches['default'].should == %w(another woot an updated body)
+ end
+ end
+
+ context "Searching on multiple fields one of which is array key" do
+ before(:each) do
+ Note.searches(:title, :tags)
+ @note = Note.create(:title => 'Woot for MongoDB!', :tags => %w(mongo nosql))
+ end
+
+ let(:note) { @note }
+
+ it "indexes merged terms on create" do
+ note.searches['default'].should == %w(woot for mongodb mongo nosql)
+ end
+
+ it "indexes merged terms on update" do
+ note.update_attributes(:title => 'Another woot', :tags => %w(mongo))
+ note.searches['default'].should == %w(another woot mongo)
+ end
+ end
+end

0 comments on commit 173efd0

Please sign in to comment.