activesearch 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ad6a5304a892366c29fdaaac5d36acd68c01cccd
4
- data.tar.gz: 390657dc4805e5404f5a05ddecd264e20283fb7a
3
+ metadata.gz: 4780fd611e6b324444174e68454b0ddcb420a98f
4
+ data.tar.gz: 6bc865f6eb2e43b5573006ca4b34ca11ffc04140
5
5
  SHA512:
6
- metadata.gz: 241cd473dcb494c7267864e8976166da68e2a0f13b40a5c9b1b6759167d70ebfa570f8a4c0252d06fd6bca8044088dcca3368188a53aa18993a8c55100d5f0bc
7
- data.tar.gz: ad669841f7ea28d1e1f337cdfd1192c945e50d7499e99ae39e18372fa96752e7157d41ef18f24c01e7626f8c6e9b440b2c6bf28e0a31ba8a60d456ee03337ef0
6
+ metadata.gz: 62c93e5576610fac82d96c31b2516aa5c39697ecbe22653ce6961a51001c7d835ebbb74a6b5e735337a7b840a76c19103d0e70ebfec9f7bd422f974c318d3e29
7
+ data.tar.gz: 314d636362c96882f7cfdeb5d8fd7e5e611996e585add94e06e4047a1dd12ab255ff51064bb3414f75cb6fdb5b7cedcd32804713158f7f285b86ebbb1cd3519d
data/README.md CHANGED
@@ -15,20 +15,22 @@ What lies below is for mongoid 3.x.
15
15
 
16
16
  gem 'mongoid'
17
17
  gem 'activesearch'
18
-
18
+
19
19
  This is not a fulltext search engine, but it's a good solution for those users that don't have access to anything else.
20
20
  It works by storing keywords taken from the specified fields and storing them in an Array field, which would be indexed.
21
-
21
+
22
22
  ###elasticsearch
23
23
 
24
24
  gem 'tire'
25
25
  gem 'activesearch'
26
-
26
+
27
+ **Important**: the elasticsearch engine has not not been updated so it does not work.
28
+
27
29
  ###Algolia
28
30
 
29
31
  gem 'httparty'
30
32
  gem 'activesearch'
31
-
33
+
32
34
  Then you need to configure the client. In rails, put this in an algolia.rb initializer:
33
35
 
34
36
  Algolia.configure do |c|
@@ -40,7 +42,7 @@ Then you need to configure the client. In rails, put this in an algolia.rb initi
40
42
  ##Configuration
41
43
 
42
44
  call "search_by" from your model:
43
-
45
+
44
46
  search_by [:title, :body, store: [:slug]], if: :its_friday
45
47
 
46
48
  **IMPORTANT: the first parameter must be either an array, or a symbol.
@@ -51,7 +53,7 @@ You can also add :if or :unless conditions in the same way you would do with Act
51
53
  If you need virtual options, pass a symbol instead:
52
54
 
53
55
  search_by :options_for_search
54
-
56
+
55
57
  And define an instance method with that name which must return an array with the options, ie:
56
58
 
57
59
  def options_for_search
@@ -59,9 +61,9 @@ And define an instance method with that name which must return an array with the
59
61
  end
60
62
 
61
63
  ## Querying
62
-
64
+
63
65
  ActiveSearch.search("some words").first.to_hash["title"]
64
-
66
+
65
67
  You can access the stored fields with to_hash, so you don't need to fetch the real document.
66
68
 
67
69
  ## Why?
@@ -1,34 +1,33 @@
1
- require "activesearch/algolia/client"
2
- require "activesearch/algolia/worker"
3
- require "activesearch/base"
4
- require "activesearch/proxy"
1
+ require 'activesearch/base'
2
+ require 'activesearch/results_set'
3
+ require 'activesearch/proxy'
4
+
5
+ require 'activesearch/algolia/client'
6
+ require 'activesearch/algolia/results_set'
7
+ require 'activesearch/algolia/worker'
5
8
 
6
9
  module ActiveSearch
7
10
 
8
11
  def self.search(text, conditions = {}, options = {})
9
- locale = options[:locale] || I18n.locale
10
- conditions[:locale] ||= locale
12
+ conditions.symbolize_keys!
13
+ options.symbolize_keys!
11
14
 
12
- Proxy.new(text, conditions, options) do |text, conditions|
13
- Algolia::Client.new.query(text, { tags: conditions_to_tags(conditions) }, options)["hits"].map! do |hit|
14
- if hit["_tags"]
15
- hit["_tags"].each do |tag|
16
- # preserve other ":" characters
17
- _segments = tag.split(':')
15
+ clean_locale(conditions, options)
18
16
 
