caoutsearch 0.0.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.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +43 -0
  4. data/lib/caoutsearch/config/client.rb +13 -0
  5. data/lib/caoutsearch/config/mappings.rb +40 -0
  6. data/lib/caoutsearch/config/settings.rb +29 -0
  7. data/lib/caoutsearch/filter/base.rb +101 -0
  8. data/lib/caoutsearch/filter/boolean.rb +19 -0
  9. data/lib/caoutsearch/filter/date.rb +49 -0
  10. data/lib/caoutsearch/filter/default.rb +51 -0
  11. data/lib/caoutsearch/filter/geo_point.rb +11 -0
  12. data/lib/caoutsearch/filter/match.rb +57 -0
  13. data/lib/caoutsearch/filter/none.rb +7 -0
  14. data/lib/caoutsearch/filter/range.rb +28 -0
  15. data/lib/caoutsearch/filter.rb +29 -0
  16. data/lib/caoutsearch/index/base.rb +35 -0
  17. data/lib/caoutsearch/index/document.rb +107 -0
  18. data/lib/caoutsearch/index/indice.rb +55 -0
  19. data/lib/caoutsearch/index/indice_versions.rb +123 -0
  20. data/lib/caoutsearch/index/instrumentation.rb +19 -0
  21. data/lib/caoutsearch/index/internal_dsl.rb +77 -0
  22. data/lib/caoutsearch/index/naming.rb +29 -0
  23. data/lib/caoutsearch/index/reindex.rb +77 -0
  24. data/lib/caoutsearch/index/scoping.rb +54 -0
  25. data/lib/caoutsearch/index/serialization.rb +136 -0
  26. data/lib/caoutsearch/index.rb +7 -0
  27. data/lib/caoutsearch/instrumentation/base.rb +69 -0
  28. data/lib/caoutsearch/instrumentation/index.rb +57 -0
  29. data/lib/caoutsearch/instrumentation/search.rb +41 -0
  30. data/lib/caoutsearch/mappings.rb +79 -0
  31. data/lib/caoutsearch/search/base.rb +27 -0
  32. data/lib/caoutsearch/search/dsl/item.rb +42 -0
  33. data/lib/caoutsearch/search/query/base.rb +16 -0
  34. data/lib/caoutsearch/search/query/boolean.rb +63 -0
  35. data/lib/caoutsearch/search/query/cleaning.rb +29 -0
  36. data/lib/caoutsearch/search/query/getters.rb +35 -0
  37. data/lib/caoutsearch/search/query/merge.rb +27 -0
  38. data/lib/caoutsearch/search/query/nested.rb +23 -0
  39. data/lib/caoutsearch/search/query/setters.rb +68 -0
  40. data/lib/caoutsearch/search/sanitizer.rb +28 -0
  41. data/lib/caoutsearch/search/search/delete_methods.rb +21 -0
  42. data/lib/caoutsearch/search/search/inspect.rb +36 -0
  43. data/lib/caoutsearch/search/search/instrumentation.rb +21 -0
  44. data/lib/caoutsearch/search/search/internal_dsl.rb +77 -0
  45. data/lib/caoutsearch/search/search/naming.rb +47 -0
  46. data/lib/caoutsearch/search/search/query_builder.rb +94 -0
  47. data/lib/caoutsearch/search/search/query_methods.rb +180 -0
  48. data/lib/caoutsearch/search/search/resettable.rb +35 -0
  49. data/lib/caoutsearch/search/search/response.rb +88 -0
  50. data/lib/caoutsearch/search/search/scroll_methods.rb +113 -0
  51. data/lib/caoutsearch/search/search/search_methods.rb +230 -0
  52. data/lib/caoutsearch/search/type_cast.rb +76 -0
  53. data/lib/caoutsearch/search/value.rb +111 -0
  54. data/lib/caoutsearch/search/value_overflow.rb +17 -0
  55. data/lib/caoutsearch/search.rb +6 -0
  56. data/lib/caoutsearch/settings.rb +22 -0
  57. data/lib/caoutsearch/version.rb +5 -0
  58. data/lib/caoutsearch.rb +38 -0
  59. metadata +268 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: bca0632fb965301b9c35bcd47ea7c8346cae1b49a22f41f6ccebd5fdd3181495
