awesome_search 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,7 +2,6 @@
2
2
 
3
3
  * A helpful library to make searching more organized in the controller and views.
4
4
  * Using this library will force you to write a class for each kind of search you want to use.
5
- * It has features that allow easy configuration of searches tailored to work with rsolr.
6
5
 
7
6
  == Usage
8
7
 
data/Rakefile CHANGED
@@ -22,11 +22,22 @@ begin
22
22
  "lib/awesome/definitions/bits.rb",
23
23
  "lib/awesome/definitions/filters.rb",
24
24
  "lib/awesome/definitions/locales.rb",
25
+ "lib/awesome/definitions/stopwords.rb",
25
26
  "lib/awesome/definitions/types.rb",
26
27
  "lib/awesome/search.rb",
27
28
  "lib/awesome/super_search.rb",
28
29
  "lib/awesome/triage.rb",
29
30
  "lib/awesome_search.rb",
31
+ "test/search_classes/amazon.rb",
32
+ "test/search_classes/ebay.rb",
33
+ "test/search_classes/google.rb",
34
+ "test/search_classes/local.rb",
35
+ "test/helper.rb",
36
+ "test/test_awesome_search.rb",
37
+ "test/test_multiple.rb",
38
+ "test/test_multiple_filters.rb",
39
+ "test/test_multiple_locales.rb",
40
+ "test/test_multiple_types.rb",
30
41
  "rails/init.rb",
31
42
  "init.rb"
32
43
  ]
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.1
1
+ 1.1.0
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{awesome_search}
8
- s.version = "1.0.1"
8
+ s.version = "1.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["pboling"]
12
- s.date = %q{2010-04-29}
12
+ s.date = %q{2010-06-22}
13
13
  s.description = %q{Organize complicated search results}
14
14
  s.email = %q{peter.boling@peterboling.com}
15
15
  s.extra_rdoc_files = [
@@ -26,17 +26,28 @@ Gem::Specification.new do |s|
26
26
  "lib/awesome/definitions/bits.rb",
27
27
  "lib/awesome/definitions/filters.rb",
28
28
  "lib/awesome/definitions/locales.rb",
29
+ "lib/awesome/definitions/stopwords.rb",
29
30
  "lib/awesome/definitions/types.rb",
30
31
  "lib/awesome/search.rb",
31
32
  "lib/awesome/super_search.rb",
32
33
  "lib/awesome/triage.rb",
33
34
  "lib/awesome_search.rb",
34
- "rails/init.rb"
35
+ "rails/init.rb",
36
+ "test/helper.rb",
37
+ "test/search_classes/amazon.rb",
38
+ "test/search_classes/ebay.rb",
39
+ "test/search_classes/google.rb",
40
+ "test/search_classes/local.rb",
41
+ "test/test_awesome_search.rb",
42
+ "test/test_multiple.rb",
43
+ "test/test_multiple_filters.rb",
44
+ "test/test_multiple_locales.rb",
45
+ "test/test_multiple_types.rb"
35
46
  ]
