searchkick 1.3.4 → 1.3.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
![Aggregations](https://raw.githubusercontent.com/ankane/searchkick/gh-pages/facets.png)
|
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
|