4
+ data.tar.gz: af08a8b38223708009694fa0025c6e83986b12db7e564009416a2cf8c2e303c8
5
+ SHA512:
6
+ metadata.gz: aef96ca4c7b5dfb867dcaf962eb619deb6091eb47dcbc4946ca3e940d6496af9c8e92d0bff3138039e920f27eaf447e735ec008c79b30e6a4eceadb9d66b13ce
7
+ data.tar.gz: 0fbc8e5451179f8066a7af0ac228d33639fe81e8e4f157edcf36923e89d73a778116717cc50e06f5d614844249da2993baabac0b99236978ff7373917e0f5735
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2022 Mon Territoire
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # Caoutsearch [\ˈkawt͡ˈsɝtʃ\\](http://ipa-reader.xyz/?text=ˈkawt͡ˈsɝtʃ)
2
+
3
+ ### Installation
4
+
5
+ ```bash
6
+ bundle add caoutsearch
7
+ ```
8
+
9
+ ### Configuration
10
+
11
+ <!-- TODO -->
12
+
13
+ ### Usage
14
+
15
+ <!-- TODO -->
16
+
17
+ ## Contributing
18
+
19
+ 1. Don't hesitate to submit your feature/idea/fix in [issues](https://github.com/mon-territoire/caoutsearch)
20
+ 2. Fork the [repository](https://github.com/mon-territoire/caoutsearch)
21
+ 3. Create your feature branch
22
+ 4. Ensure RSpec & Rubocop are passing
23
+ 4. Create a pull request
24
+
25
+ ### Tests & lint
26
+
27
+ ```bash
28
+ bundle exec rspec
29
+ bundle exec rubocop
30
+ ```
31
+
32
+ Both can be run with:
33
+
34
+ ```bash
35
+ bundle exec rake
36
+ ```
37
+
38
+ ## License & credits
39
+
40
+ Please see [LICENSE](https://github.com/mon-territoire/caoutsearch/blob/main/LICENSE) for further details.
41
+
42
+ Contributors: [./graphs/contributors](https://github.com/mon-territoire/caoutsearch/graphs/contributors)
43
+
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caoutsearch
4
+ module Config
5
+ module Client
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :client, default: Caoutsearch.client
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caoutsearch
4
+ module Config
5
+ module Mappings
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ delegate :mappings, to: :class
10
+ end
11
+
12
+ class_methods do
13
+ def mappings
14
+ @mappings ||= Caoutsearch::Mappings.new(default_mappings)
15
+ end
16
+
17
+ def mappings=(mappings)
18
+ @mappings = Caoutsearch::Mappings.new(mappings)
19
+ end
20
+
21
+ def remote_mappings
22
+ @remote_mappings ||= Caoutsearch::Mappings.new(get_remote_mappings)
23
+ end
24
+
25
+ protected
26
+
27
+ def default_mappings
28
+ path = ::Rails.root.join("config/elasticsearch/#{index_name}.json")
29
+ raise ArgumentError, "No mappings file found for #{index_name} at #{path}" unless path.exist?
30
+
31
+ JSON.parse(path.read)
32
+ end
33
+
34
+ def get_remote_mappings
35
+ client.indices.get_mapping(index: index_name).values[0]["mappings"]
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caoutsearch
4
+ module Config
5
+ module Settings
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ delegate :mappings, to: :class
10
+ end
11
+
12
+ class_methods do
13
+ def settings
14
+ @settings ||= Caoutsearch::Settings.new(default_settings)
15
+ end
16
+
17
+ def settings=(settings)
18
+ @settings = Caoutsearch::Settings.new(settings)
19
+ end
20
+
21
+ protected
22
+
23
+ def default_settings
24
+ Caoutsearch.settings.to_hash.dup
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caoutsearch
4
+ module Filter
5
+ class Base
6
+ attr_reader :key, :original_value, :type, :options
7
+
8
+ def initialize(key, original_value, type, options = {})
9
+ @key = key
10
+ @original_value = original_value
11
+ @type = type
12
+ @options = options
13
+ end
14
+
15
+ def value
16
+ @value = cast_value(original_value) unless defined?(@value)
17
+ @value
18
+ end
19
+
20
+ def as_json
21
+ filter = self.filter
22
+ filter = group_filters(filter) if filter.is_a?(Array)
23
+
24
+ if filter.present? && nested_query?
25
+ filter = {
26
+ nested: {
27
+ path: nested_path,
28
+ query: { bool: { filter: Array.wrap(filter) } }
29
+ }
30
+ }
31
+ end
32
+
33
+ filter
34
+ end
35
+
36
+ def filter
37
+ raise NotImplementedError
38
+ end
39
+
40
+ protected
41
+
42
+ def default_cast_type
43
+ type
44
+ end
45
+
46
+ def cast_value(value, type = default_cast_type)
47
+ Caoutsearch::Search::Value.new(
48
+ value,
49
+ type,
50
+ **options.slice(:null_values, :transform)
51
+ ).value
52
+ end
53
+
54
+ def original_values
55
+ case original_value
56
+ when String then original_value.split(",")
57
+ when Array then original_value
58
+ else Array.wrap(original_value)
59
+ end
60
+ end
61
+
62
+ def group_filters(terms)
63
+ groups = terms.select { |term| term.is_a?(Hash) && (term.key?(:term) || term.key?(:terms)) }
64
+ groups = groups.group_by { |term| (term[:term] || term[:terms]).keys[0] }
65
+
66
+ groups.each do |key, grouped_terms|
67
+ next if grouped_terms.size < 2
68
+
69
+ values = grouped_terms.flat_map { |term| (term[:term] || term[:terms])[key] }
70
+ values = values.uniq
71
+
72
+ grouped_terms.each { |term| terms.delete(term) }
73
+
74
+ terms << if values.size == 1
75
+ { term: { key => values[0] } }
76
+ else
77
+ { terms: { key => values } }
78
+ end
79
+ end
80
+
81
+ terms
82
+ end
83
+
84
+ def nested_query?
85
+ nested_path? && !include_in_parent?
86
+ end
87
+
88
+ def nested_path?
89
+ options[:nested] && key.to_s.include?(".")
90
+ end
91
+
92
+ def include_in_parent?
93
+ options[:include_in_parent]
94
+ end
95
+
96
+ def nested_path
97
+ options[:nested].is_a?(String) ? options[:nested] : key.to_s.split(".")[0].to_sym
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caoutsearch
4
+ module Filter
5
+ class Boolean < Base
6
+ def filter
7
+ return {} if value.nil?
8
+
9
+ { term: { key => value } }
10
+ end
11
+
12
+ protected
13
+
14
+ def default_cast_type
15
+ "boolean"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caoutsearch
4
+ module Filter
5
+ class Date < Base
6
+ def filter
7
+ original_values.map do |value|
8
+ case value
9
+ when true
10
+ { exists: { field: key } }
11
+ when false
12
+ { bool: { must_not: { exists: { field: key } } } }
13
+ when Hash
14
+ operator, value, unit = value.stringify_keys.values_at("operator", "value", "unit")
15
+
16
+ case operator
17
+ when "less_than"
18
+ { range: { key => { gte: cast_date(value, unit) } } }
19
+ when "greater_than"
20
+ { range: { key => { lt: cast_date(value, unit) } } }
21
+ when "between"
22
+ dates = value.map { |v| cast_date(v, unit) }.sort
23
+ { range: { key => { gte: dates[0], lt: dates[1] } } }
24
+ else
25
+ raise ArgumentError, "unknown operator #{operator.inspect} in #{value.inspect}"
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ def cast_date(value, unit)
32
+ if value.is_a?(Numeric) && unit
33
+ case unit
34
+ when "day" then value = value.days.ago
35
+ when "week" then value = value.weeks.ago
36
+ when "month" then value = value.months.ago
37
+ when "year", nil then value = value.years.ago
38
+ else
39
+ raise ArgumentError, "unknown unit #{unit.inspect} in #{value.inspect}"
40
+ end
41
+ elsif value.is_a?(ActiveSupport::Duration)
42
+ value = value.ago
43
+ end
44
+
45
+ cast_value(value, :date)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caoutsearch
4
+ module Filter
5
+ class Default < Base
6
+ def filter
7
+ if value.nil? && %w[text keyword].include?(type)
8
+ {
9
+ bool: {
10
+ should: [
11
+ { bool: { must_not: { exists: { field: key } } } },
12
+ { term: { key => "" } }
13
+ ]
14
+ }
15
+ }
16
+
17
+ elsif value.nil?
18
+ { bool: { must_not: { exists: { field: key } } } }
19
+
20
+ elsif value.is_a?(Array) && value.any?(&:nil?)
21
+ terms = []
22
+ terms << { bool: { must_not: { exists: { field: key } } } }
23
+
24
+ terms_values = value.compact
25
+ terms_values += [""] if %w[text keyword].include?(type)
26
+
27
+ if terms_values.size == 1
28
+ terms << { term: { key => terms_values[0] } }
29
+ elsif terms_values.size > 1
30
+ terms << { terms: { key => terms_values } }
31
+ end
32
+
33
+ if terms.size == 1
34
+ terms[0]
35
+ else
36
+ { bool: { should: terms } }
37
+ end
38
+
39
+ elsif value.is_a?(Array) && value.size == 1
40
+ { term: { key => value[0] } }
41
+
42
+ elsif value.is_a?(Array)
43
+ { terms: { key => value } }
44
+
45
+ else
46
+ { term: { key => value } }
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caoutsearch
4
+ module Filter
5
+ class GeoPoint < Base
6
+ def filter
7
+ { geo_distance: { :distance => "1mm", key => value } }
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caoutsearch
4
+ module Filter
5
+ class Match < Base
6
+ def filter
7
+ if use_query_string?
8
+ {
9
+ query_string: {
10
+ query: sanitized_for_query_string,
11
+ default_field: key,
12
+ default_operator: "and",
13
+ analyze_wildcard: true
14
+ }
15
+ }
16
+ elsif multiple_words?
17
+ { match: { key => { query: value, operator: "and" } } }
18
+ else
19
+ { match: { key => value } }
20
+ end
21
+ end
22
+
23
+ def nested_query?
24
+ nested_path? && (multiple_words? || !include_in_parent?)
25
+ end
26
+
27
+ def multiple_words?
28
+ value.is_a?(String) && value.squish.include?(" ")
29
+ end
30
+
31
+ # https://rubular.com/r/KEA7poAaIeNrZe
32
+ QUERY_STRING_REGEXP = %r{
33
+ (?:[*?][^\s*?]|[^\s*?][*?]|(?:^|\s)(?:AND|OR|NOT)(?:$|\s)|^\*$)
34
+ }x
35
+
36
+ # https://rubular.com/r/tVMSviF0a74e1s
37
+ STRIPPED_OPERATOR_REGEXP = %r{
38
+ (?:^\s*(?:AND|OR)\*$|^\s*(?:AND|OR)\s+|\s+(?:AND|OR)\s*$)
39
+ }x
40
+
41
+ def use_query_string?
42
+ QUERY_STRING_REGEXP.match?(value)
43
+ end
44
+
45
+ def sanitized_for_query_string
46
+ # Do not allow setting fields in query string
47
+ clean_value = Caoutsearch::Search::Sanitizer.sanitize(value, ":")
48
+
49
+ # Delete leading and trailing operators
50
+ # Example : " OR ANGE" => "ANGE"
51
+ # Example : "MASSE OR " => "MASSE"
52
+
53
+ clean_value.gsub(STRIPPED_OPERATOR_REGEXP, "")
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caoutsearch
4
+ module Filter
5
+ NONE = { term: { 0 => 1 } }.freeze
6
+ end
7
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caoutsearch
4
+ module Filter
5
+ class Range < Base
6
+ def filter
7
+ original_values.map do |value|
8
+ case value
9
+ when />(.+)/ then { range: { key => { gt: cast_value_with_overflow($1, :lower) } } }
10
+ when /<(.+)/ then { range: { key => { lt: cast_value_with_overflow($1, :upper) } } }
11
+ when /≥(.+)/ then { range: { key => { gte: cast_value_with_overflow($1, :lower) } } }
12
+ when /≤(.+)/ then { range: { key => { lte: cast_value_with_overflow($1, :upper) } } }
13
+ when /(.+)-(.+)/ then { range: { key => { gte: cast_value_with_overflow($1, :lower), lte: cast_value_with_overflow($2, :upper) } } }
14
+ else { term: { key => cast_value(value) } }
15
+ end
16
+ end
17
+ end
18
+
19
+ def cast_value_with_overflow(value, type)
20
+ cast_value(value)
21
+ rescue Caoutsearch::Search::ValueOverflow => e
22
+ raise(e) unless type == e.type
23
+
24
+ e.limit
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caoutsearch
4
+ module Filter
5
+ class << self
6
+ def filters
7
+ @filters ||= {}
8
+ end
9
+
10
+ def [](key)
11
+ @filters[key.to_s]
12
+ end
13
+
14
+ def register(filter_class, as: nil)
15
+ as ||= filter_class.name.demodulize.underscore
16
+
17
+ @filters ||= {}
18
+ @filters[as] = filter_class
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ Caoutsearch::Filter.register(Caoutsearch::Filter::Boolean)
25
+ Caoutsearch::Filter.register(Caoutsearch::Filter::Date)
26
+ Caoutsearch::Filter.register(Caoutsearch::Filter::Default)
27
+ Caoutsearch::Filter.register(Caoutsearch::Filter::GeoPoint)
28
+ Caoutsearch::Filter.register(Caoutsearch::Filter::Match)
29
+ Caoutsearch::Filter.register(Caoutsearch::Filter::Range)
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caoutsearch
4
+ module Index
5
+ class Base
6
+ include Caoutsearch::Config::Client
7
+ include Caoutsearch::Config::Mappings
8
+ include Caoutsearch::Config::Settings
9
+
10
+ include Caoutsearch::Index::Document
11
+ include Caoutsearch::Index::Indice
12
+ include Caoutsearch::Index::IndiceVersions
13
+ include Caoutsearch::Index::Instrumentation
14
+ include Caoutsearch::Index::InternalDSL
15
+ include Caoutsearch::Index::Naming
16
+ include Caoutsearch::Index::Reindex
17
+ include Caoutsearch::Index::Scoping
18
+ include Caoutsearch::Index::Serialization
19
+
20
+ attr_reader :record
21
+
22
+ delegate_missing_to :record
23
+
24
+ def initialize(record)
25
+ @record = record
26
+ end
27
+
28
+ class << self
29
+ def wrap(*records)
30
+ Array.wrap(*records).map { |record| new(record) }
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caoutsearch
4
+ module Index
5
+ module Document
6
+ extend ActiveSupport::Concern
7
+
8
+ # Return the indexed document
9
+ #
10
+ # record = Article.find(1)
11
+ # ArticleIndex.new(record).indexed_document
12
+ #
13
+ def indexed_document
14
+ request_payload = {
15
+ index: index_name,
16
+ id: record.id
17
+ }
18
+
19
+ response = instrument(:get) do |event_payload|
20
+ event_payload[:request] = request_payload
21
+ event_payload[:response] = client.get(request_payload)
22
+ end
23
+
24
+ response.body
25
+ end
26
+
27
+ # Overwrite or partially update a document
28
+ #
29
+ # record = Article.find(1)
30
+ # ArticleIndex.new(record).update_document
31
+ # ArticleIndex.new(record).update_document(:title, :content)
32
+ #
33
+ def update_document(*keys, index: index_name, refresh: false)
34
+ request_payload = {
35
+ index: index,
36
+ id: id
37
+ }
38
+
39
+ if keys.empty?
40
+ request_payload[:body] = as_json
41
+
42
+ instrument(:index) do |event_payload|
43
+ event_payload[:request] = request_payload
44
+ event_payload[:response] = client.index(request_payload)
45
+ end
46
+ else
47
+ request_payload[:body] = bulkify(:update, keys)
48
+
49
+ instrument(:bulk, method: :update) do |event_payload|
50
+ event_payload[:request] = request_payload
51
+ event_payload[:response] = client.bulk(request_payload)
52
+ end
53
+ end
54
+
55
+ refresh_indice(index: index) if refresh
56
+ end
57
+
58
+ # Delete the document
59
+ #
60
+ # record = Article.find(1)
61
+ # ArticleIndex.new(record).delete_document
62
+ #
63
+ def delete_document(index: index_name, refresh: false)
64
+ self.class.delete_document(record.id, index: index, refresh: refresh)
65
+ end
66
+
67
+ class_methods do
68
+ # Delete one document
69
+ #
70
+ # ArticleIndex.delete_document(1)
71
+ #
72
+ def delete_document(id, index: index_name, refresh: false)
73
+ request_payload = {
74
+ index: index,
75
+ id: id,
76
+ ignore: 404
77
+ }
78
+
79
+ instrument(:delete) do |event_payload|
80
+ event_payload[:request] = request_payload
81
+ event_payload[:response] = client.delete(request_payload)
82
+ end
83
+
84
+ refresh_indice(index: index) if refresh
85
+ end
86
+
87
+ # Delete many documents, using bulk API
88
+ #
89
+ # ArticleIndex.delete_documents([1, 2, 3])
90
+ #
91
+ def delete_documents(ids, index: index_name, refresh: false)
92
+ request_payload = {
93
+ index: index,
94
+ body: ids.map { |id| { delete: { _id: id } } }
95
+ }
96
+
97
+ instrument(:bulk, method: :delete) do |event_payload|
98
+ event_payload[:request] = request_payload
99
+ event_payload[:response] = client.bulk(request_payload)
100
+ end
101
+
102
+ refresh_indice(index: index) if refresh
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end