36
47
  s.homepage = %q{http://github.com/pboling/awesome_search}
37
48
  s.rdoc_options = ["--charset=UTF-8"]
38
49
  s.require_paths = ["lib"]
39
- s.rubygems_version = %q{1.3.6}
50
+ s.rubygems_version = %q{1.3.7}
40
51
  s.summary = %q{Organize complicated search results}
41
52
  s.test_files = [
42
53
  "test/helper.rb",
@@ -55,7 +66,7 @@ Gem::Specification.new do |s|
55
66
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
56
67
  s.specification_version = 3
57
68
 
58
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
69
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
59
70
  s.add_development_dependency(%q<shoulda>, [">= 0"])
60
71
  s.add_runtime_dependency(%q<activesupport>, [">= 2.1"])
61
72
  else
@@ -0,0 +1,125 @@
1
+ module Awesome
2
+ module Definitions
3
+ module Stopwords
4
+
5
+ QUOTED_REGEX = /("[^"]*")/
6
+ UNQUOTED_REGEX = /"([^"]*)"/
7
+ RM_QUOTED_REGEX = /"[^"]*"/
8
+ BEG_OPERATORS = /^[+-]/
9
+ END_OPERATORS = /[,+-]$/
10
+ EXCLUSION_OPERATORS = /^[-]/
11
+ INCLUSION_OPERATORS = /^[+]/
12
+
13
+ def self.included(base)
14
+ base.extend ClassMethods
15
+ base.cattr_accessor :search_stopwords
16
+ base.cattr_accessor :verbose_stopwords
17
+ end
18
+
19
+ module ClassMethods
20
+ def stopwords(key = :none)
21
+ case key
22
+ when :none then
23
+ self.search_stopwords[:none]
24
+ when :standard then
25
+ self.search_stopwords[:standard]
26
+ when :custom then
27
+ self.search_stopwords[:custom]
28
+ when :both then
29
+ self.search_stopwords[:custom] | self.search_stopwords[:standard]
30
+ else
31
+ Rails.logger.warn("AwesomeSearch: Stopwords Key Invalid, defaulting to :both")
32
+ self.search_stopwords[:custom] | self.search_stopwords[:standard]
33
+ end
34
+ end
35
+ end
36
+
37
+ #Instance Methods:
38
+
39
+ #remove the stopwords from regular search terms, BUT NOT from exact phrase searches (quoted)
40
+ #example:
41
+ # txt = "+hair \"in the\" on the grapes, \"middle fork\" wrath \"age of man\" -end"
42
+ def process_stopwords(txt = self.search_text)
43
+ #Needs to be set so highlighting will work properly (can't match quotes)
44
+ self.highlight_token_array(txt)
45
+ #Now put humpty dumpty back together without the nasty stopwords, sort the tokens by length
46
+ self.search_token_array(txt).join(" ")
47
+ end
48
+
49
+ def search_token_array(txt)
50
+ self.search_tokens ||= (self.quoted_exact_phrases_array(txt) | self.gowords_array(txt)).sort {|a,b| b.length <=> a.length }
51
+ end
52
+
53
+ def highlight_token_array(txt)
54
+ self.highlight_tokens ||= begin
55
+ array = (self.unquoted_exact_phrases_array(txt) | self.gowords_array(txt)).sort {|a,b| b.length <=> a.length }
56
+ remove_exclusions(array)
57
+ end
58
+ end
59
+
60
+ def set_clean_search_query
61
+ self.clean_search_query = self.highlight_tokens.join(" ")
62
+ end
63
+
64
+ def remove_exclusions(array)
65
+ array.map do |tok|
66
+ tok.match(Awesome::Definitions::Stopwords::EXCLUSION_OPERATORS) ?
67
+ nil :
68
+ tok.match(Awesome::Definitions::Stopwords::RM_QUOTED_REGEX) ?
69
+ tok :
70
+ tok.gsub(Awesome::Definitions::Stopwords::INCLUSION_OPERATORS, '')
71
+ end.compact
72
+ end
73
+
74
+ #All tokens that are quoted
75
+ def tokenize_quot(txt)
76
+ self.tokenize_quoted ||= txt.split(Awesome::Definitions::Stopwords::QUOTED_REGEX)
77
+ end
78
+
79
+ #All tokens that are quoted, in their unquoted form
80
+ def tokenize_unquot(txt)
81
+ self.tokenize_unquoted ||= txt.split(Awesome::Definitions::Stopwords::UNQUOTED_REGEX)
82
+ end
83
+
84
+ #Remove all tokens that are quoted
85
+ def tokenize_without_quot(txt)
86
+ self.tokenize_without_quoted ||= txt.split(Awesome::Definitions::Stopwords::RM_QUOTED_REGEX)
87
+ end
88
+
89
+ # ["\"in the\"", "\"middle fork\"", "\"age of man\""]
90
+ def quoted_exact_phrases_array(txt)
91
+ self.quoted_exact_phrases ||= self.tokenize_quot(txt) - self.tokenize_without_quot(txt) - ['']
92
+ end
93
+
94
+ # ["in the", "middle fork", "age of man"]
95
+ def unquoted_exact_phrases_array(txt)
96
+ self.unquoted_exact_phrases ||= self.tokenize_unquot(txt) - self.tokenize_without_quot(txt) - ['']
97
+ end
98
+
99
+ # "+hair on the grapes, wrath -end"
100
+ def query_wo_exact_phrases(txt)
101
+ self.query_without_exact_phrases ||= txt.gsub(Awesome::Definitions::Stopwords::QUOTED_REGEX, '')
102
+ end
103
+
104
+ # ["+hair", "on", "the", "grapes,", "wrath", "-end"]
105
+ def array_with_stopwords(txt)
106
+ qa = self.query_wo_exact_phrases(txt).split
107
+ qa.delete(',') #delete works on self (qa here), so won't work chained onto the statement above!
108
+ qa
109
+ end
110
+
111
+ # ["+hair", "grapes,", "wrath", "-end"]
112
+ def gowords_array(txt)
113
+ self.gowords ||= self.array_with_stopwords(txt).map do |token|
114
+ cleaned_token = self.clean_token(token)
115
+ self.stopwords.include?(cleaned_token) ? nil : cleaned_token.blank? ? nil : token
116
+ end.compact
117
+ end
118
+
119
+ def clean_token(token)
120
+ token.gsub(Awesome::Definitions::Stopwords::BEG_OPERATORS, '').gsub(Awesome::Definitions::Stopwords::END_OPERATORS, '')
121
+ end
122
+
123
+ end
124
+ end
125
+ end
@@ -15,6 +15,7 @@ module Awesome
15
15
 
