searchkick 1.3.4 → 1.3.5
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 +4 -4
- data/.travis.yml +3 -3
- data/CHANGELOG.md +6 -0
- data/Gemfile +1 -0
- data/README.md +31 -11
- data/lib/searchkick.rb +14 -11
- data/lib/searchkick/index.rb +10 -357
- data/lib/searchkick/index_options.rb +359 -0
- data/lib/searchkick/query.rb +44 -29
- data/lib/searchkick/version.rb +1 -1
- data/test/aggs_test.rb +8 -10
- data/test/boost_test.rb +2 -2
- data/test/errors_test.rb +1 -1
- data/test/index_test.rb +9 -1
- data/test/query_test.rb +6 -1
- data/test/sql_test.rb +32 -1
- data/test/test_helper.rb +11 -9
- data/test/where_test.rb +7 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e69261f029a233983def6aaec3cd101621807fe8
|
4
|
+
data.tar.gz: 93d6d5e53193af4ae04537c012845d336a9b7197
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a2f79b352af2ae191c719480e648e07fc8083e3d6c836632e5f1f5245786386bad8e0397d15f3c13771a7197e92052a9c2ba269de23f3f5858fb528642eff26
|
7
|
+
data.tar.gz: 57972ac3b439dcbf77bef6d9f121383f93822ae3e3c0b6bbf64baedbe0f46a2dd1dd8c9ded33a0384bb80abb5922a33bd216cdeea9271c25f7dc754fd0f7c5a4
|
data/.travis.yml
CHANGED
@@ -24,7 +24,7 @@ gemfile:
|
|
24
24
|
- test/gemfiles/mongoid4.gemfile
|
25
25
|
- test/gemfiles/mongoid5.gemfile
|
26
26
|
env:
|
27
|
-
- ELASTICSEARCH_VERSION=2.
|
27
|
+
- ELASTICSEARCH_VERSION=2.4.0
|
28
28
|
matrix:
|
29
29
|
include:
|
30
30
|
- gemfile: Gemfile
|
@@ -34,11 +34,11 @@ matrix:
|
|
34
34
|
- gemfile: Gemfile
|
35
35
|
env: ELASTICSEARCH_VERSION=2.0.0
|
36
36
|
- gemfile: Gemfile
|
37
|
-
env: ELASTICSEARCH_VERSION=5.0.0-
|
37
|
+
env: ELASTICSEARCH_VERSION=5.0.0-beta1
|
38
38
|
# - gemfile: test/gemfiles/nobrainer.gemfile
|
39
39
|
# env: NOBRAINER=true
|
40
40
|
allow_failures:
|
41
41
|
- gemfile: Gemfile
|
42
|
-
env: ELASTICSEARCH_VERSION=5.0.0-
|
42
|
+
env: ELASTICSEARCH_VERSION=5.0.0-beta1
|
43
43
|
# - gemfile: test/gemfiles/nobrainer.gemfile
|
44
44
|
# env: NOBRAINER=true
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -116,6 +116,12 @@ Limit / offset
|
|
116
116
|
limit: 20, offset: 40
|
117
117
|
```
|
118
118
|
|
119
|
+
Select
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
select_v2: ["name"]
|
123
|
+
```
|
124
|
+
|
119
125
|
### Results
|
120
126
|
|
121
127
|
Searches return a `Searchkick::Results` object. This responds like an array to most methods.
|
@@ -127,6 +133,12 @@ results.any?
|
|
127
133
|
results.each { |result| ... }
|
128
134
|
```
|
129
135
|
|
136
|
+
By default, ids are fetched from Elasticsearch and records are fetched from your database. To fetch everything from Elasticsearch, use:
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
Product.search("apples", load: false)
|
140
|
+
```
|
141
|
+
|
130
142
|
Get total results
|
131
143
|
|
132
144
|
```ruby
|
@@ -476,7 +488,7 @@ Next, add conversions to the index.
|
|
476
488
|
|
477
489
|
```ruby
|
478
490
|
class Product < ActiveRecord::Base
|
479
|
-
has_many :searches, class_name: "Searchjoy::Search"
|
491
|
+
has_many :searches, class_name: "Searchjoy::Search", as: :convertable
|
480
492
|
|
481
493
|
searchkick conversions: ["conversions"] # name of field
|
482
494
|
|
@@ -593,7 +605,7 @@ products.suggestions # ["peanut butter"]
|
|
593
605
|
|
594
606
|
### Aggregations
|
595
607
|
|
596
|
-
[Aggregations](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-
|
608
|
+
[Aggregations](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html) provide aggregated search data.
|
597
609
|
|
598
610
|

|
599
611
|
|
@@ -1121,7 +1133,7 @@ products =
|
|
1121
1133
|
end
|
1122
1134
|
```
|
1123
1135
|
|
1124
|
-
|
1136
|
+
## Multi Search
|
1125
1137
|
|
1126
1138
|
To batch search requests for performance, use:
|
1127
1139
|
|
@@ -1135,7 +1147,7 @@ Then use `fresh_products` and `frozen_products` as typical results.
|
|
1135
1147
|
|
1136
1148
|
**Note:** Errors are not raised as with single requests. Use the `error` method on each query to check for errors. Also, if you use the `below` option for misspellings, misspellings will be disabled.
|
1137
1149
|
|
1138
|
-
|
1150
|
+
## Multiple Indices
|
1139
1151
|
|
1140
1152
|
Search across multiple indices with:
|
1141
1153
|
|
@@ -1149,6 +1161,14 @@ Boost specific indices with:
|
|
1149
1161
|
indices_boost: {Category => 2, Product => 1}
|
1150
1162
|
```
|
1151
1163
|
|
1164
|
+
## Nested Data
|
1165
|
+
|
1166
|
+
To query nested data, use dot notation.
|
1167
|
+
|
1168
|
+
```ruby
|
1169
|
+
User.search "*", where: {"address.zip_code" => 12345}
|
1170
|
+
```
|
1171
|
+
|
1152
1172
|
## Reference
|
1153
1173
|
|
1154
1174
|
Reindex one record
|
@@ -1267,13 +1287,7 @@ Searchkick.search_method_name = :lookup
|
|
1267
1287
|
Eager load associations
|
1268
1288
|
|
1269
1289
|
```ruby
|
1270
|
-
Product.search "milk",
|
1271
|
-
```
|
1272
|
-
|
1273
|
-
Do not load models
|
1274
|
-
|
1275
|
-
```ruby
|
1276
|
-
Product.search "milk", load: false
|
1290
|
+
Product.search "milk", includes: [:brand, :stores]
|
1277
1291
|
```
|
1278
1292
|
|
1279
1293
|
Turn off special characters
|
@@ -1314,6 +1328,12 @@ products = Product.search("carrots", execute: false)
|
|
1314
1328
|
products.each { ... } # search not executed until here
|
1315
1329
|
```
|
1316
1330
|
|
1331
|
+
Add [request parameters](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-uri-request.html), like `search_type` and `query_cache`
|
1332
|
+
|
1333
|
+
```ruby
|
1334
|
+
Product.search("carrots", request_params: {search_type: "dfs_query_then_fetch"})
|
1335
|
+
```
|
1336
|
+
|
1317
1337
|
Make fields unsearchable but include in the source
|
1318
1338
|
|
1319
1339
|
```ruby
|
data/lib/searchkick.rb
CHANGED
@@ -2,6 +2,7 @@ require "active_model"
|
|
2
2
|
require "elasticsearch"
|
3
3
|
require "hashie"
|
4
4
|
require "searchkick/version"
|
5
|
+
require "searchkick/index_options"
|
5
6
|
require "searchkick/index"
|
6
7
|
require "searchkick/results"
|
7
8
|
require "searchkick/query"
|
@@ -11,14 +12,6 @@ require "searchkick/tasks"
|
|
11
12
|
require "searchkick/middleware"
|
12
13
|
require "searchkick/logging" if defined?(ActiveSupport::Notifications)
|
13
14
|
|
14
|
-
# background jobs
|
15
|
-
begin
|
16
|
-
require "active_job"
|
17
|
-
rescue LoadError
|
18
|
-
# do nothing
|
19
|
-
end
|
20
|
-
require "searchkick/reindex_v2_job" if defined?(ActiveJob)
|
21
|
-
|
22
15
|
module Searchkick
|
23
16
|
class Error < StandardError; end
|
24
17
|
class MissingIndexError < Error; end
|
@@ -37,13 +30,16 @@ module Searchkick
|
|
37
30
|
self.models = []
|
38
31
|
|
39
32
|
def self.client
|
40
|
-
@client ||=
|
33
|
+
@client ||= begin
|
34
|
+
require "typhoeus/adapters/faraday" if defined?(Typhoeus)
|
35
|
+
|
41
36
|
Elasticsearch::Client.new(
|
42
37
|
url: ENV["ELASTICSEARCH_URL"],
|
43
|
-
transport_options: {request: {timeout: timeout}}
|
38
|
+
transport_options: {request: {timeout: timeout}, headers: {content_type: "application/json"}}
|
44
39
|
) do |f|
|
45
40
|
f.use Searchkick::Middleware
|
46
41
|
end
|
42
|
+
end
|
47
43
|
end
|
48
44
|
|
49
45
|
def self.env
|
@@ -156,6 +152,13 @@ module Searchkick
|
|
156
152
|
end
|
157
153
|
end
|
158
154
|
|
155
|
+
ActiveSupport.on_load(:active_job) do
|
156
|
+
require "searchkick/reindex_v2_job"
|
157
|
+
end
|
158
|
+
|
159
159
|
# TODO find better ActiveModel hook
|
160
160
|
ActiveModel::Callbacks.send(:include, Searchkick::Model)
|
161
|
-
|
161
|
+
|
162
|
+
ActiveSupport.on_load(:active_record) do
|
163
|
+
ActiveRecord::Base.send(:extend, Searchkick::Model)
|
164
|
+
end
|
data/lib/searchkick/index.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module Searchkick
|
2
2
|
class Index
|
3
|
+
include IndexOptions
|
4
|
+
|
3
5
|
attr_reader :name, :options
|
4
6
|
|
5
7
|
def initialize(name, options = {})
|
@@ -95,8 +97,10 @@ module Searchkick
|
|
95
97
|
if Searchkick.callbacks_value.nil?
|
96
98
|
if defined?(Searchkick::ReindexV2Job)
|
97
99
|
Searchkick::ReindexV2Job.perform_later(record.class.name, record.id.to_s)
|
98
|
-
|
100
|
+
elsif defined?(Delayed::Job)
|
99
101
|
Delayed::Job.enqueue Searchkick::ReindexJob.new(record.class.name, record.id.to_s)
|
102
|
+
else
|
103
|
+
raise Searchkick::Error, "Job adapter not found"
|
100
104
|
end
|
101
105
|
else
|
102
106
|
reindex_record(record)
|
@@ -123,7 +127,7 @@ module Searchkick
|
|
123
127
|
|
124
128
|
def search_model(searchkick_klass, term = nil, options = {}, &block)
|
125
129
|
query = Searchkick::Query.new(searchkick_klass, term, options)
|
126
|
-
|
130
|
+
yield(query.body) if block
|
127
131
|
if options[:execute] == false
|
128
132
|
query
|
129
133
|
else
|
@@ -147,8 +151,8 @@ module Searchkick
|
|
147
151
|
rescue Elasticsearch::Transport::Transport::Errors::NotFound
|
148
152
|
{}
|
149
153
|
end
|
150
|
-
indices = indices.select { |
|
151
|
-
indices.select { |k,
|
154
|
+
indices = indices.select { |_k, v| v.empty? || v["aliases"].empty? } if options[:unaliased]
|
155
|
+
indices.select { |k, _v| k =~ /\A#{Regexp.escape(name)}_\d{14,17}\z/ }.keys
|
152
156
|
end
|
153
157
|
|
154
158
|
# remove old indices that start w/ index_name
|
@@ -244,357 +248,6 @@ module Searchkick
|
|
244
248
|
end
|
245
249
|
end
|
246
250
|
|
247
|
-
def index_options
|
248
|
-
options = @options
|
249
|
-
language = options[:language]
|
250
|
-
language = language.call if language.respond_to?(:call)
|
251
|
-
|
252
|
-
if options[:mappings] && !options[:merge_mappings]
|
253
|
-
settings = options[:settings] || {}
|
254
|
-
mappings = options[:mappings]
|
255
|
-
else
|
256
|
-
below22 = Searchkick.server_below?("2.2.0")
|
257
|
-
below50 = Searchkick.server_below?("5.0.0-alpha1")
|
258
|
-
default_type = below50 ? "string" : "text"
|
259
|
-
default_analyzer = below50 ? :default_index : :default
|
260
|
-
keyword_mapping =
|
261
|
-
if below50
|
262
|
-
{
|
263
|
-
type: default_type,
|
264
|
-
index: "not_analyzed"
|
265
|
-
}
|
266
|
-
else
|
267
|
-
{
|
268
|
-
type: "keyword"
|
269
|
-
}
|
270
|
-
end
|
271
|
-
|
272
|
-
keyword_mapping[:ignore_above] = 256 unless below22
|
273
|
-
|
274
|
-
settings = {
|
275
|
-
analysis: {
|
276
|
-
analyzer: {
|
277
|
-
searchkick_keyword: {
|
278
|
-
type: "custom",
|
279
|
-
tokenizer: "keyword",
|
280
|
-
filter: ["lowercase"] + (options[:stem_conversions] == false ? [] : ["searchkick_stemmer"])
|
281
|
-
},
|
282
|
-
default_analyzer => {
|
283
|
-
type: "custom",
|
284
|
-
# character filters -> tokenizer -> token filters
|
285
|
-
# https://www.elastic.co/guide/en/elasticsearch/guide/current/analysis-intro.html
|
286
|
-
char_filter: ["ampersand"],
|
287
|
-
tokenizer: "standard",
|
288
|
-
# synonym should come last, after stemming and shingle
|
289
|
-
# shingle must come before searchkick_stemmer
|
290
|
-
filter: ["standard", "lowercase", "asciifolding", "searchkick_index_shingle", "searchkick_stemmer"]
|
291
|
-
},
|
292
|
-
searchkick_search: {
|
293
|
-
type: "custom",
|
294
|
-
char_filter: ["ampersand"],
|
295
|
-
tokenizer: "standard",
|
296
|
-
filter: ["standard", "lowercase", "asciifolding", "searchkick_search_shingle", "searchkick_stemmer"]
|
297
|
-
},
|
298
|
-
searchkick_search2: {
|
299
|
-
type: "custom",
|
300
|
-
char_filter: ["ampersand"],
|
301
|
-
tokenizer: "standard",
|
302
|
-
filter: ["standard", "lowercase", "asciifolding", "searchkick_stemmer"]
|
303
|
-
},
|
304
|
-
# https://github.com/leschenko/elasticsearch_autocomplete/blob/master/lib/elasticsearch_autocomplete/analyzers.rb
|
305
|
-
searchkick_autocomplete_index: {
|
306
|
-
type: "custom",
|
307
|
-
tokenizer: "searchkick_autocomplete_ngram",
|
308
|
-
filter: ["lowercase", "asciifolding"]
|
309
|
-
},
|
310
|
-
searchkick_autocomplete_search: {
|
311
|
-
type: "custom",
|
312
|
-
tokenizer: "keyword",
|
313
|
-
filter: ["lowercase", "asciifolding"]
|
314
|
-
},
|
315
|
-
searchkick_word_search: {
|
316
|
-
type: "custom",
|
317
|
-
tokenizer: "standard",
|
318
|
-
filter: ["lowercase", "asciifolding"]
|
319
|
-
},
|
320
|
-
searchkick_suggest_index: {
|
321
|
-
type: "custom",
|
322
|
-
tokenizer: "standard",
|
323
|
-
filter: ["lowercase", "asciifolding", "searchkick_suggest_shingle"]
|
324
|
-
},
|
325
|
-
searchkick_text_start_index: {
|
326
|
-
type: "custom",
|
327
|
-
tokenizer: "keyword",
|
328
|
-
filter: ["lowercase", "asciifolding", "searchkick_edge_ngram"]
|
329
|
-
},
|
330
|
-
searchkick_text_middle_index: {
|
331
|
-
type: "custom",
|
332
|
-
tokenizer: "keyword",
|
333
|
-
filter: ["lowercase", "asciifolding", "searchkick_ngram"]
|
334
|
-
},
|
335
|
-
searchkick_text_end_index: {
|
336
|
-
type: "custom",
|
337
|
-
tokenizer: "keyword",
|
338
|
-
filter: ["lowercase", "asciifolding", "reverse", "searchkick_edge_ngram", "reverse"]
|
339
|
-
},
|
340
|
-
searchkick_word_start_index: {
|
341
|
-
type: "custom",
|
342
|
-
tokenizer: "standard",
|
343
|
-
filter: ["lowercase", "asciifolding", "searchkick_edge_ngram"]
|
344
|
-
},
|
345
|
-
searchkick_word_middle_index: {
|
346
|
-
type: "custom",
|
347
|
-
tokenizer: "standard",
|
348
|
-
filter: ["lowercase", "asciifolding", "searchkick_ngram"]
|
349
|
-
},
|
350
|
-
searchkick_word_end_index: {
|
351
|
-
type: "custom",
|
352
|
-
tokenizer: "standard",
|
353
|
-
filter: ["lowercase", "asciifolding", "reverse", "searchkick_edge_ngram", "reverse"]
|
354
|
-
}
|
355
|
-
},
|
356
|
-
filter: {
|
357
|
-
searchkick_index_shingle: {
|
358
|
-
type: "shingle",
|
359
|
-
token_separator: ""
|
360
|
-
},
|
361
|
-
# lucky find http://web.archiveorange.com/archive/v/AAfXfQ17f57FcRINsof7
|
362
|
-
searchkick_search_shingle: {
|
363
|
-
type: "shingle",
|
364
|
-
token_separator: "",
|
365
|
-
output_unigrams: false,
|
366
|
-
output_unigrams_if_no_shingles: true
|
367
|
-
},
|
368
|
-
searchkick_suggest_shingle: {
|
369
|
-
type: "shingle",
|
370
|
-
max_shingle_size: 5
|
371
|
-
},
|
372
|
-
searchkick_edge_ngram: {
|
373
|
-
type: "edgeNGram",
|
374
|
-
min_gram: 1,
|
375
|
-
max_gram: 50
|
376
|
-
},
|
377
|
-
searchkick_ngram: {
|
378
|
-
type: "nGram",
|
379
|
-
min_gram: 1,
|
380
|
-
max_gram: 50
|
381
|
-
},
|
382
|
-
searchkick_stemmer: {
|
383
|
-
# use stemmer if language is lowercase, snowball otherwise
|
384
|
-
# TODO deprecate language option in favor of stemmer
|
385
|
-
type: language == language.to_s.downcase ? "stemmer" : "snowball",
|
386
|
-
language: language || "English"
|
387
|
-
}
|
388
|
-
},
|
389
|
-
char_filter: {
|
390
|
-
# https://www.elastic.co/guide/en/elasticsearch/guide/current/custom-analyzers.html
|
391
|
-
# &_to_and
|
392
|
-
ampersand: {
|
393
|
-
type: "mapping",
|
394
|
-
mappings: ["&=> and "]
|
395
|
-
}
|
396
|
-
},
|
397
|
-
tokenizer: {
|
398
|
-
searchkick_autocomplete_ngram: {
|
399
|
-
type: "edgeNGram",
|
400
|
-
min_gram: 1,
|
401
|
-
max_gram: 50
|
402
|
-
}
|
403
|
-
}
|
404
|
-
}
|
405
|
-
}
|
406
|
-
|
407
|
-
if Searchkick.env == "test"
|
408
|
-
settings.merge!(number_of_shards: 1, number_of_replicas: 0)
|
409
|
-
end
|
410
|
-
|
411
|
-
if options[:similarity]
|
412
|
-
settings[:similarity] = {default: {type: options[:similarity]}}
|
413
|
-
end
|
414
|
-
|
415
|
-
settings.deep_merge!(options[:settings] || {})
|
416
|
-
|
417
|
-
# synonyms
|
418
|
-
synonyms = options[:synonyms] || []
|
419
|
-
|
420
|
-
synonyms = synonyms.call if synonyms.respond_to?(:call)
|
421
|
-
|
422
|
-
if synonyms.any?
|
423
|
-
settings[:analysis][:filter][:searchkick_synonym] = {
|
424
|
-
type: "synonym",
|
425
|
-
synonyms: synonyms.select { |s| s.size > 1 }.map { |s| s.join(",") }
|
426
|
-
}
|
427
|
-
# choosing a place for the synonym filter when stemming is not easy
|
428
|
-
# https://groups.google.com/forum/#!topic/elasticsearch/p7qcQlgHdB8
|
429
|
-
# TODO use a snowball stemmer on synonyms when creating the token filter
|
430
|
-
|
431
|
-
# http://elasticsearch-users.115913.n3.nabble.com/synonym-multi-words-search-td4030811.html
|
432
|
-
# I find the following approach effective if you are doing multi-word synonyms (synonym phrases):
|
433
|
-
# - Only apply the synonym expansion at index time
|
434
|
-
# - Don't have the synonym filter applied search
|
435
|
-
# - Use directional synonyms where appropriate. You want to make sure that you're not injecting terms that are too general.
|
436
|
-
settings[:analysis][:analyzer][default_analyzer][:filter].insert(4, "searchkick_synonym")
|
437
|
-
settings[:analysis][:analyzer][default_analyzer][:filter] << "searchkick_synonym"
|
438
|
-
|
439
|
-
%w(word_start word_middle word_end).each do |type|
|
440
|
-
settings[:analysis][:analyzer]["searchkick_#{type}_index".to_sym][:filter].insert(2, "searchkick_synonym")
|
441
|
-
end
|
442
|
-
end
|
443
|
-
|
444
|
-
if options[:wordnet]
|
445
|
-
settings[:analysis][:filter][:searchkick_wordnet] = {
|
446
|
-
type: "synonym",
|
447
|
-
format: "wordnet",
|
448
|
-
synonyms_path: Searchkick.wordnet_path
|
449
|
-
}
|
450
|
-
|
451
|
-
settings[:analysis][:analyzer][default_analyzer][:filter].insert(4, "searchkick_wordnet")
|
452
|
-
settings[:analysis][:analyzer][default_analyzer][:filter] << "searchkick_wordnet"
|
453
|
-
|
454
|
-
%w(word_start word_middle word_end).each do |type|
|
455
|
-
settings[:analysis][:analyzer]["searchkick_#{type}_index".to_sym][:filter].insert(2, "searchkick_wordnet")
|
456
|
-
end
|
457
|
-
end
|
458
|
-
|
459
|
-
if options[:special_characters] == false
|
460
|
-
settings[:analysis][:analyzer].each do |_, analyzer_settings|
|
461
|
-
analyzer_settings[:filter].reject! { |f| f == "asciifolding" }
|
462
|
-
end
|
463
|
-
end
|
464
|
-
|
465
|
-
mapping = {}
|
466
|
-
|
467
|
-
# conversions
|
468
|
-
Array(options[:conversions]).each do |conversions_field|
|
469
|
-
mapping[conversions_field] = {
|
470
|
-
type: "nested",
|
471
|
-
properties: {
|
472
|
-
query: {type: default_type, analyzer: "searchkick_keyword"},
|
473
|
-
count: {type: "integer"}
|
474
|
-
}
|
475
|
-
}
|
476
|
-
end
|
477
|
-
|
478
|
-
mapping_options = Hash[
|
479
|
-
[:autocomplete, :suggest, :word, :text_start, :text_middle, :text_end, :word_start, :word_middle, :word_end, :highlight, :searchable, :only_analyzed]
|
480
|
-
.map { |type| [type, (options[type] || []).map(&:to_s)] }
|
481
|
-
]
|
482
|
-
|
483
|
-
word = options[:word] != false && (!options[:match] || options[:match] == :word)
|
484
|
-
|
485
|
-
mapping_options.values.flatten.uniq.each do |field|
|
486
|
-
fields = {}
|
487
|
-
|
488
|
-
if mapping_options[:only_analyzed].include?(field)
|
489
|
-
fields[field] = {type: default_type, index: "no"}
|
490
|
-
else
|
491
|
-
fields[field] = keyword_mapping
|
492
|
-
end
|
493
|
-
|
494
|
-
if !options[:searchable] || mapping_options[:searchable].include?(field)
|
495
|
-
if word
|
496
|
-
fields["analyzed"] = {type: default_type, index: "analyzed", analyzer: default_analyzer}
|
497
|
-
|
498
|
-
if mapping_options[:highlight].include?(field)
|
499
|
-
fields["analyzed"][:term_vector] = "with_positions_offsets"
|
500
|
-
end
|
501
|
-
end
|
502
|
-
|
503
|
-
mapping_options.except(:highlight, :searchable, :only_analyzed).each do |type, f|
|
504
|
-
if options[:match] == type || f.include?(field)
|
505
|
-
fields[type] = {type: default_type, index: "analyzed", analyzer: "searchkick_#{type}_index"}
|
506
|
-
end
|
507
|
-
end
|
508
|
-
end
|
509
|
-
|
510
|
-
mapping[field] =
|
511
|
-
if below50
|
512
|
-
{
|
513
|
-
type: "multi_field",
|
514
|
-
fields: fields
|
515
|
-
}
|
516
|
-
elsif fields[field]
|
517
|
-
fields[field].merge(fields: fields.except(field))
|
518
|
-
end
|
519
|
-
end
|
520
|
-
|
521
|
-
(options[:locations] || []).map(&:to_s).each do |field|
|
522
|
-
mapping[field] = {
|
523
|
-
type: "geo_point"
|
524
|
-
}
|
525
|
-
end
|
526
|
-
|
527
|
-
(options[:unsearchable] || []).map(&:to_s).each do |field|
|
528
|
-
mapping[field] = {
|
529
|
-
type: default_type,
|
530
|
-
index: "no"
|
531
|
-
}
|
532
|
-
end
|
533
|
-
|
534
|
-
routing = {}
|
535
|
-
if options[:routing]
|
536
|
-
routing = {required: true}
|
537
|
-
unless options[:routing] == true
|
538
|
-
routing[:path] = options[:routing].to_s
|
539
|
-
end
|
540
|
-
end
|
541
|
-
|
542
|
-
dynamic_fields = {
|
543
|
-
# analyzed field must be the default field for include_in_all
|
544
|
-
# http://www.elasticsearch.org/guide/reference/mapping/multi-field-type/
|
545
|
-
# however, we can include the not_analyzed field in _all
|
546
|
-
# and the _all index analyzer will take care of it
|
547
|
-
"{name}" => keyword_mapping.merge(include_in_all: !options[:searchable])
|
548
|
-
}
|
549
|
-
|
550
|
-
dynamic_fields["{name}"][:ignore_above] = 256 unless below22
|
551
|
-
|
552
|
-
unless options[:searchable]
|
553
|
-
if options[:match] && options[:match] != :word
|
554
|
-
dynamic_fields[options[:match]] = {type: default_type, index: "analyzed", analyzer: "searchkick_#{options[:match]}_index"}
|
555
|
-
end
|
556
|
-
|
557
|
-
if word
|
558
|
-
dynamic_fields["analyzed"] = {type: default_type, index: "analyzed"}
|
559
|
-
end
|
560
|
-
end
|
561
|
-
|
562
|
-
# http://www.elasticsearch.org/guide/reference/mapping/multi-field-type/
|
563
|
-
multi_field =
|
564
|
-
if below50
|
565
|
-
{
|
566
|
-
type: "multi_field",
|
567
|
-
fields: dynamic_fields
|
568
|
-
}
|
569
|
-
else
|
570
|
-
dynamic_fields["{name}"].merge(fields: dynamic_fields.except("{name}"))
|
571
|
-
end
|
572
|
-
|
573
|
-
mappings = {
|
574
|
-
_default_: {
|
575
|
-
_all: {type: default_type, index: "analyzed", analyzer: default_analyzer},
|
576
|
-
properties: mapping,
|
577
|
-
_routing: routing,
|
578
|
-
# https://gist.github.com/kimchy/2898285
|
579
|
-
dynamic_templates: [
|
580
|
-
{
|
581
|
-
string_template: {
|
582
|
-
match: "*",
|
583
|
-
match_mapping_type: "string",
|
584
|
-
mapping: multi_field
|
585
|
-
}
|
586
|
-
}
|
587
|
-
]
|
588
|
-
}
|
589
|
-
}.deep_merge(options[:mappings] || {})
|
590
|
-
end
|
591
|
-
|
592
|
-
{
|
593
|
-
settings: settings,
|
594
|
-
mappings: mappings
|
595
|
-
}
|
596
|
-
end
|
597
|
-
|
598
251
|
# other
|
599
252
|
|
600
253
|
def tokens(text, options = {})
|
@@ -634,10 +287,10 @@ module Searchkick
|
|
634
287
|
|
635
288
|
# stringify fields
|
636
289
|
# remove _id since search_id is used instead
|
637
|
-
source = source.
|
290
|
+
source = source.each_with_object({}) { |(k, v), memo| memo[k.to_s] = v; memo }.except("_id")
|
638
291
|
|
639
292
|
# conversions
|
640
|
-
Array(options[:conversions]).each do |conversions_field|
|
293
|
+
Array(options[:conversions]).map(&:to_s).each do |conversions_field|
|
641
294
|
if source[conversions_field]
|
642
295
|
source[conversions_field] = source[conversions_field].map { |k, v| {query: k, count: v} }
|
643
296
|
end
|