caoutsearch 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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