16
16
  #Some defaults if stopwords are set to be used (by default they are turned off)
17
17
  @@search_stopwords ||= {:standard => %w(an and are as at be but by for if in into is it no not of on or s such t that the their then there these they this to was will with),
18
+ :none => [],
18
19
  :custom => []}
19
20
 
20
21
  def self.configure_search_types(&block)
@@ -37,6 +38,7 @@ module Awesome
37
38
 
38
39
  attr_accessor(:search_text,
39
40
  :search_query,
41
+ :clean_search_query,
40
42
  :search_type,
41
43
  :search_filters,
42
44
  :search_locale,
@@ -61,19 +63,22 @@ module Awesome
61
63
  :search_tokens,
62
64
  :highlight_tokens)
63
65
 
66
+ alias :query :search_text
67
+ alias :processed_query :search_query
68
+
64
69
  #CLASS METHODS
65
70
  #Main focus of class methods is determining which sort of AwesomeSearch subclass we need to instantiate for the search
66
71
  def initialize(*args)
67
72
  @multiple_types_as_one = args.first[:multiple_types_as_one]
68
- @stopwords = args.first[:stopwords] ?
73
+ @stopwords = !args.first[:stopwords].blank? ?
69
74
  args.first[:stopwords].is_a?(Array) ?
70
75
  args.first[:stopwords] :
71
76
  args.first[:stopwords].is_a?(Symbol) ?
72
77
  self.class.stopwords(args.first[:stopwords]) :
73
78
  self.class.stopwords(:both) :
74
- args.first[:stopwords] != false ?
79
+ args.first[:stopwords] == true ?
75
80
  self.class.stopwords(:standard) :
76
- []
81
+ self.class.stopwords(:none)
77
82
  @page = args.first[:page]
78
83
  @per_page = args.first[:per_page]
79
84
  @search_text = args.first[:search_text] # a string
@@ -82,6 +87,7 @@ module Awesome
82
87
  @search_tokens = args.first[:search_tokens] # an array of the query terms as tokens after being cleaned, unless passed in as param (not sure why this would ever be desired, but why not allow it jic?) When not set in args, will be set by clean_search_text methods
83
88
  @highlight_tokens = args.first[:highlight_tokens] # an array of the query terms as unquoted tokens after being cleaned, unless passed in as param (not sure why this would ever be desired, but why not allow it jic?) When not set in args, will be set by clean_search_text methods
84
89
  @search_query = self.clean_search_text # a string to be set based on the search text by removing the search modifiers from the search text
