activesearch 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +26 -23
- data/activesearch.gemspec +5 -2
- data/elasticsearch.log +24 -0
- data/lib/activesearch/elastic_search/proxy.rb +26 -0
- data/lib/activesearch/elastic_search.rb +30 -0
- data/lib/activesearch/mongoid/model.rb +19 -10
- data/lib/activesearch/mongoid.rb +24 -11
- data/lib/activesearch/result.rb +13 -0
- data/lib/activesearch/version.rb +1 -1
- data/lib/activesearch.rb +1 -1
- data/log/elasticsearch.log +93792 -0
- data/spec/engines_spec.rb +54 -0
- data/spec/models/elastic_search.rb +35 -0
- data/spec/models/mongoid.rb +31 -0
- data/spec/mongoid_spec.rb +7 -45
- data/spec/spec_helper.rb +31 -0
- metadata +65 -4
data/README.md
CHANGED
@@ -4,39 +4,42 @@ This gem allows any class to be indexed by the chosen fulltext search engine.
|
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
7
|
-
|
7
|
+
Depending on the chosen engine, you need to require a dependency and then activesearch on your Gemfile:
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
And then execute:
|
9
|
+
###Mongoid
|
12
10
|
|
13
|
-
|
11
|
+
gem 'mongoid'
|
12
|
+
gem 'activesearch'
|
13
|
+
|
14
|
+
This is not a fulltext search engine, but it's a good solution for those users that don't have access to anything else.
|
15
|
+
It works by storing keywords taken from the specified fields and storing them in an Array field, which would be indexed.
|
16
|
+
|
17
|
+
###elasticsearch
|
14
18
|
|
15
|
-
|
19
|
+
gem 'tire'
|
20
|
+
gem 'activesearch'
|
16
21
|
|
17
|
-
|
22
|
+
##Configuration
|
18
23
|
|
19
|
-
|
24
|
+
Add this to your model:
|
25
|
+
|
26
|
+
search_by :title, :body, store: [:slug]
|
27
|
+
|
28
|
+
the :store option allows you to retrieve that value but it won't be used for search.
|
20
29
|
|
21
|
-
|
30
|
+
## Querying
|
31
|
+
|
32
|
+
ActiveSearch.search("some words").first.to_hash["title"]
|
33
|
+
|
34
|
+
You can access the stored fields with to_hash, so you don't need to fetch the real document.
|
22
35
|
|
23
|
-
|
24
|
-
It works by storing keywords taken from the specified fields and storing them in an Array field, which would be indexed.
|
25
|
-
search() method will return a Mongod::Criteria, so you can chain it with further scopes, like pagination.
|
26
|
-
You won't get original documents though.
|
36
|
+
## Testing
|
27
37
|
|
28
|
-
|
38
|
+
Run specs with this command:
|
29
39
|
|
30
|
-
|
31
|
-
# [...] field definitions if needed
|
32
|
-
include ActiveSearch::Engine # "Engine" being your chosen engine ie. "Mongoid"
|
33
|
-
|
34
|
-
search_on :title, :body, store: [:title]
|
35
|
-
end
|
36
|
-
|
37
|
-
# Access the stored fields so you don't need to fetch the real document
|
38
|
-
ActiveSearch.search("some words").first.stored["title"]
|
40
|
+
bundle exec parallel_rspec spec/
|
39
41
|
|
42
|
+
Since different engines define their own version of ActiveSearch, running specs on a single process will break.
|
40
43
|
|
41
44
|
## Contributing
|
42
45
|
|
data/activesearch.gemspec
CHANGED
@@ -19,5 +19,8 @@ Gem::Specification.new do |gem|
|
|
19
19
|
|
20
20
|
gem.add_development_dependency "rspec"
|
21
21
|
gem.add_development_dependency "bson_ext"
|
22
|
-
gem.add_development_dependency "
|
23
|
-
|
22
|
+
gem.add_development_dependency "active_attr"
|
23
|
+
gem.add_development_dependency "mongoid", "~> 2"
|
24
|
+
gem.add_development_dependency "tire"
|
25
|
+
gem.add_development_dependency "parallel_tests"
|
26
|
+
end
|
data/elasticsearch.log
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# 2012-10-31 15:41:49:965 [HEAD] ("elastic_search_models")
|
2
|
+
#
|
3
|
+
curl -I "http://localhost:9200/elastic_search_models"
|
4
|
+
|
5
|
+
# 2012-10-31 15:41:49:988 [200]
|
6
|
+
#
|
7
|
+
# ""
|
8
|
+
|
9
|
+
# 2012-10-31 15:41:50:002 [HEAD] ("another_elastic_search_models")
|
10
|
+
#
|
11
|
+
curl -I "http://localhost:9200/another_elastic_search_models"
|
12
|
+
|
13
|
+
# 2012-10-31 15:41:50:002 [200]
|
14
|
+
#
|
15
|
+
# ""
|
16
|
+
|
17
|
+
# 2012-10-31 15:41:50:007 [_search] (["_all"])
|
18
|
+
#
|
19
|
+
curl -X GET "http://localhost:9200/_all/_search?query%5Bstring%5D=findable&pretty=true" -d '{}'
|
20
|
+
|
21
|
+
# 2012-10-31 15:41:50:008 [200] (1 msec)
|
22
|
+
#
|
23
|
+
# {"took":1,"timed_out":false,"_shards":{"total":10,"successful":10,"failed":0},"hits":{"total":0,"max_score":null,"hits":[]}}
|
24
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "activesearch/result"
|
2
|
+
|
3
|
+
module ActiveSearch
|
4
|
+
module ElasticSearch
|
5
|
+
class Proxy
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
def initialize(text)
|
9
|
+
@text = text
|
10
|
+
end
|
11
|
+
|
12
|
+
def each(&block)
|
13
|
+
search.results.each { |result| block.call(Result.new(result)) }
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
def search
|
18
|
+
@search ||= Tire.search('_all') do |search|
|
19
|
+
search.query do |query|
|
20
|
+
query.text("_all", @text)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "active_support/core_ext"
|
2
|
+
require "activesearch/elastic_search/proxy"
|
3
|
+
|
4
|
+
module ActiveSearch
|
5
|
+
|
6
|
+
def self.search(text)
|
7
|
+
ElasticSearch::Proxy.new(text)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ElasticSearch
|
11
|
+
def self.included(base)
|
12
|
+
base.extend ClassMethods
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
def search_by(*fields)
|
18
|
+
options = fields.pop if fields.last.is_a?(Hash)
|
19
|
+
include Tire::Model::Search
|
20
|
+
include Tire::Model::Callbacks
|
21
|
+
|
22
|
+
mapping do
|
23
|
+
indexes :type
|
24
|
+
indexes :id, as: :original_id
|
25
|
+
fields.each { |f| indexes f }
|
26
|
+
(Array(options[:store]) - fields).each { |f| indexes f, :index => :no }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -3,24 +3,28 @@ module ActiveSearch
|
|
3
3
|
class Model
|
4
4
|
include ::Mongoid::Document
|
5
5
|
|
6
|
-
field :
|
7
|
-
field :
|
8
|
-
field :
|
9
|
-
field :
|
6
|
+
field :_original_type, type: String
|
7
|
+
field :_original_id, type: BSON::ObjectId
|
8
|
+
field :_keywords
|
9
|
+
field :_stored, type: Hash, default: {}
|
10
10
|
|
11
|
-
index :
|
12
|
-
index [:
|
11
|
+
index :_keywords
|
12
|
+
index [:_original_type, :_original_id], unique: true
|
13
|
+
|
14
|
+
def to_hash
|
15
|
+
_stored
|
16
|
+
end
|
13
17
|
|
14
18
|
def store_fields(original, fields, options)
|
15
19
|
if options && options[:store]
|
16
20
|
options[:store].each do |f|
|
17
|
-
self.
|
21
|
+
self._stored[f] = original[f] if original.send("#{f}_changed?")
|
18
22
|
end
|
19
23
|
end
|
20
24
|
end
|
21
25
|
|
22
26
|
def refresh_keywords(original, fields)
|
23
|
-
self.
|
27
|
+
self._keywords = fields.select { |f| original.fields[f.to_s] }.inject([]) do |memo,f|
|
24
28
|
|
25
29
|
if original.fields[f.to_s].localized?
|
26
30
|
memo += original.send("#{f}_translations").map do |locale,translation|
|
@@ -30,11 +34,16 @@ module ActiveSearch
|
|
30
34
|
original[f] ? memo += original[f].downcase.split : memo
|
31
35
|
end
|
32
36
|
end
|
33
|
-
self.
|
37
|
+
self._keywords.uniq!
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.deindex(original)
|
41
|
+
ActiveSearch::Mongoid::Model.where(_original_type: original.class.to_s, _original_id: original.id).destroy
|
34
42
|
end
|
35
43
|
|
36
44
|
def self.reindex(original, fields, options)
|
37
|
-
|
45
|
+
return unless fields.any? { |f| original.send("#{f}_changed?") }
|
46
|
+
doc = find_or_initialize_by(_original_type: original.class.to_s, _original_id: original.id)
|
38
47
|
doc.store_fields(original, fields, options)
|
39
48
|
doc.refresh_keywords(original, fields)
|
40
49
|
doc.save
|
data/lib/activesearch/mongoid.rb
CHANGED
@@ -4,7 +4,8 @@ module ActiveSearch
|
|
4
4
|
|
5
5
|
# TODO: Wrap this so all engines behave consistently
|
6
6
|
def self.search(text)
|
7
|
-
|
7
|
+
text = text.split(/\s+/)
|
8
|
+
Mongoid::Model.where(:_keywords.in => text + text.map { |word| "#{I18n.locale}:#{word}"})
|
8
9
|
end
|
9
10
|
|
10
11
|
module Mongoid
|
@@ -12,17 +13,29 @@ module ActiveSearch
|
|
12
13
|
base.extend ClassMethods
|
13
14
|
end
|
14
15
|
|
16
|
+
protected
|
17
|
+
def reindex
|
18
|
+
ActiveSearch::Mongoid::Model.reindex(self, self.class.search_fields, self.class.search_options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def deindex
|
22
|
+
ActiveSearch::Mongoid::Model.deindex(self)
|
23
|
+
end
|
24
|
+
|
15
25
|
module ClassMethods
|
16
|
-
def
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
+
def search_options
|
27
|
+
@search_options
|
28
|
+
end
|
29
|
+
|
30
|
+
def search_fields
|
31
|
+
@search_fields
|
32
|
+
end
|
33
|
+
|
34
|
+
def search_by(*fields)
|
35
|
+
@search_options = fields.pop if fields.last.is_a?(Hash)
|
36
|
+
@search_fields = fields
|
37
|
+
self.after_save :reindex
|
38
|
+
self.after_destroy :deindex
|
26
39
|
end
|
27
40
|
end
|
28
41
|
end
|
data/lib/activesearch/version.rb
CHANGED
data/lib/activesearch.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
require "activesearch/version"
|
1
|
+
require "activesearch/version"
|