mongoid_search 0.3.6 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/README.md +18 -2
- data/Rakefile +2 -0
- data/lib/mongoid_search/log.rb +2 -0
- data/lib/mongoid_search/mongoid_search.rb +5 -3
- data/lib/mongoid_search/railtie.rb +2 -0
- data/lib/mongoid_search/tasks/mongoid_search.rake +4 -2
- data/lib/mongoid_search/util.rb +7 -6
- data/lib/mongoid_search.rb +3 -1
- metadata +13 -88
- data/VERSION +0 -1
- data/spec/config/ignorelist.yml +0 -2
- data/spec/models/category.rb +0 -9
- data/spec/models/product.rb +0 -24
- data/spec/models/subproduct.rb +0 -9
- data/spec/models/tag.rb +0 -15
- data/spec/models/variant.rb +0 -7
- data/spec/mongoid_search_spec.rb +0 -371
- data/spec/spec_helper.rb +0 -35
- data/spec/util_spec.rb +0 -72
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 6551ebbcf9e4da8d5ef2bd51036aa3580e7c4023043d228ce88cb8069c8bba31
|
|
4
|
+
data.tar.gz: a1b040501713dbb5d7f874dadeb2a05cc5a0af3f80dc89b4b8b387afa02abb1f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e24c0139031664d6616db5ba52338850d0cb4b2fbb9e82920a8db4e0cfb87b1cc0dabb9ba4bf17e78699143682f2055bdd5ac9acc9ed9d475decb73d76b06129
|
|
7
|
+
data.tar.gz: f4a234e8c235fa037362715e405e9dc45ea85d18e5de54274b8525e3f5cb9173d6d230696ff7b2031e0f4bcd58bfeaa75a9456a69f00d47088f50bb15f211019
|
data/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Mongoid Search
|
|
2
2
|
|
|
3
|
-
Mongoid Search is a simple full text search implementation for Mongoid ORM. It supports Mongoid
|
|
3
|
+
Mongoid Search is a simple full text search implementation for Mongoid ORM. It supports Mongoid 6, 7, and 8 and performs well for small data sets. If your searchable model is big (i.e. 1.000.000+ records), [mongoid_fulltext](https://github.com/mongoid/mongoid_fulltext), ElasticSearch, Solr or Sphinx may suit you better.
|
|
4
4
|
|
|
5
|
-
[](https://github.com/mongoid/mongoid_search/actions/workflows/test.yml)
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -73,6 +73,22 @@ Product.full_text_search("apple iphone").size
|
|
|
73
73
|
# => 1
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
+
You can also search in "virtual" fields by defining them as methods. This can be useful when you have a method with dynamic fields (i.e. variable schema).
|
|
77
|
+
```ruby
|
|
78
|
+
class ModelWithDynamicFields
|
|
79
|
+
|
|
80
|
+
...
|
|
81
|
+
|
|
82
|
+
search_in :search_data
|
|
83
|
+
|
|
84
|
+
def search_data
|
|
85
|
+
# concatenate all String fields' values
|
|
86
|
+
self.attributes.select{|k,v| v.is_a?(String) }.values.join(' ')
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
```
|
|
90
|
+
Mongoid_search will run the method before save and use it's output to populate the `_keywords` field.
|
|
91
|
+
|
|
76
92
|
Of course, some models could have more than one index. For instance, two different searches with different fields, so you could even specify from which index should be searched:
|
|
77
93
|
|
|
78
94
|
```ruby
|
data/Rakefile
CHANGED
data/lib/mongoid_search/log.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Mongoid::Search
|
|
2
4
|
extend ActiveSupport::Concern
|
|
3
5
|
|
|
@@ -11,7 +13,7 @@ module Mongoid::Search
|
|
|
11
13
|
@@classes
|
|
12
14
|
end
|
|
13
15
|
|
|
14
|
-
module ClassMethods
|
|
16
|
+
module ClassMethods # :nodoc:
|
|
15
17
|
# Set a field or a number of fields as sources for search
|
|
16
18
|
def search_in(*args)
|
|
17
19
|
args, options = args_and_options(args)
|
|
@@ -65,7 +67,7 @@ module Mongoid::Search
|
|
|
65
67
|
{ options[:index] => kw }
|
|
66
68
|
end
|
|
67
69
|
|
|
68
|
-
criteria.send("#{
|
|
70
|
+
criteria.send("#{options[:match]}_of", *keywords_hash)
|
|
69
71
|
end
|
|
70
72
|
|
|
71
73
|
def args_and_options(args)
|
|
@@ -92,7 +94,7 @@ module Mongoid::Search
|
|
|
92
94
|
end
|
|
93
95
|
|
|
94
96
|
def search_relevant(query, options)
|
|
95
|
-
results_with_relevance(query, options).
|
|
97
|
+
results_with_relevance(query, options).sort_by { |o| o['value'] }.map do |r|
|
|
96
98
|
new(r['_id'].merge(relevance: r['value'])) do |o|
|
|
97
99
|
# Need to match the actual object
|
|
98
100
|
o.instance_variable_set('@new_record', false)
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
namespace :mongoid_search do
|
|
2
4
|
desc 'Goes through all documents with search enabled and indexes the keywords.'
|
|
3
5
|
task index: :environment do
|
|
4
|
-
|
|
6
|
+
Rails.application.eager_load!
|
|
5
7
|
if Mongoid::Search.classes.blank?
|
|
6
8
|
Mongoid::Search::Log.log "No model to index keywords.\n"
|
|
7
9
|
else
|
|
8
10
|
Mongoid::Search.classes.each do |klass|
|
|
9
|
-
Mongoid::Search::Log.silent = ENV
|
|
11
|
+
Mongoid::Search::Log.silent = ENV.fetch('SILENT', nil)
|
|
10
12
|
Mongoid::Search::Log.log "\nIndexing documents for #{klass.name}:\n"
|
|
11
13
|
klass.index_keywords!
|
|
12
14
|
end
|
data/lib/mongoid_search/util.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
|
|
2
3
|
module Mongoid::Search::Util
|
|
3
4
|
def self.keywords(klass, fields)
|
|
@@ -17,8 +18,8 @@ module Mongoid::Search::Util
|
|
|
17
18
|
end
|
|
18
19
|
end
|
|
19
20
|
else
|
|
20
|
-
value = if klass.respond_to?(fields
|
|
21
|
-
klass.send(fields
|
|
21
|
+
value = if klass.respond_to?("#{fields}_translations")
|
|
22
|
+
klass.send("#{fields}_translations").values
|
|
22
23
|
elsif klass.respond_to?(fields)
|
|
23
24
|
klass.send(fields)
|
|
24
25
|
else
|
|
@@ -38,15 +39,15 @@ module Mongoid::Search::Util
|
|
|
38
39
|
strip_accents = Mongoid::Search.strip_accents
|
|
39
40
|
|
|
40
41
|
return [] if text.blank?
|
|
42
|
+
|
|
41
43
|
text = text.to_s
|
|
42
|
-
.
|
|
43
|
-
.normalize(:kd)
|
|
44
|
+
.unicode_normalize(:nfkd)
|
|
44
45
|
.downcase
|
|
45
46
|
.to_s
|
|
46
47
|
.gsub(strip_symbols, ' ') # strip symbols
|
|
47
48
|
.gsub(strip_accents, '') # strip accents
|
|
48
|
-
.gsub(/[#{ligatures.keys.join
|
|
49
|
-
.split
|
|
49
|
+
.gsub(/[#{ligatures.keys.join}]/) { |c| ligatures[c] }
|
|
50
|
+
.split
|
|
50
51
|
.reject { |word| word.size < Mongoid::Search.minimum_word_size }
|
|
51
52
|
text = text.reject { |word| ignore_list.include?(word) } unless ignore_list.blank?
|
|
52
53
|
text = text.map(&stem_proc) if stem_keywords
|
data/lib/mongoid_search.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'mongoid_search/mongoid_search'
|
|
2
4
|
|
|
3
5
|
require 'mongoid_search/railtie' if defined?(Rails)
|
|
@@ -58,7 +60,7 @@ module Mongoid::Search
|
|
|
58
60
|
|
|
59
61
|
# Strip special symbols
|
|
60
62
|
mattr_accessor :strip_symbols
|
|
61
|
-
@@strip_symbols = /[._:;'
|
|
63
|
+
@@strip_symbols = /[._:;'"`,?|+={}()!@#%^&*<>~$\-\\\/\[\]]/
|
|
62
64
|
|
|
63
65
|
# Strip accents
|
|
64
66
|
mattr_accessor :strip_accents
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mongoid_search
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mauricio Zaffari
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-02-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: fast-stemmer
|
|
@@ -30,70 +30,14 @@ dependencies:
|
|
|
30
30
|
requirements:
|
|
31
31
|
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version:
|
|
33
|
+
version: 6.0.0
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version:
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: database_cleaner
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - ">="
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: 0.8.0
|
|
48
|
-
type: :development
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - ">="
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: 0.8.0
|
|
55
|
-
- !ruby/object:Gem::Dependency
|
|
56
|
-
name: mongoid-compatibility
|
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
|
58
|
-
requirements:
|
|
59
|
-
- - ">="
|
|
60
|
-
- !ruby/object:Gem::Version
|
|
61
|
-
version: '0'
|
|
62
|
-
type: :development
|
|
63
|
-
prerelease: false
|
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
-
requirements:
|
|
66
|
-
- - ">="
|
|
67
|
-
- !ruby/object:Gem::Version
|
|
68
|
-
version: '0'
|
|
69
|
-
- !ruby/object:Gem::Dependency
|
|
70
|
-
name: rake
|
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
|
72
|
-
requirements:
|
|
73
|
-
- - "<"
|
|
74
|
-
- !ruby/object:Gem::Version
|
|
75
|
-
version: '11.0'
|
|
76
|
-
type: :development
|
|
77
|
-
prerelease: false
|
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
-
requirements:
|
|
80
|
-
- - "<"
|
|
81
|
-
- !ruby/object:Gem::Version
|
|
82
|
-
version: '11.0'
|
|
83
|
-
- !ruby/object:Gem::Dependency
|
|
84
|
-
name: rspec
|
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
|
86
|
-
requirements:
|
|
87
|
-
- - "~>"
|
|
88
|
-
- !ruby/object:Gem::Version
|
|
89
|
-
version: '2.4'
|
|
90
|
-
type: :development
|
|
91
|
-
prerelease: false
|
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
-
requirements:
|
|
94
|
-
- - "~>"
|
|
95
|
-
- !ruby/object:Gem::Version
|
|
96
|
-
version: '2.4'
|
|
40
|
+
version: 6.0.0
|
|
97
41
|
description: Simple full text search implementation.
|
|
98
42
|
email:
|
|
99
43
|
- mauricio@papodenerd.net
|
|
@@ -104,27 +48,18 @@ files:
|
|
|
104
48
|
- LICENSE
|
|
105
49
|
- README.md
|
|
106
50
|
- Rakefile
|
|
107
|
-
- VERSION
|
|
108
51
|
- lib/mongoid_search.rb
|
|
109
52
|
- lib/mongoid_search/log.rb
|
|
110
53
|
- lib/mongoid_search/mongoid_search.rb
|
|
111
54
|
- lib/mongoid_search/railtie.rb
|
|
112
55
|
- lib/mongoid_search/tasks/mongoid_search.rake
|
|
113
56
|
- lib/mongoid_search/util.rb
|
|
114
|
-
|
|
115
|
-
- spec/models/category.rb
|
|
116
|
-
- spec/models/product.rb
|
|
117
|
-
- spec/models/subproduct.rb
|
|
118
|
-
- spec/models/tag.rb
|
|
119
|
-
- spec/models/variant.rb
|
|
120
|
-
- spec/mongoid_search_spec.rb
|
|
121
|
-
- spec/spec_helper.rb
|
|
122
|
-
- spec/util_spec.rb
|
|
123
|
-
homepage: http://www.papodenerd.net/mongoid-search-full-text-search-for-your-mongoid-models/
|
|
57
|
+
homepage: https://github.com/mongoid/mongoid_search
|
|
124
58
|
licenses:
|
|
125
59
|
- MIT
|
|
126
|
-
metadata:
|
|
127
|
-
|
|
60
|
+
metadata:
|
|
61
|
+
rubygems_mfa_required: 'true'
|
|
62
|
+
post_install_message:
|
|
128
63
|
rdoc_options: []
|
|
129
64
|
require_paths:
|
|
130
65
|
- lib
|
|
@@ -132,25 +67,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
132
67
|
requirements:
|
|
133
68
|
- - ">="
|
|
134
69
|
- !ruby/object:Gem::Version
|
|
135
|
-
version: '0'
|
|
70
|
+
version: '3.0'
|
|
136
71
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
137
72
|
requirements:
|
|
138
73
|
- - ">="
|
|
139
74
|
- !ruby/object:Gem::Version
|
|
140
75
|
version: 1.3.6
|
|
141
76
|
requirements: []
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
signing_key:
|
|
77
|
+
rubygems_version: 3.4.19
|
|
78
|
+
signing_key:
|
|
145
79
|
specification_version: 4
|
|
146
80
|
summary: Search implementation for Mongoid ORM
|
|
147
|
-
test_files:
|
|
148
|
-
- spec/models/variant.rb
|
|
149
|
-
- spec/models/category.rb
|
|
150
|
-
- spec/models/product.rb
|
|
151
|
-
- spec/models/tag.rb
|
|
152
|
-
- spec/models/subproduct.rb
|
|
153
|
-
- spec/config/ignorelist.yml
|
|
154
|
-
- spec/spec_helper.rb
|
|
155
|
-
- spec/util_spec.rb
|
|
156
|
-
- spec/mongoid_search_spec.rb
|
|
81
|
+
test_files: []
|
data/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.3.6
|
data/spec/config/ignorelist.yml
DELETED
data/spec/models/category.rb
DELETED
data/spec/models/product.rb
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
class Product
|
|
2
|
-
include Mongoid::Document
|
|
3
|
-
include Mongoid::Search
|
|
4
|
-
include Mongoid::Attributes::Dynamic if ::Mongoid::VERSION >= '4'
|
|
5
|
-
|
|
6
|
-
field :brand
|
|
7
|
-
field :name
|
|
8
|
-
field :unit
|
|
9
|
-
field :measures, type: Array
|
|
10
|
-
field :attrs, type: Array
|
|
11
|
-
field :info, type: Hash
|
|
12
|
-
|
|
13
|
-
has_many :tags
|
|
14
|
-
if Mongoid::Compatibility::Version.mongoid6_or_newer?
|
|
15
|
-
belongs_to :category, required: false
|
|
16
|
-
else
|
|
17
|
-
belongs_to :category
|
|
18
|
-
end
|
|
19
|
-
embeds_many :subproducts
|
|
20
|
-
|
|
21
|
-
search_in :brand, :name, :outlet, :attrs, tags: :name, category: %i[name description],
|
|
22
|
-
subproducts: %i[brand name], info: %i[summary description]
|
|
23
|
-
search_in :unit, :measures, index: :_unit_keywords
|
|
24
|
-
end
|
data/spec/models/subproduct.rb
DELETED
data/spec/models/tag.rb
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
class Tag
|
|
2
|
-
include Mongoid::Document
|
|
3
|
-
include Mongoid::Search
|
|
4
|
-
include Mongoid::Attributes::Dynamic if ::Mongoid::VERSION >= '4'
|
|
5
|
-
|
|
6
|
-
field :name
|
|
7
|
-
|
|
8
|
-
belongs_to :product
|
|
9
|
-
|
|
10
|
-
def title
|
|
11
|
-
name
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
search_in :title, product: [:name, { info: %i[summary description], category: %i[name description] }]
|
|
15
|
-
end
|
data/spec/models/variant.rb
DELETED
data/spec/mongoid_search_spec.rb
DELETED
|
@@ -1,371 +0,0 @@
|
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
2
|
-
|
|
3
|
-
describe Mongoid::Search do
|
|
4
|
-
before(:all) do
|
|
5
|
-
@default_proc = Mongoid::Search.stem_proc
|
|
6
|
-
@default_strip_symbols = Mongoid::Search.strip_symbols
|
|
7
|
-
@default_strip_accents = Mongoid::Search.strip_accents
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
after(:all) do
|
|
11
|
-
Mongoid::Search.stem_proc = @default_proc
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
before(:each) do
|
|
15
|
-
Mongoid::Search.match = :any
|
|
16
|
-
Mongoid::Search.stem_keywords = false
|
|
17
|
-
Mongoid::Search.ignore_list = nil
|
|
18
|
-
Mongoid::Search.stem_proc = @default_proc
|
|
19
|
-
@product = Product.create brand: 'Apple',
|
|
20
|
-
name: 'iPhone',
|
|
21
|
-
unit: 'mobile olé awesome',
|
|
22
|
-
tags: (@tags = %w[Amazing Awesome Olé].map { |tag| Tag.new(name: tag) }),
|
|
23
|
-
category: Category.new(name: 'Mobile', description: 'Reviews'),
|
|
24
|
-
subproducts: [Subproduct.new(brand: 'Apple', name: 'Craddle')],
|
|
25
|
-
info: { summary: 'Info-summary',
|
|
26
|
-
description: 'Info-description' }
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
describe 'Serialized hash fields' do
|
|
30
|
-
context 'when the hash is populated' do
|
|
31
|
-
it 'should return the product' do
|
|
32
|
-
expect(Product.full_text_search('Info-summary').first).to eq @product
|
|
33
|
-
expect(Product.full_text_search('Info-description').first).to eq @product
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
context 'when the hash is empty' do
|
|
38
|
-
before(:each) do
|
|
39
|
-
@product.info = nil
|
|
40
|
-
@product.save
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
it 'should not return the product' do
|
|
44
|
-
expect(Product.full_text_search('Info-description').size).to eq 0
|
|
45
|
-
expect(Product.full_text_search('Info-summary').size).to eq 0
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
context 'utf-8 characters' do
|
|
51
|
-
before do
|
|
52
|
-
Mongoid::Search.stem_keywords = false
|
|
53
|
-
Mongoid::Search.ignore_list = nil
|
|
54
|
-
@product = Product.create brand: 'Эльбрус',
|
|
55
|
-
name: 'Процессор',
|
|
56
|
-
unit: 'kílográm Olé',
|
|
57
|
-
tags: %w[Amazing Awesome Olé].map { |tag| Tag.new(name: tag) },
|
|
58
|
-
category: Category.new(name: 'процессоры'),
|
|
59
|
-
subproducts: []
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
it 'should leave utf8 characters' do
|
|
63
|
-
expect(@product._keywords).to eq %w[amazing awesome ole процессор процессоры эльбрус]
|
|
64
|
-
expect(@product._unit_keywords).to eq %w[kilogram ole]
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
it "should return results in search when case doesn't match" do
|
|
68
|
-
expect(Product.full_text_search('ЭЛЬБРУС').size).to eq 1
|
|
69
|
-
expect(Product.full_text_search('KILOGRAM', index: :_unit_keywords).size).to eq 1
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
context 'when references are nil' do
|
|
74
|
-
context 'when instance is being created' do
|
|
75
|
-
it 'should not complain about method missing' do
|
|
76
|
-
expect { Product.create! }.not_to raise_error
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
it 'should validate keywords' do
|
|
81
|
-
product = Product.create brand: 'Apple', name: 'iPhone', unit: 'box'
|
|
82
|
-
expect(product._keywords).to eq(%w[apple iphone])
|
|
83
|
-
expect(product._unit_keywords).to eq(%w[box])
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
it 'should set the _keywords field for array fields also' do
|
|
88
|
-
@product.attrs = ['lightweight', 'plastic', :red]
|
|
89
|
-
@product.measures = ['box', 'bunch', :bag]
|
|
90
|
-
@product.save!
|
|
91
|
-
expect(@product._keywords).to include 'lightweight', 'plastic', 'red'
|
|
92
|
-
expect(@product._unit_keywords).to include 'box', 'bunch', 'bag'
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
it 'should inherit _keywords field and build upon' do
|
|
96
|
-
variant = Variant.create brand: 'Apple',
|
|
97
|
-
name: 'iPhone',
|
|
98
|
-
tags: %w[Amazing Awesome Olé].map { |tag| Tag.new(name: tag) },
|
|
99
|
-
category: Category.new(name: 'Mobile'),
|
|
100
|
-
subproducts: [Subproduct.new(brand: 'Apple', name: 'Craddle')],
|
|
101
|
-
color: :white,
|
|
102
|
-
size: :big
|
|
103
|
-
expect(variant._keywords).to include 'white'
|
|
104
|
-
expect(variant._unit_keywords).to include 'big'
|
|
105
|
-
expect(Variant.full_text_search(name: 'Apple', color: :white)).to eq [variant]
|
|
106
|
-
expect(Variant.full_text_search({ size: 'big' }, index: :_unit_keywords)).to eq [variant]
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
it 'should expand the ligature to ease searching' do
|
|
110
|
-
# ref: http://en.wikipedia.org/wiki/Typographic_ligature, only for french right now. Rules for other languages are not know
|
|
111
|
-
variant1 = Variant.create tags: ['œuvre'].map { |tag| Tag.new(name: tag) }
|
|
112
|
-
variant2 = Variant.create tags: ['æquo'].map { |tag| Tag.new(name: tag) }
|
|
113
|
-
variant3 = Variant.create measures: ['ꜵquo'].map { |measure| measure }
|
|
114
|
-
|
|
115
|
-
expect(Variant.full_text_search('œuvre')).to eq [variant1]
|
|
116
|
-
expect(Variant.full_text_search('oeuvre')).to eq [variant1]
|
|
117
|
-
expect(Variant.full_text_search('æquo')).to eq [variant2]
|
|
118
|
-
expect(Variant.full_text_search('aequo')).to eq [variant2]
|
|
119
|
-
expect(Variant.full_text_search('aoquo', index: :_unit_keywords)).to eq [variant3]
|
|
120
|
-
expect(Variant.full_text_search('ꜵquo', index: :_unit_keywords)).to eq [variant3]
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
it 'should set the keywords fields with stemmed words if stem is enabled' do
|
|
124
|
-
Mongoid::Search.stem_keywords = true
|
|
125
|
-
@product.save!
|
|
126
|
-
expect(@product._keywords.sort).to eq %w[amaz appl awesom craddl iphon mobil review ol info descript summari].sort
|
|
127
|
-
expect(@product._unit_keywords.sort).to eq %w[mobil awesom ol].sort
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
it 'should set the keywords fields with custom stemmed words if stem is enabled with a custom lambda' do
|
|
131
|
-
Mongoid::Search.stem_keywords = true
|
|
132
|
-
Mongoid::Search.stem_proc = proc { |word| word.upcase }
|
|
133
|
-
@product.save!
|
|
134
|
-
expect(@product._keywords.sort).to eq %w[AMAZING APPLE AWESOME CRADDLE DESCRIPTION INFO IPHONE MOBILE OLE REVIEWS SUMMARY]
|
|
135
|
-
expect(@product._unit_keywords.sort).to eq %w[AWESOME MOBILE OLE]
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
it 'should ignore keywords in an ignore list' do
|
|
139
|
-
Mongoid::Search.ignore_list = YAML.safe_load(File.open(File.dirname(__FILE__) + '/config/ignorelist.yml'))['ignorelist']
|
|
140
|
-
@product.save!
|
|
141
|
-
expect(@product._keywords.sort).to eq %w[apple craddle iphone mobile reviews ole info description summary].sort
|
|
142
|
-
expect(@product._unit_keywords.sort).to eq %w[mobile ole].sort
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
it 'should incorporate numbers as keywords' do
|
|
146
|
-
@product = Product.create brand: 'Ford',
|
|
147
|
-
name: 'T 1908',
|
|
148
|
-
tags: %w[Amazing First Car].map { |tag| Tag.new(name: tag) },
|
|
149
|
-
category: Category.new(name: 'Vehicle')
|
|
150
|
-
|
|
151
|
-
@product.save!
|
|
152
|
-
expect(@product._keywords).to eq %w[1908 amazing car first ford vehicle]
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
it 'should return results in search' do
|
|
156
|
-
expect(Product.full_text_search('apple').size).to eq 1
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
it 'should return results in search for dynamic attribute' do
|
|
160
|
-
@product[:outlet] = 'online shop'
|
|
161
|
-
@product.save!
|
|
162
|
-
expect(Product.full_text_search('online').size).to eq 1
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
it 'should return results in search even searching a accented word' do
|
|
166
|
-
expect(Product.full_text_search('Ole').size).to eq 1
|
|
167
|
-
expect(Product.full_text_search('Olé').size).to eq 1
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
it "should return results in search even if the case doesn't match" do
|
|
171
|
-
expect(Product.full_text_search('oLe').size).to eq 1
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
it 'should return results in search with a partial word by default' do
|
|
175
|
-
expect(Product.full_text_search('iph').size).to eq 1
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
it 'should return results for any matching word with default search' do
|
|
179
|
-
expect(Product.full_text_search('apple motorola').size).to eq 1
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
it 'should not return results when all words do not match, if using :match => :all' do
|
|
183
|
-
Mongoid::Search.match = :all
|
|
184
|
-
expect(Product.full_text_search('apple motorola').size).to eq 0
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
it 'should return results for any matching word, using :match => :all, passing :match => :any to .full_text_search' do
|
|
188
|
-
Mongoid::Search.match = :all
|
|
189
|
-
expect(Product.full_text_search('apple motorola', match: :any).size).to eq 1
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
it 'should not return results when all words do not match, passing :match => :all to .full_text_search' do
|
|
193
|
-
expect(Product.full_text_search('apple motorola', match: :all).size).to eq 0
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
it 'should return no results when a blank search is made' do
|
|
197
|
-
Mongoid::Search.allow_empty_search = false
|
|
198
|
-
expect(Product.full_text_search('').size).to eq 0
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
it 'should return results when a blank search is made when :allow_empty_search is true' do
|
|
202
|
-
Mongoid::Search.allow_empty_search = true
|
|
203
|
-
expect(Product.full_text_search('').size).to eq 1
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
it 'should search for embedded documents' do
|
|
207
|
-
expect(Product.full_text_search('craddle').size).to eq 1
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
it 'should search for reference documents' do
|
|
211
|
-
expect(Product.full_text_search('reviews').size).to eq 1
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
it 'should work in a chainable fashion' do
|
|
215
|
-
expect(@product.category.products.where(brand: 'Apple').full_text_search('apple').size).to eq 1
|
|
216
|
-
expect(@product.category.products.full_text_search('craddle').where(brand: 'Apple').size).to eq 1
|
|
217
|
-
end
|
|
218
|
-
|
|
219
|
-
it 'should return the classes that include the search module' do
|
|
220
|
-
expect(Mongoid::Search.classes.sort_by(&:name)).to eq [Product, Tag]
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
it 'should have a method to index keywords' do
|
|
224
|
-
expect(@product.index_keywords!).to include(true)
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
it 'should have a class method to index all documents keywords' do
|
|
228
|
-
expect(Product.index_keywords!).not_to include(false)
|
|
229
|
-
end
|
|
230
|
-
|
|
231
|
-
context 'when regex search is false' do
|
|
232
|
-
before do
|
|
233
|
-
Mongoid::Search.regex_search = false
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
it 'should not return results in search with a partial word if not using regex search' do
|
|
237
|
-
expect(Product.full_text_search('iph').size).to eq 0
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
it 'should return results in search with a full word if not using regex search' do
|
|
241
|
-
expect(Product.full_text_search('iphone').size).to eq 1
|
|
242
|
-
end
|
|
243
|
-
end
|
|
244
|
-
|
|
245
|
-
context 'when regex search is true' do
|
|
246
|
-
before do
|
|
247
|
-
Mongoid::Search.regex_search = true
|
|
248
|
-
end
|
|
249
|
-
|
|
250
|
-
after do
|
|
251
|
-
Mongoid::Search.regex_search = false
|
|
252
|
-
end
|
|
253
|
-
|
|
254
|
-
it 'should not return results in search with a partial word if using regex search' do
|
|
255
|
-
expect(Product.full_text_search('iph').size).to eq 1
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
it 'should return results in search with a full word if using regex search' do
|
|
259
|
-
expect(Product.full_text_search('iphone').size).to eq 1
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
context 'when query include special characters that should not be stripped' do
|
|
263
|
-
before do
|
|
264
|
-
Mongoid::Search.strip_symbols = /[\n]/
|
|
265
|
-
Mongoid::Search.strip_accents = /[^\s\p{Graph}]/
|
|
266
|
-
end
|
|
267
|
-
|
|
268
|
-
after do
|
|
269
|
-
Mongoid::Search.strip_symbols = @default_strip_symbols
|
|
270
|
-
Mongoid::Search.strip_accents = @default_strip_accents
|
|
271
|
-
end
|
|
272
|
-
|
|
273
|
-
it 'should escape regex special characters' do
|
|
274
|
-
expect(Product.full_text_search('iphone (steve').size).to eq 1
|
|
275
|
-
end
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
context 'Match partial words on the beginning' do
|
|
279
|
-
before do
|
|
280
|
-
Mongoid::Search.regex = proc { |query| /^#{query}/ }
|
|
281
|
-
end
|
|
282
|
-
|
|
283
|
-
it 'should return results in search which starts with query string' do
|
|
284
|
-
expect(Product.full_text_search('iphone').size).to eq 1
|
|
285
|
-
end
|
|
286
|
-
|
|
287
|
-
it 'should not return results in search which does not start with query string' do
|
|
288
|
-
expect(Product.full_text_search('phone').size).to eq 0
|
|
289
|
-
end
|
|
290
|
-
end
|
|
291
|
-
|
|
292
|
-
context 'Match partial words on the end' do
|
|
293
|
-
before do
|
|
294
|
-
Mongoid::Search.regex = proc { |query| /#{query}$/ }
|
|
295
|
-
end
|
|
296
|
-
|
|
297
|
-
it 'should return results in search which ends with query string' do
|
|
298
|
-
expect(Product.full_text_search('phone').size).to eq 1
|
|
299
|
-
end
|
|
300
|
-
|
|
301
|
-
it 'should not return results in search which does not end with query string' do
|
|
302
|
-
expect(Product.full_text_search('phon').size).to eq 0
|
|
303
|
-
end
|
|
304
|
-
end
|
|
305
|
-
end
|
|
306
|
-
|
|
307
|
-
context 'relevant search' do
|
|
308
|
-
before do
|
|
309
|
-
Mongoid::Search.relevant_search = true
|
|
310
|
-
@imac = Product.create name: 'apple imac'
|
|
311
|
-
end
|
|
312
|
-
|
|
313
|
-
it 'should return results ordered by relevance and with correct ids' do
|
|
314
|
-
expect(Product.full_text_search('apple imac').map(&:_id)).to eq [@imac._id, @product._id]
|
|
315
|
-
end
|
|
316
|
-
|
|
317
|
-
it 'results should be recognized as persisted objects' do
|
|
318
|
-
expect(Product.full_text_search('apple imac').map(&:persisted?)).not_to include false
|
|
319
|
-
end
|
|
320
|
-
|
|
321
|
-
it 'should include relevance information' do
|
|
322
|
-
expect(Product.full_text_search('apple imac').map(&:relevance)).to eq [2, 1]
|
|
323
|
-
end
|
|
324
|
-
end
|
|
325
|
-
|
|
326
|
-
context 'when using methods for keywords' do
|
|
327
|
-
it 'should set the _keywords from methods' do
|
|
328
|
-
expect(@tags.first._keywords).to include 'amazing'
|
|
329
|
-
end
|
|
330
|
-
end
|
|
331
|
-
|
|
332
|
-
context 'when using deeply nested fields for keywords' do
|
|
333
|
-
context 'when explicitly calling set_keywords' do
|
|
334
|
-
it 'should set the _keywords from parent' do
|
|
335
|
-
@tags.first.send(:set_keywords)
|
|
336
|
-
expect(@tags.first._keywords).to eq %w[amazing description info iphone mobile reviews summary]
|
|
337
|
-
end
|
|
338
|
-
end
|
|
339
|
-
end
|
|
340
|
-
|
|
341
|
-
context 'when using localized fields' do
|
|
342
|
-
it 'should set the keywords from all localizations' do
|
|
343
|
-
@product = Product.create brand: 'Ford',
|
|
344
|
-
name: 'T 1908',
|
|
345
|
-
tags: %w[Amazing First Car].map { |tag| Tag.new(name: tag) },
|
|
346
|
-
category: Category.new(name_translations: { en: 'Vehicle', de: 'Fahrzeug' })
|
|
347
|
-
expect(@product._keywords).to include('fahrzeug')
|
|
348
|
-
end
|
|
349
|
-
end
|
|
350
|
-
|
|
351
|
-
context 'minimum word size' do
|
|
352
|
-
before(:each) do
|
|
353
|
-
Mongoid::Search.minimum_word_size = 3
|
|
354
|
-
end
|
|
355
|
-
|
|
356
|
-
after(:each) do
|
|
357
|
-
Mongoid::Search.minimum_word_size = 2
|
|
358
|
-
end
|
|
359
|
-
|
|
360
|
-
it 'should ignore keywords with length less than minimum word size' do
|
|
361
|
-
@product = Product.create name: 'a'
|
|
362
|
-
expect(@product._keywords.size).to eq 0
|
|
363
|
-
|
|
364
|
-
@product = Product.create name: 'ap'
|
|
365
|
-
expect(@product._keywords.size).to eq 0
|
|
366
|
-
|
|
367
|
-
@product = Product.create name: 'app'
|
|
368
|
-
expect(@product._keywords.size).to eq 1
|
|
369
|
-
end
|
|
370
|
-
end
|
|
371
|
-
end
|
data/spec/spec_helper.rb
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
require 'simplecov'
|
|
2
|
-
SimpleCov.start
|
|
3
|
-
|
|
4
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
5
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
6
|
-
require 'mongoid'
|
|
7
|
-
require 'database_cleaner'
|
|
8
|
-
require 'fast_stemmer'
|
|
9
|
-
require 'yaml'
|
|
10
|
-
require 'mongoid_search'
|
|
11
|
-
require 'mongoid-compatibility'
|
|
12
|
-
|
|
13
|
-
Mongoid.configure do |config|
|
|
14
|
-
config.connect_to 'mongoid_search_test'
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
Dir["#{File.dirname(__FILE__)}/models/*.rb"].each { |file| require file }
|
|
18
|
-
|
|
19
|
-
DatabaseCleaner.orm = :mongoid
|
|
20
|
-
|
|
21
|
-
RSpec.configure do |config|
|
|
22
|
-
config.before(:all) do
|
|
23
|
-
Mongoid.logger.level = Logger::INFO
|
|
24
|
-
Mongo::Logger.logger.level = Logger::INFO if Mongoid::Compatibility::Version.mongoid5_or_newer?
|
|
25
|
-
DatabaseCleaner.strategy = :truncation
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
config.before(:each) do
|
|
29
|
-
DatabaseCleaner.start
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
config.after(:each) do
|
|
33
|
-
DatabaseCleaner.clean
|
|
34
|
-
end
|
|
35
|
-
end
|
data/spec/util_spec.rb
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
3
|
-
|
|
4
|
-
describe Mongoid::Search::Util do
|
|
5
|
-
before(:all) do
|
|
6
|
-
@default_proc = Mongoid::Search.stem_proc
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
after(:all) do
|
|
10
|
-
Mongoid::Search.stem_proc = @default_proc
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
before do
|
|
14
|
-
Mongoid::Search.stem_keywords = false
|
|
15
|
-
Mongoid::Search.ignore_list = ''
|
|
16
|
-
Mongoid::Search.stem_proc = @default_proc
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
it 'should return an empty array if no text is passed' do
|
|
20
|
-
expect(Mongoid::Search::Util.normalize_keywords('')).to eq []
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
it 'should return an array of keywords' do
|
|
24
|
-
expect(Mongoid::Search::Util.normalize_keywords('keyword').class).to eq Array
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
it 'should return an array of strings' do
|
|
28
|
-
expect(Mongoid::Search::Util.normalize_keywords('keyword').first.class).to eq String
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
it 'should remove accents from the text passed' do
|
|
32
|
-
expect(Mongoid::Search::Util.normalize_keywords('café')).to eq ['cafe']
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
it 'should downcase the text passed' do
|
|
36
|
-
expect(Mongoid::Search::Util.normalize_keywords('CaFé')).to eq ['cafe']
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
it 'should downcase utf-8 chars of the text passed' do
|
|
40
|
-
expect(Mongoid::Search::Util.normalize_keywords('Кафе')).to eq ['кафе']
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
it 'should split whitespaces, hifens, dots, underlines, etc..' do
|
|
44
|
-
expect(Mongoid::Search::Util.normalize_keywords("CaFé-express.com delicious;come visit, and 'win' an \"iPad\"")).to eq %w[cafe express com delicious come visit and win an ipad]
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
it 'should stem keywords' do
|
|
48
|
-
Mongoid::Search.stem_keywords = true
|
|
49
|
-
expect(Mongoid::Search::Util.normalize_keywords('A runner running and eating')).to eq %w[runner run and eat]
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
it 'should stem keywords using a custom proc' do
|
|
53
|
-
Mongoid::Search.stem_keywords = true
|
|
54
|
-
Mongoid::Search.stem_proc = ->(word) { word.upcase }
|
|
55
|
-
|
|
56
|
-
expect(Mongoid::Search::Util.normalize_keywords('A runner running and eating')).to eq %w[RUNNER RUNNING AND EATING]
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
it 'should ignore keywords from ignore list' do
|
|
60
|
-
Mongoid::Search.stem_keywords = true
|
|
61
|
-
Mongoid::Search.ignore_list = YAML.safe_load(File.open(File.dirname(__FILE__) + '/config/ignorelist.yml'))['ignorelist']
|
|
62
|
-
expect(Mongoid::Search::Util.normalize_keywords('An amazing awesome runner running and eating')).to eq %w[an runner run and eat]
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
it 'should ignore keywords with less than two words' do
|
|
66
|
-
expect(Mongoid::Search::Util.normalize_keywords('A runner running')).not_to include 'a'
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
it 'should not ignore numbers' do
|
|
70
|
-
expect(Mongoid::Search::Util.normalize_keywords('Ford T 1908')).to include '1908'
|
|
71
|
-
end
|
|
72
|
-
end
|