90
+ @clean_search_query = self.set_clean_search_query # Removed search tokens and modifiers (+) so Regexes can be run on this string
85
91
  @search_type = args.first[:search_type] # a symring (symring methods are in the Bits mixin)
86
92
  @search_locale = args.first[:search_locale] # a symring (symring methods are in the Bits mixin)
87
93
  @found = nil
@@ -104,9 +110,13 @@ module Awesome
104
110
  end
105
111
 
106
112
  def clean_search_text
113
+ #puts "search_text: #{self.search_text}" if Awesome::Search.verbose
107
114
  txt = Awesome::Triage.clean_search_text(self.search_text)
115
+ #puts "cleaning1: #{txt}" if Awesome::Search.verbose
108
116
  txt = Awesome::Search.clean_search_text(txt, self.multiple_types_as_one)
117
+ #puts "cleaning2: #{txt}" if Awesome::Search.verbose
109
118
  txt = self.process_stopwords(txt)
119
+ #puts "cleaning3: #{txt}" if Awesome::Search.verbose
110
120
  txt
111
121
  end
112
122
 
@@ -1,6 +1,7 @@
1
1
  require "awesome/definitions/bits" unless defined?(Awesome::Definitions::Bits)
2
2
  require "awesome/definitions/locales" unless defined?(Awesome::Definitions::Locales)
3
3
  require "awesome/definitions/filters" unless defined?(Awesome::Definitions::Filters)
4
+ require "awesome/definitions/stopwords" unless defined?(Awesome::Definitions::Stopwords)
4
5
  require "awesome/definitions/types" unless defined?(Awesome::Definitions::Types)
5
6
  require "awesome/triage" unless defined?(Awesome::Triage)
6
7
  require "awesome/search" unless defined?(Awesome::Search)
@@ -12,7 +12,8 @@ require "test/search_classes/google"
12
12
  require "test/search_classes/local"
13
13
 
14
14
  class Test::Unit::TestCase
15
- Awesome::Search.protect_types = true
15
+ #protect_types is a work in progress, doesn't work now.
16
+ Awesome::Search.protect_types = false
16
17
  Awesome::Search.protect_filters = true
17
18
  Awesome::Search.verbose = false
18
19
  Awesome::Search.verbose_types = false
@@ -100,7 +101,7 @@ class Test::Unit::TestCase
100
101
  [ ":all",
101
102
  ":every" ],
102
103
  ":spotted" =>
103
- [ "spotted" ],
104
+ [ ":spotted" ],
104
105
  ":old" =>