19
- unless _segments.empty? || _segments[1..-1].empty?
20
- hit[_segments.first] = _segments[1..-1].join(':')
21
- end
22
- end
23
- hit.delete("_tags")
24
- end
25
- hit
26
- end
27
- end
17
+ results_set = Algolia::Client.new.query_text(text, { tags: conditions_to_tags(conditions) }, options)
18
+
19
+ Proxy.new(results_set, text, options)
28
20
  end
29
21
 
30
22
  protected
31
23
 
24
+ def self.clean_locale(conditions, options)
25
+ locale = options[:locale] || I18n.locale
26
+ conditions[:locale] ||= locale
27
+
28
+ conditions.delete(:locale) if options[:locale] == false
29
+ end
30
+
32
31
  def self.conditions_to_tags(conditions)
33
32
  conditions.map { |c| c.join(':') }.join(',')
34
33
  end
@@ -28,19 +28,31 @@ module ActiveSearch
28
28
  self.class.put("/#{id}", body: object.to_json)
29
29
  end
30
30
 
31
- def query(text, extras = {}, options = {})
31
+ def query_text(text, extras = {}, options = {})
32
32
  page, per_page = options[:page] || 0, options[:per_page] || 20
33
33
 
34
- self.class.get('', query: extras.merge!(
34
+ response = self.class.get('', query: extras.merge!(
35
35
  query: text,
36
36
  page: page,
37
37
  hitsPerPage: per_page
38
38
  ))
39
+
40
+ ResultsSet.new(response, page, per_page)
39
41
  end
40
42
 
41
43
  def get(id)
42
44
  self.class.get("/#{id}")
43
45
  end
46
+
47
+ def find_from_resource(type, id, &block)
48
+ query = {
49
+ tags: "original_type:#{type},original_id:#{id}",
50
+ query: ''
51
+ }
52
+
53
+ self.class.get('', query: query)['hits'].each(&block)
54
+ end
55
+
44
56
  end
45
57
  end
46
58
  end
@@ -0,0 +1,30 @@
1
+ module ActiveSearch
2
+ module Algolia
3
+ class ResultsSet < ActiveSearch::ResultsSet
4
+
5
+ def initialize(results, page = nil, per_page = nil)
6
+ super
7
+
8
+ @results = results['hits']
9
+ @total_entries = results['nbHits']
10
+ @total_pages = results['hitsPerPage']
11
+ end
12
+
13
+ def parse(result)
14
+ if result['_tags']
15
+ result['_tags'].each do |tag|
16
+ # preserve other ":" characters
17
+ _segments = tag.split(':')
18
+
19
+ unless _segments.empty? || _segments[1..-1].empty?
20
+ result[_segments.first] = _segments[1..-1].join(':')
21
+ end
22
+ end
23
+ result.delete("_tags")
24
+ end
25
+ result
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -10,7 +10,8 @@ class ActiveSearch::Algolia::Worker
10
10
  ::ActiveSearch::Algolia::Client.new.save(msg[:id], msg[:doc])
11
11
  when :deindex
12
12
  client = ::ActiveSearch::Algolia::Client.new
13
- client.query("", tags: "original_type:#{msg[:type]},original_id:#{msg[:id]}")["hits"].each do |hit|
13
+
14
+ client.find_from_resource(msg[:type], msg[:id]).each do |hit|
14
15
  client.delete(hit["objectID"])
15
16
  end
16
17
  end
@@ -4,8 +4,10 @@ require "activesearch/base"
4
4
  require "activesearch/proxy"
5
5
 
6
6
  module ActiveSearch
7
-
7
+
8
8
  def self.search(text)
9
+ raise 'sorry but it is not implemented'
10
+
9
11
  Proxy.new(text) do |text|
10
12
  Tire.search('_all') do |search|
11
13
  search.query do |query|
@@ -14,33 +16,33 @@ module ActiveSearch
14
16
  end.results
15
17
  end
16
18
  end
17
-
19
+
18
20
  module ElasticSearch
19
21
  def self.included(base)
20
22
  base.class_eval do
21
23
  include ActiveSearch::Base
22
24
  end
23
25
  end
24
-
26
+
25
27
  def to_indexable
26
28
  elastic_properties.keys.inject({_type: self.elastic_type}) do |memo,field|
27
29
  memo.merge!(field => self.send(field))
28
30
  end
29
31
  end
30
-
32
+
31
33
  protected
32
34
  def elastic_type
33
35
  @elastic_type ||= self.type.gsub!(/(.)([A-Z])/,'\1_\2').downcase
34
36
  end
35
-
37
+
36
38
  def elastic_index(&block)
37
39
  Tire.index(elastic_type, &block)
38
40
  end
39
-
41
+
40
42
  def reindex
41
43
  doc = self.to_indexable
42
44
  properties = self.elastic_properties
43
-
45
+
44
46
  elastic_index do
45
47
  unless exists?
46
48
  create({ mappings: { doc[:_type] => {properties: properties}}})
@@ -48,25 +50,25 @@ module ActiveSearch
48
50
  store doc
49
51
  end
50
52
  end
51
-
53
+
52
54
  def deindex
53
55
  doc = self.to_indexable
54
56
  elastic_index do
55
57
  remove doc
56
58
  end
57
59
  end
58
-
60
+
59
61
  def elastic_properties
60
62
  props = {id: {type: 'string'}}
61
-
63
+
62
64
  search_fields.each_with_object(props) do |field,hash|
63
65
  hash[field] = {type: 'string'}
64
66
  end
65
-
67
+
66
68
  (Array(search_options[:store]) - search_fields).each_with_object(props) do |field,hash|
67
69
  hash[field] = {type: 'string', :index => :no}
68
70
  end
69
-
71
+
70
72
  props
71
73
  end
72
74
  end
@@ -1,17 +1,31 @@
1
1
  require 'activesearch/base'
2
+ require 'activesearch/results_set'
2
3
  require 'activesearch/proxy'
4
+
5
+ require 'activesearch/mongoid/results_set'
3
6
  require 'activesearch/mongoid/full_text_search_query'
4
7
  require 'activesearch/mongoid/index'
5
8
 
6
9
  module ActiveSearch
7
10
 
8
11
  def self.search(text, conditions = {}, options = {})
12
+ conditions.symbolize_keys!
13
+ options.symbolize_keys!
14
+
15
+ clean_locale(conditions, options)
16
+
17
+ results_set = ActiveSearch::Mongoid::Index.search(text, conditions, options)
18
+
19
+ Proxy.new(results_set, text, options)
20
+ end
21
+
22
+ protected
23
+
24
+ def self.clean_locale(conditions, options)
9
25
  locale = options[:locale] || I18n.locale
10
26
  conditions[:locale] ||= locale
11
27
 
12
- Proxy.new(text, conditions, options) do |text, conditions|
13
- ActiveSearch::Mongoid::Index.search(text, conditions, options)
14
- end
28
+ conditions.delete(:locale) if options[:locale] == false
15
29
  end
16
30
 
17
31
  module Mongoid
@@ -2,8 +2,6 @@ module ActiveSearch
2
2
  module Mongoid
3
3
  class FullTextSearchQuery
4
4
 
5
- attr_reader :results
6
-
7
5
  def initialize(name, query, conditions, options)
8
6
  @name = name
9
7
  @query = query
@@ -13,18 +11,14 @@ module ActiveSearch
13
11
  end
14
12
 
15
13
  def run
16
- @results = session.command({
14
+ results = session.command({
17
15
  'text' => @name,
18
16
  'search' => @query,
19
17
  'language' => @language,
20
18
  'filter' => @filter
21
19
  })
22
20
 
23
- if @results.has_key?('results')
24
- sanitize_results
25
- else
26
- []
27
- end
21
+ ResultsSet.new(results, @options[:page], @options[:per_page])
28
22
  end
29
23
 
30
24
  def session
@@ -57,8 +51,8 @@ module ActiveSearch
57
51
  def prepare_filter(conditions)
58
52
  {}.tap do |filter|
59
53
  conditions.each do |key, value|
60
- if key == :locale
61
- filter['locale'] = value.to_s
54
+ if key.to_sym == :locale
55
+ filter['locale'] = value.to_s unless value == false
62
56
  else
63
57
  filter["stored.#{key}"] = value
64
58
  end
@@ -66,21 +60,6 @@ module ActiveSearch
66
60
  end
67
61
  end
68
62
 
69
- def sanitize_results
70
- page, per_page = @options[:page] || 0, @options[:per_page] || 10
71
- included = ((page * per_page)..((page + 1) * per_page - 1))
72
- _results = []
73
-
74
- # manual pagination since it is not natively supported by MongoDB
75
- @results['results'].each_with_index do |result, index|
76
- if included === index
77
- _results << result['obj']['stored'].merge(result['obj'].slice('locale', 'original_type', 'original_id'))
78
- end
79
- end
80
-
81
- @results = _results
82
- end
83
-
84
63
  end
85
64
  end
86
65
  end
@@ -63,7 +63,7 @@ module ActiveSearch
63
63
  doc = find_or_initialize_by(original_type: original.class.to_s, original_id: original.id, locale: locale)
64
64
 
65
65
  doc.store_language(original)
66
- doc.store_fields(original) #, fields, options)
66
+ doc.store_fields(original)
67
67
  doc.refresh_content(original)
68
68
 
69
69
  # save it (create or update it)
@@ -0,0 +1,36 @@
1
+ module ActiveSearch
2
+ module Mongoid
3
+ class ResultsSet < ActiveSearch::ResultsSet
4
+
5
+ def initialize(results, page = nil, per_page = nil)
6
+ super
7
+
8
+ @results = results.has_key?('results') ? results['results'] : []
9
+ @total_entries = @results.size
10
+ @total_pages = @total_entries / @per_page
11
+
12
+ self.paginate if @total_entries > 0
13
+ end
14
+
15
+ protected
16
+
17
+ def index_range
18
+ ((@page * @per_page)..((@page + 1) * @per_page - 1))
19
+ end
20
+
21
+ def paginate
22
+ _results = []
23
+
24
+ # manual pagination since it is not natively supported by MongoDB
25
+ @results.each_with_index do |result, index|
26
+ if index_range === index
27
+ _results << result['obj']['stored'].merge(result['obj'].slice('locale', 'original_type', 'original_id'))
28
+ end
29
+ end
30
+
31
+ @results = _results
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -4,17 +4,22 @@ module ActiveSearch
4
4
  class Proxy
5
5
  include Enumerable
6
6
 
7
- def initialize(text, conditions, options = {}, &implementation)
7
+ extend Forwardable
8
+
9
+ def_delegators :@results_set, :total_pages, :total_entries, :per_page, :page
10
+
11
+ def initialize(results_set, text, options)
12
+ @results_set = results_set
8
13
  @text = text
9
- @conditions = conditions
10
14
  @options = options
11
- @implementation = implementation
12
15
  end
13
16
 
14
17
  def each(&block)
15
- @implementation.call(@text, @conditions).each do |result|
16
- block.call(Result.new(result, @text, @options))
18
+ @results_set.results.map do |result|
19
+ _result = @results_set.parse(result)
20
+ block.call(Result.new(_result, @text, @options))
17
21
  end
18
22
  end
23
+
19
24
  end
20
25
  end
@@ -0,0 +1,21 @@
1
+ module ActiveSearch
2
+ class ResultsSet
3
+
4
+ attr_reader :results, :total_entries, :total_pages, :page, :per_page
5
+
6
+ def initialize(results, page = nil, per_page = nil)
7
+ @results = results
8
+ @page = page || 0
9
+ @per_page = per_page || 10
10
+ end
11
+
12
+ def define_parser(&block)
13
+ @parser = block
14
+ end
15
+
16
+ def parse(result)
17
+ result
18
+ end
19
+
20
+ end
21
+ end
@@ -1,3 +1,3 @@
1
1
  module ActiveSearch
2
- VERSION = '0.3.1'
2
+ VERSION = '0.3.2'
3
3
  end
@@ -64,6 +64,8 @@ shared_examples 'an engine' do
64
64
  it "should paginate docs" do
65
65
  page = ActiveSearch.search("findable", {}, { page: 0, per_page: 2 })
66
66
  first_page_titles = page.map { |e| e['title'] }
67
+ page.total_entries.should == 5
68
+ page.total_pages.should == 2
67
69
  page.count.should == 2
68
70
 
69
71
  page = ActiveSearch.search("findable", {}, { page: 1, per_page: 2 })
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activesearch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Alvarez
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-04-14 00:00:00.000000000 Z
12
+ date: 2014-04-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -199,14 +199,17 @@ files:
199
199
  - lib/activesearch.rb
200
200
  - lib/activesearch/algolia.rb
201
201
  - lib/activesearch/algolia/client.rb
202
+ - lib/activesearch/algolia/results_set.rb
202
203
  - lib/activesearch/algolia/worker.rb
203
204
  - lib/activesearch/base.rb
204
205
  - lib/activesearch/elastic_search.rb
205
206
  - lib/activesearch/mongoid.rb
206
207
  - lib/activesearch/mongoid/full_text_search_query.rb
207
208
  - lib/activesearch/mongoid/index.rb
209
+ - lib/activesearch/mongoid/results_set.rb
208
210
  - lib/activesearch/proxy.rb
209
211
  - lib/activesearch/result.rb
212
+ - lib/activesearch/results_set.rb
210
213
  - lib/activesearch/version.rb
211
214
  - spec/config/mongoid.yml
212
215
  - spec/engines/algolia_spec.rb