105
106
  [ ":old",
106
107
  ":ancient",
@@ -7,8 +7,14 @@ class TestAwesomeSearch < Test::Unit::TestCase
7
7
  @awesome = Awesome::Search.new({:search_text => "this is a test", :search_type => :isbn, :search_locale => :amazon})
8
8
  end
9
9
 
10
+ should "return search_text" do
11
+ assert_equal 'this is a test', @awesome.search_text
12
+ end
10
13
  should "return search_query" do
11
- assert_equal 'this is a test', @awesome.search_query
14
+ assert_equal 'this test is a', @awesome.search_query
15
+ end
16
+ should "return clean_search_query" do
17
+ assert_equal 'this test is a', @awesome.clean_search_query
12
18
  end
13
19
  should "return search_type" do
14
20
  assert_equal :isbn, @awesome.search_type
@@ -20,11 +26,14 @@ class TestAwesomeSearch < Test::Unit::TestCase
20
26
 
21
27
  context "An Awesome::Search instance with query modifiers" do
22
28
  setup do
23
- @awesome = Awesome::Search.new({:search_text => ":ebay :sku this is a test"})
29
+ @awesome = Awesome::Search.new({:search_text => ":ebay :sku this is a +test"})
24
30
  end
25
31
 
26
- should "return search_query without query modifiers" do
27
- assert_equal 'this is a test', @awesome.search_query
32
+ should "return search_query without query tokens" do
33
+ assert_equal '+test this is a', @awesome.search_query
34
+ end
35
+ should "return clean_search_query without query tokens and modifiers" do
36
+ assert_equal 'test this is a', @awesome.clean_search_query
28
37
  end
29
38
  should "search_type should be nil" do
30
39
  assert_equal nil, @awesome.search_type
@@ -4,7 +4,7 @@ class TestMultipleFilters < Test::Unit::TestCase
4
4
 
5
5
  context "#results_for with multiple filters" do
6
6
  setup do
7
- @searches = Awesome::Search.results_for(":local :amazon :text :isbn :old this is a test 1234567890", ":text", ":local", [":old",":spotted",":shiny",":new"])
7
+ @searches = Awesome::Search.results_for(":local :amazon :text :isbn :old :spotted :new this is a test 1234567890", ":text", ":local", [":old",":spotted",":shiny",":new"])
8
8
  end
9
9
  should "should return an array" do
10
10
  assert @searches.is_a?(Array)
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: awesome_search
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 19
4
5
  prerelease: false
5
6
  segments:
6
7
  - 1
7
- - 0
8
8
  - 1
9
- version: 1.0.1
9
+ - 0
10
+ version: 1.1.0
10
11
  platform: ruby
11
12
  authors:
12
13
  - pboling
@@ -14,16 +15,18 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-04-29 00:00:00 -04:00
18
+ date: 2010-06-22 00:00:00 -04:00
18
19
  default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
22
  name: shoulda
22
23
  prerelease: false
23
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
24
26
  requirements:
25
27
  - - ">="
26
28
  - !ruby/object:Gem::Version
29
+ hash: 3
27
30
  segments:
28
31
  - 0
29
32
  version: "0"
@@ -33,9 +36,11 @@ dependencies:
33
36
  name: activesupport
34
37
  prerelease: false
35
38
  requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
36
40
  requirements:
37
41
  - - ">="
38
42
  - !ruby/object:Gem::Version
43
+ hash: 1
39
44
  segments:
40
45
  - 2
41
46
  - 1
@@ -61,12 +66,23 @@ files:
61
66
  - lib/awesome/definitions/bits.rb
62
67
  - lib/awesome/definitions/filters.rb
63
68
  - lib/awesome/definitions/locales.rb
69
+ - lib/awesome/definitions/stopwords.rb
64
70
  - lib/awesome/definitions/types.rb
65
71
  - lib/awesome/search.rb
66
72
  - lib/awesome/super_search.rb
67
73
  - lib/awesome/triage.rb
68
74
  - lib/awesome_search.rb
69
75
  - rails/init.rb
76
+ - test/helper.rb
77
+ - test/search_classes/amazon.rb
78
+ - test/search_classes/ebay.rb
79
+ - test/search_classes/google.rb
80
+ - test/search_classes/local.rb
81
+ - test/test_awesome_search.rb
82
+ - test/test_multiple.rb
83
+ - test/test_multiple_filters.rb
84
+ - test/test_multiple_locales.rb
85
+ - test/test_multiple_types.rb
70
86
  has_rdoc: true
71
87
  homepage: http://github.com/pboling/awesome_search
72
88
  licenses: []
@@ -77,23 +93,27 @@ rdoc_options:
77
93
  require_paths:
78
94
  - lib
79
95
  required_ruby_version: !ruby/object:Gem::Requirement
96
+ none: false
80
97
  requirements:
81
98
  - - ">="
82
99
  - !ruby/object:Gem::Version
100
+ hash: 3
83
101
  segments:
84
102
  - 0
85
103
  version: "0"
86
104
  required_rubygems_version: !ruby/object:Gem::Requirement
105
+ none: false
87
106
  requirements:
88
107
  - - ">="
89
108
  - !ruby/object:Gem::Version
109
+ hash: 3
90
110
  segments:
91
111
  - 0
92
112
  version: "0"
93
113
  requirements: []
94
114
 
95
115
  rubyforge_project:
96
- rubygems_version: 1.3.6
116
+ rubygems_version: 1.3.7
97
117
  signing_key:
98
118
  specification_version: 3
99
119
  summary: Organize complicated search results