elasticsearch-model 5.1.0 → 6.1.1

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 (127) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -3
  3. data/Gemfile +5 -0
  4. data/README.md +18 -11
  5. data/Rakefile +27 -36
  6. data/elasticsearch-model.gemspec +6 -6
  7. data/examples/activerecord_custom_analyzer.rb +135 -0
  8. data/examples/activerecord_mapping_completion.rb +3 -18
  9. data/examples/datamapper_article.rb +11 -1
  10. data/gemfiles/3.0.gemfile +6 -1
  11. data/gemfiles/4.0.gemfile +8 -1
  12. data/gemfiles/5.0.gemfile +7 -1
  13. data/gemfiles/6.0.gemfile +18 -0
  14. data/lib/elasticsearch/model/adapters/active_record.rb +8 -24
  15. data/lib/elasticsearch/model/adapters/mongoid.rb +10 -3
  16. data/lib/elasticsearch/model/importing.rb +1 -1
  17. data/lib/elasticsearch/model/indexing.rb +6 -4
  18. data/lib/elasticsearch/model/naming.rb +9 -2
  19. data/lib/elasticsearch/model/response/aggregations.rb +1 -1
  20. data/lib/elasticsearch/model/response/base.rb +3 -2
  21. data/lib/elasticsearch/model/response/pagination/kaminari.rb +109 -0
  22. data/lib/elasticsearch/model/response/pagination/will_paginate.rb +95 -0
  23. data/lib/elasticsearch/model/response/pagination.rb +2 -192
  24. data/lib/elasticsearch/model/response/result.rb +1 -1
  25. data/lib/elasticsearch/model/response/suggestions.rb +1 -1
  26. data/lib/elasticsearch/model/response.rb +11 -10
  27. data/lib/elasticsearch/model/version.rb +1 -1
  28. data/lib/elasticsearch/model.rb +15 -8
  29. data/spec/elasticsearch/model/adapter_spec.rb +119 -0
  30. data/spec/elasticsearch/model/adapters/active_record/associations_spec.rb +334 -0
  31. data/spec/elasticsearch/model/adapters/active_record/basic_spec.rb +340 -0
  32. data/spec/elasticsearch/model/adapters/active_record/dynamic_index_name_spec.rb +18 -0
  33. data/spec/elasticsearch/model/adapters/active_record/import_spec.rb +187 -0
  34. data/spec/elasticsearch/model/adapters/active_record/multi_model_spec.rb +110 -0
  35. data/spec/elasticsearch/model/adapters/active_record/namespaced_model_spec.rb +38 -0
  36. data/spec/elasticsearch/model/adapters/active_record/pagination_spec.rb +315 -0
  37. data/spec/elasticsearch/model/adapters/active_record/parent_child_spec.rb +75 -0
  38. data/spec/elasticsearch/model/adapters/active_record/serialization_spec.rb +61 -0
  39. data/spec/elasticsearch/model/adapters/active_record_spec.rb +207 -0
  40. data/spec/elasticsearch/model/adapters/default_spec.rb +41 -0
  41. data/spec/elasticsearch/model/adapters/mongoid/basic_spec.rb +267 -0
  42. data/spec/elasticsearch/model/adapters/mongoid/multi_model_spec.rb +66 -0
  43. data/spec/elasticsearch/model/adapters/mongoid_spec.rb +235 -0
  44. data/spec/elasticsearch/model/adapters/multiple_spec.rb +125 -0
  45. data/spec/elasticsearch/model/callbacks_spec.rb +33 -0
  46. data/spec/elasticsearch/model/client_spec.rb +66 -0
  47. data/spec/elasticsearch/model/hash_wrapper_spec.rb +12 -0
  48. data/spec/elasticsearch/model/importing_spec.rb +214 -0
  49. data/spec/elasticsearch/model/indexing_spec.rb +918 -0
  50. data/spec/elasticsearch/model/module_spec.rb +101 -0
  51. data/spec/elasticsearch/model/multimodel_spec.rb +55 -0
  52. data/spec/elasticsearch/model/naming_inheritance_spec.rb +184 -0
  53. data/spec/elasticsearch/model/naming_spec.rb +186 -0
  54. data/spec/elasticsearch/model/proxy_spec.rb +107 -0
  55. data/spec/elasticsearch/model/response/aggregations_spec.rb +66 -0
  56. data/spec/elasticsearch/model/response/base_spec.rb +90 -0
  57. data/spec/elasticsearch/model/response/pagination/kaminari_spec.rb +410 -0
  58. data/spec/elasticsearch/model/response/pagination/will_paginate_spec.rb +262 -0
  59. data/spec/elasticsearch/model/response/records_spec.rb +118 -0
  60. data/spec/elasticsearch/model/response/response_spec.rb +131 -0
  61. data/spec/elasticsearch/model/response/result_spec.rb +122 -0
  62. data/spec/elasticsearch/model/response/results_spec.rb +56 -0
  63. data/spec/elasticsearch/model/searching_search_request_spec.rb +112 -0
  64. data/spec/elasticsearch/model/searching_spec.rb +49 -0
  65. data/spec/elasticsearch/model/serializing_spec.rb +22 -0
  66. data/spec/spec_helper.rb +161 -0
  67. data/spec/support/app/answer.rb +33 -0
  68. data/spec/support/app/article.rb +22 -0
  69. data/spec/support/app/article_for_pagination.rb +12 -0
  70. data/spec/support/app/article_with_custom_serialization.rb +13 -0
  71. data/spec/support/app/article_with_dynamic_index_name.rb +15 -0
  72. data/spec/support/app/author.rb +9 -0
  73. data/spec/support/app/authorship.rb +4 -0
  74. data/spec/support/app/category.rb +3 -0
  75. data/spec/support/app/comment.rb +3 -0
  76. data/spec/support/app/episode.rb +11 -0
  77. data/spec/support/app/image.rb +19 -0
  78. data/spec/support/app/import_article.rb +12 -0
  79. data/spec/support/app/mongoid_article.rb +21 -0
  80. data/spec/support/app/namespaced_book.rb +10 -0
  81. data/spec/support/app/parent_and_child_searchable.rb +24 -0
  82. data/spec/support/app/post.rb +14 -0
  83. data/spec/support/app/question.rb +27 -0
  84. data/spec/support/app/searchable.rb +48 -0
  85. data/spec/support/app/series.rb +11 -0
  86. data/spec/support/app.rb +21 -0
  87. data/spec/support/model.json +1 -0
  88. data/{test → spec}/support/model.yml +0 -0
  89. metadata +134 -89
  90. data/test/integration/active_record_associations_parent_child_test.rb +0 -147
  91. data/test/integration/active_record_associations_test.rb +0 -339
  92. data/test/integration/active_record_basic_test.rb +0 -251
  93. data/test/integration/active_record_custom_serialization_test.rb +0 -67
  94. data/test/integration/active_record_import_test.rb +0 -115
  95. data/test/integration/active_record_namespaced_model_test.rb +0 -54
  96. data/test/integration/active_record_pagination_test.rb +0 -149
  97. data/test/integration/dynamic_index_name_test.rb +0 -52
  98. data/test/integration/mongoid_basic_test.rb +0 -176
  99. data/test/integration/multiple_models_test.rb +0 -176
  100. data/test/support/model.json +0 -1
  101. data/test/test_helper.rb +0 -92
  102. data/test/unit/adapter_active_record_test.rb +0 -157
  103. data/test/unit/adapter_default_test.rb +0 -41
  104. data/test/unit/adapter_mongoid_test.rb +0 -104
  105. data/test/unit/adapter_multiple_test.rb +0 -106
  106. data/test/unit/adapter_test.rb +0 -69
  107. data/test/unit/callbacks_test.rb +0 -31
  108. data/test/unit/client_test.rb +0 -27
  109. data/test/unit/hash_wrapper_test.rb +0 -13
  110. data/test/unit/importing_test.rb +0 -203
  111. data/test/unit/indexing_test.rb +0 -687
  112. data/test/unit/module_test.rb +0 -68
  113. data/test/unit/multimodel_test.rb +0 -38
  114. data/test/unit/naming_inheritance_test.rb +0 -94
  115. data/test/unit/naming_test.rb +0 -103
  116. data/test/unit/proxy_test.rb +0 -98
  117. data/test/unit/response_aggregations_test.rb +0 -46
  118. data/test/unit/response_base_test.rb +0 -40
  119. data/test/unit/response_pagination_kaminari_test.rb +0 -433
  120. data/test/unit/response_pagination_will_paginate_test.rb +0 -398
  121. data/test/unit/response_records_test.rb +0 -91
  122. data/test/unit/response_result_test.rb +0 -90
  123. data/test/unit/response_results_test.rb +0 -31
  124. data/test/unit/response_test.rb +0 -104
  125. data/test/unit/searching_search_request_test.rb +0 -78
  126. data/test/unit/searching_test.rb +0 -41
  127. data/test/unit/serializing_test.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d3f9a1aed30a6f21b7c51f5f73ab01e9f00ea7b3d2d3d6b2203b5ba922e6dd63
4
- data.tar.gz: 65bcb7ba51e908ada809d47d9aea04d2c78091182061b2d83488735696c25c93
3
+ metadata.gz: 75f65b56d90c490e0f107c9f57883a91eedc3c1c25793951df233872892f1a01
4
+ data.tar.gz: fe2cc84bbeb796de9b88f11f741f4822d6c3a4999bbaff5a54f40a339ff918a5
5
5
  SHA512:
6
- metadata.gz: 81f261b7d86e68d6b23e811a5053bb300982d4536ac3a7026850b098f08237e46a51c847cc2d12736fee7544a3984cae45a0ebef034a6cb38dd41c3bc96c8b14
7
- data.tar.gz: e53472c07599acae594fdcdc19405eb4f03cbfe77ee242429d539dc09cce5b03011d3e74f2b777642e0018895ba0e57551f385d37dbb20c6ce62877e8393819f
6
+ metadata.gz: a458e5ad312c67db37c7ff678c4bbfc19570202d72534a089a30818825fae380e06a40a419ec2f854838f0e924315a2d67f46ca9f966da91a264339c91f2be9f
7
+ data.tar.gz: dba1a2b0c88ecaca8a5b408f4f6ae8757812ad522320ecfd1f823e679398c6e1c042bb35deec5a6cbe38b9043e6e50605558f07cd0c2d33fd9e4642e5786478e
data/.gitignore CHANGED
@@ -16,6 +16,4 @@ test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
18
 
19
- gemfiles/3.0.gemfile.lock
20
- gemfiles/4.0.gemfile.lock
21
- gemfiles/5.0.gemfile.lock
19
+ gemfiles/*.gemfile.lock
data/Gemfile CHANGED
@@ -2,3 +2,8 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in elasticsearch-model.gemspec
4
4
  gemspec
5
+
6
+ group :development, :testing do
7
+ gem 'rspec'
8
+ gem 'pry-nav'
9
+ end
data/README.md CHANGED
@@ -19,6 +19,7 @@ is compatible with the Elasticsearch `master` branch, therefore, with the next m
19
19
  | 0.1 | → | 1.x |
20
20
  | 2.x | → | 2.x |
21
21
  | 5.x | → | 5.x |
22
+ | 6.x | → | 6.x |
22
23
  | master | → | master |
23
24
 
24
25
  ## Installation
@@ -321,7 +322,7 @@ Elasticsearch::Model::Response::Response.__send__ :include, Elasticsearch::Model
321
322
 
322
323
  #### The Elasticsearch DSL
323
324
 
324
- In most situation, you'll want to pass the search definition
325
+ In most situations, you'll want to pass the search definition
325
326
  in the Elasticsearch [domain-specific language](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl.html) to the client:
326
327
 
327
328
  ```ruby
@@ -425,7 +426,7 @@ Article.__elasticsearch__.refresh_index!
425
426
  ```
426
427
 
427
428
  By default, index name and document type will be inferred from your class name,
428
- you can set it explicitely, however:
429
+ you can set it explicitly, however:
429
430
 
430
431
  ```ruby
431
432
  class Article
@@ -534,7 +535,7 @@ class Indexer
534
535
  case operation.to_s
535
536
  when /index/
536
537
  record = Article.find(record_id)
537
- Client.index index: 'articles', type: 'article', id: record.id, body: record.as_indexed_json
538
+ Client.index index: 'articles', type: 'article', id: record.id, body: record.__elasticsearch__.as_indexed_json
538
539
  when /delete/
539
540
  Client.delete index: 'articles', type: 'article', id: record_id
540
541
  else raise ArgumentError, "Unknown operation '#{operation}'"
@@ -674,7 +675,7 @@ module DataMapperAdapter
674
675
  #
675
676
  module Records
676
677
  def records
677
- klass.all(id: @ids)
678
+ klass.all(id: ids)
678
679
  end
679
680
 
680
681
  # ...
@@ -724,13 +725,8 @@ module and its submodules for technical information.
724
725
 
725
726
  The module provides a common `settings` method to customize various features.
726
727
 
727
- At the moment, the only supported setting is `:inheritance_enabled`, which makes the class receiving the module
728
- respect index names and document types of a super-class, eg. in case you're using "single table inheritance" (STI)
729
- in Rails:
730
-
731
- ```ruby
732
- Elasticsearch::Model.settings[:inheritance_enabled] = true
733
- ```
728
+ Before version 7.0.0 of the gem, the only supported setting was `:inheritance_enabled`. This setting has been deprecated
729
+ and removed.
734
730
 
735
731
  ## Development and Community
736
732
 
@@ -748,6 +744,17 @@ curl -# https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticse
748
744
  SERVER=start TEST_CLUSTER_COMMAND=$PWD/tmp/elasticsearch-1.0.0.RC1/bin/elasticsearch bundle exec rake test:all
749
745
  ```
750
746
 
747
+ ### Single Table Inheritance deprecation
748
+
749
+ `Single Table Inheritance` has been supported through the 6.x series of this gem. With this feature,
750
+ elasticsearch settings (index mappings, etc) on a parent model could be inherited by a child model leading to different
751
+ model documents being indexed into the same Elasticsearch index. This feature depended on the ability to set a `type`
752
+ for a document in Elasticsearch. The Elasticsearch team has deprecated support for `types`, as is described
753
+ [here.](https://www.elastic.co/guide/en/elasticsearch/reference/current/removal-of-types.html)
754
+ This gem will also remove support for types and `Single Table Inheritance` in version 7.0 as it enables an anti-pattern.
755
+ Please save different model documents in separate indices. If you want to use STI, you can include an artificial
756
+ `type` field manually in each document and use it in other operations.
757
+
751
758
  ## License
752
759
 
753
760
  This software is licensed under the Apache 2 license, quoted below.
data/Rakefile CHANGED
@@ -4,12 +4,25 @@ desc "Run unit tests"
4
4
  task :default => 'test:unit'
5
5
  task :test => 'test:unit'
6
6
 
7
- namespace :bundler do
8
- desc "Install dependencies for all the Gemfiles"
7
+ if RUBY_VERSION < '2.3'
8
+ GEMFILES = ['3.0.gemfile', '4.0.gemfile', '5.0.gemfile']
9
+ else
10
+ GEMFILES = ['4.0.gemfile', '5.0.gemfile']
11
+ end
12
+
13
+ namespace :bundle do
14
+ desc 'Install dependencies for all the Gemfiles in /gemfiles. Optionally define env variable RAILS_VERSIONS. E.g. RAILS_VERSIONS=3.0,5.0'
9
15
  task :install do
10
- sh "BUNDLE_GEMFILE='#{File.expand_path('../gemfiles/3.0.gemfile', __FILE__)}' bundle install"
11
- sh "BUNDLE_GEMFILE='#{File.expand_path('../gemfiles/4.0.gemfile', __FILE__)}' bundle install"
12
- sh "BUNDLE_GEMFILE='#{File.expand_path('../gemfiles/5.0.gemfile', __FILE__)}' bundle install"
16
+ unless defined?(JRUBY_VERSION)
17
+ puts '-'*80
18
+ gemfiles = ENV['RAILS_VERSIONS'] ? ENV['RAILS_VERSIONS'].split(',').map { |v| "#{v}.gemfile"} : GEMFILES
19
+ gemfiles.each do |gemfile|
20
+ Bundler.with_clean_env do
21
+ sh "bundle install --gemfile #{File.expand_path('../gemfiles/'+gemfile, __FILE__)}"
22
+ end
23
+ puts '-'*80
24
+ end
25
+ end
13
26
  end
14
27
  end
15
28
 
@@ -17,38 +30,16 @@ end
17
30
 
18
31
  require 'rake/testtask'
19
32
  namespace :test do
20
- Rake::TestTask.new(:run_unit) do |test|
21
- test.libs << 'lib' << 'test'
22
- test.test_files = FileList["test/unit/**/*_test.rb"]
23
- test.verbose = false
24
- test.warning = false
25
- end
26
-
27
- Rake::TestTask.new(:run_integration) do |test|
28
- test.libs << 'lib' << 'test'
29
- test.test_files = FileList["test/integration/**/*_test.rb"]
30
- test.verbose = false
31
- test.warning = false
32
- end
33
-
34
- desc "Run unit tests against ActiveModel 3, 4 and 5"
35
- task :unit do
36
- sh "BUNDLE_GEMFILE='#{File.expand_path('../gemfiles/3.0.gemfile', __FILE__)}' bundle exec rake test:run_unit"
37
- sh "BUNDLE_GEMFILE='#{File.expand_path('../gemfiles/4.0.gemfile', __FILE__)}' bundle exec rake test:run_unit"
38
- sh "BUNDLE_GEMFILE='#{File.expand_path('../gemfiles/5.0.gemfile', __FILE__)}' bundle exec rake test:run_unit"
39
- end
40
-
41
- desc "Run integration tests against latest stable ActiveModel (5)"
42
- task :integration do
43
- #sh "BUNDLE_GEMFILE='#{File.expand_path('../gemfiles/3.0.gemfile', __FILE__)}' bundle exec rake test:run_integration"
44
- #sh "BUNDLE_GEMFILE='#{File.expand_path('../gemfiles/4.0.gemfile', __FILE__)}' bundle exec rake test:run_integration"
45
- sh "BUNDLE_GEMFILE='#{File.expand_path('../gemfiles/5.0.gemfile', __FILE__)}' bundle exec rake test:run_integration"
46
- end
47
33
 
48
- desc "Run unit and integration tests"
49
- task :all do
50
- Rake::Task['test:unit'].invoke
51
- Rake::Task['test:integration'].invoke
34
+ desc 'Run all tests. Optionally define env variable RAILS_VERSIONS. E.g. RAILS_VERSIONS=3.0,5.0'
35
+ task :all, [:rails_versions] do |task, args|
36
+ gemfiles = ENV['RAILS_VERSIONS'] ? ENV['RAILS_VERSIONS'].split(',').map {|v| "#{v}.gemfile"} : GEMFILES
37
+ puts '-' * 80
38
+ gemfiles.each do |gemfile|
39
+ sh "BUNDLE_GEMFILE='#{File.expand_path("../gemfiles/#{gemfile}", __FILE__)}' " +
40
+ " bundle exec rspec"
41
+ puts '-' * 80
42
+ end
52
43
  end
53
44
  end
54
45
 
@@ -21,21 +21,21 @@ Gem::Specification.new do |s|
21
21
  s.extra_rdoc_files = [ "README.md", "LICENSE.txt" ]
22
22
  s.rdoc_options = [ "--charset=UTF-8" ]
23
23
 
24
- s.required_ruby_version = ">= 1.9.3"
24
+ s.required_ruby_version = ">= 2.2"
25
25
 
26
- s.add_dependency "elasticsearch", '~> 5'
26
+ s.add_dependency "elasticsearch", '~> 6'
27
27
  s.add_dependency "activesupport", '> 3'
28
28
  s.add_dependency "hashie"
29
29
 
30
- s.add_development_dependency "bundler", "~> 1.3"
30
+ s.add_development_dependency "bundler"
31
31
  s.add_development_dependency "rake", "~> 11.1"
32
32
 
33
33
  s.add_development_dependency "elasticsearch-extensions"
34
34
 
35
- s.add_development_dependency "sqlite3"
35
+ s.add_development_dependency "sqlite3" unless defined?(JRUBY_VERSION)
36
36
  s.add_development_dependency "activemodel", "> 3"
37
37
 
38
- s.add_development_dependency "oj"
38
+ s.add_development_dependency "oj" unless defined?(JRUBY_VERSION)
39
39
  s.add_development_dependency "kaminari"
40
40
  s.add_development_dependency "will_paginate"
41
41
 
@@ -45,7 +45,7 @@ Gem::Specification.new do |s|
45
45
  s.add_development_dependency "mocha"
46
46
  s.add_development_dependency "turn"
47
47
  s.add_development_dependency "yard"
48
- s.add_development_dependency "ruby-prof"
48
+ s.add_development_dependency "ruby-prof" unless defined?(JRUBY_VERSION)
49
49
  s.add_development_dependency "pry"
50
50
 
51
51
  s.add_development_dependency "simplecov"
@@ -0,0 +1,135 @@
1
+ # Custom Analyzer for ActiveRecord integration with Elasticsearch
2
+ # ===============================================================
3
+
4
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
5
+
6
+ require 'ansi'
7
+ require 'logger'
8
+
9
+ require 'active_record'
10
+ require 'elasticsearch/model'
11
+
12
+ ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT)
13
+ ActiveRecord::Base.establish_connection( adapter: 'sqlite3', database: ":memory:" )
14
+
15
+ ActiveRecord::Schema.define(version: 1) do
16
+ create_table :articles do |t|
17
+ t.string :title
18
+ t.date :published_at
19
+ t.timestamps
20
+ end
21
+ end
22
+
23
+ Elasticsearch::Model.client.transport.logger = ActiveSupport::Logger.new(STDOUT)
24
+ Elasticsearch::Model.client.transport.logger.formatter = lambda { |s, d, p, m| "#{m.ansi(:faint)}\n" }
25
+
26
+ class Article < ActiveRecord::Base
27
+ include Elasticsearch::Model
28
+
29
+ settings index: {
30
+ number_of_shards: 1,
31
+ number_of_replicas: 0,
32
+ analysis: {
33
+ analyzer: {
34
+ pattern: {
35
+ type: 'pattern',
36
+ pattern: "\\s|_|-|\\.",
37
+ lowercase: true
38
+ },
39
+ trigram: {
40
+ tokenizer: 'trigram'
41
+ }
42
+ },
43
+ tokenizer: {
44
+ trigram: {
45
+ type: 'ngram',
46
+ min_gram: 3,
47
+ max_gram: 3,
48
+ token_chars: ['letter', 'digit']
49
+ }
50
+ }
51
+ } } do
52
+ mapping do
53
+ indexes :title, type: 'text', analyzer: 'english' do
54
+ indexes :keyword, analyzer: 'keyword'
55
+ indexes :pattern, analyzer: 'pattern'
56
+ indexes :trigram, analyzer: 'trigram'
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ # Create example records
63
+ #
64
+ Article.delete_all
65
+ Article.create title: 'Foo'
66
+ Article.create title: 'Foo-Bar'
67
+ Article.create title: 'Foo_Bar_Bazooka'
68
+ Article.create title: 'Foo.Bar'
69
+
70
+ # Index records
71
+ #
72
+ errors = Article.import force: true, refresh: true, return: 'errors'
73
+ puts "[!] Errors importing records: #{errors.map { |d| d['index']['error'] }.join(', ')}".ansi(:red) && exit(1) unless errors.empty?
74
+
75
+ puts '', '-'*80
76
+
77
+ puts "English analyzer [Foo_Bar_1_Bazooka]".ansi(:bold),
78
+ "Tokens: " +
79
+ Article.__elasticsearch__.client.indices
80
+ .analyze(index: Article.index_name, body: { field: 'title', text: 'Foo_Bar_1_Bazooka' })['tokens']
81
+ .map { |d| "[#{d['token']}]" }.join(' '),
82
+ "\n"
83
+
84
+ puts "Keyword analyzer [Foo_Bar_1_Bazooka]".ansi(:bold),
85
+ "Tokens: " +
86
+ Article.__elasticsearch__.client.indices
87
+ .analyze(index: Article.index_name, body: { field: 'title.keyword', text: 'Foo_Bar_1_Bazooka' })['tokens']
88
+ .map { |d| "[#{d['token']}]" }.join(' '),
89
+ "\n"
90
+
91
+ puts "Pattern analyzer [Foo_Bar_1_Bazooka]".ansi(:bold),
92
+ "Tokens: " +
93
+ Article.__elasticsearch__.client.indices
94
+ .analyze(index: Article.index_name, body: { field: 'title.pattern', text: 'Foo_Bar_1_Bazooka' })['tokens']
95
+ .map { |d| "[#{d['token']}]" }.join(' '),
96
+ "\n"
97
+
98
+ puts "Trigram analyzer [Foo_Bar_1_Bazooka]".ansi(:bold),
99
+ "Tokens: " +
100
+ Article.__elasticsearch__.client.indices
101
+ .analyze(index: Article.index_name, body: { field: 'title.trigram', text: 'Foo_Bar_1_Bazooka' })['tokens']
102
+ .map { |d| "[#{d['token']}]" }.join(' '),
103
+ "\n"
104
+
105
+ puts '', '-'*80
106
+
107
+ response = Article.search query: { match: { 'title' => 'foo' } } ;
108
+
109
+ puts "English search for 'foo'".ansi(:bold),
110
+ "#{response.response.hits.total} matches: " +
111
+ response.records.map { |d| d.title }.join(', '),
112
+ "\n"
113
+
114
+ puts '', '-'*80
115
+
116
+ response = Article.search query: { match: { 'title.pattern' => 'foo' } } ;
117
+
118
+ puts "Pattern search for 'foo'".ansi(:bold),
119
+ "#{response.response.hits.total} matches: " +
120
+ response.records.map { |d| d.title }.join(', '),
121
+ "\n"
122
+
123
+ puts '', '-'*80
124
+
125
+ response = Article.search query: { match: { 'title.trigram' => 'zoo' } } ;
126
+
127
+ puts "Trigram search for 'zoo'".ansi(:bold),
128
+ "#{response.response.hits.total} matches: " +
129
+ response.records.map { |d| d.title }.join(', '),
130
+ "\n"
131
+
132
+ puts '', '-'*80
133
+
134
+
135
+ require 'pry'; binding.pry;
@@ -25,9 +25,7 @@ class Article < ActiveRecord::Base
25
25
  end
26
26
 
27
27
  def as_indexed_json(options={})
28
- as_json.merge \
29
- title_suggest: { input: title },
30
- url: "/articles/#{id}"
28
+ as_json.merge 'url' => "/articles/#{id}"
31
29
  end
32
30
  end
33
31
 
@@ -52,20 +50,7 @@ response_1 = Article.search 'foo';
52
50
  puts "Article search:".ansi(:bold),
53
51
  response_1.to_a.map { |d| "Title: #{d.title}" }.inspect.ansi(:bold, :yellow)
54
52
 
55
- response_2 = Article.__elasticsearch__.client.suggest \
56
- index: Article.index_name,
57
- body: {
58
- articles: {
59
- text: 'foo',
60
- completion: { field: 'title.suggest' }
61
- }
62
- };
63
-
64
- puts "Article suggest:".ansi(:bold),
65
- response_2['articles'].first['options'].map { |d| "#{d['text']} -> #{d['_source']['url']}" }.
66
- inspect.ansi(:bold, :green)
67
-
68
- response_3 = Article.search \
53
+ response_2 = Article.search \
69
54
  query: {
70
55
  match: { title: 'foo' }
71
56
  },
@@ -78,7 +63,7 @@ response_3 = Article.search \
78
63
  _source: ['title', 'url']
79
64
 
80
65
  puts "Article search with suggest:".ansi(:bold),
81
- response_3.response['suggest']['articles'].first['options'].map { |d| "#{d['text']} -> #{d['_source']['url']}" }.
66
+ response_2.response['suggest']['articles'].first['options'].map { |d| "#{d['text']} -> #{d['_source']['url']}" }.
82
67
  inspect.ansi(:bold, :blue)
83
68
 
84
69
  require 'pry'; binding.pry;
@@ -50,11 +50,21 @@ module DataMapperAdapter
50
50
  #
51
51
  module Records
52
52
  def records
53
- klass.all(id: @ids)
53
+ klass.all(id: ids)
54
54
  end
55
55
 
56
56
  # ...
57
57
  end
58
+
59
+ module Callbacks
60
+ def self.included(model)
61
+ model.class_eval do
62
+ after(:create) { __elasticsearch__.index_document }
63
+ after(:save) { __elasticsearch__.update_document }
64
+ after(:destroy) { __elasticsearch__.delete_document }
65
+ end
66
+ end
67
+ end
58
68
  end
59
69
 
60
70
  # Register the adapter
data/gemfiles/3.0.gemfile CHANGED
@@ -10,4 +10,9 @@ gemspec path: '../'
10
10
  gem 'activemodel', '>= 3.0'
11
11
  gem 'activerecord', '~> 3.2'
12
12
  gem 'mongoid', '>= 3.0'
13
- gem 'sqlite3'
13
+ gem 'sqlite3', '~> 1.3.6' unless defined?(JRUBY_VERSION)
14
+
15
+ group :development, :testing do
16
+ gem 'rspec'
17
+ gem 'pry-nav'
18
+ end
data/gemfiles/4.0.gemfile CHANGED
@@ -9,4 +9,11 @@ gemspec path: '../'
9
9
 
10
10
  gem 'activemodel', '~> 4'
11
11
  gem 'activerecord', '~> 4'
12
- gem 'sqlite3'
12
+ gem 'mongoid', '~> 5'
13
+ gem 'sqlite3', '~> 1.3.6' unless defined?(JRUBY_VERSION)
14
+
15
+ group :development, :testing do
16
+ gem 'bigdecimal', '~> 1'
17
+ gem 'pry-nav'
18
+ gem 'rspec'
19
+ end
data/gemfiles/5.0.gemfile CHANGED
@@ -9,4 +9,10 @@ gemspec path: '../'
9
9
 
10
10
  gem 'activemodel', '~> 5'
11
11
  gem 'activerecord', '~> 5'
12
- gem 'sqlite3'
12
+ gem 'sqlite3' unless defined?(JRUBY_VERSION)
13
+ gem 'mongoid', '~> 6.4'
14
+
15
+ group :development, :testing do
16
+ gem 'rspec'
17
+ gem 'pry-nav'
18
+ end
@@ -0,0 +1,18 @@
1
+ # Usage:
2
+ #
3
+ # $ BUNDLE_GEMFILE=./gemfiles/6.0.gemfile bundle install
4
+ # $ BUNDLE_GEMFILE=./gemfiles/6.0.gemfile bundle exec rake test:integration
5
+
6
+ source 'https://rubygems.org'
7
+
8
+ gemspec path: '../'
9
+
10
+ gem 'activemodel', '~> 6'
11
+ gem 'activerecord', '~> 6'
12
+ gem 'sqlite3' unless defined?(JRUBY_VERSION)
13
+ gem 'mongoid', '~> 7'
14
+
15
+ group :development, :testing do
16
+ gem 'rspec'
17
+ gem 'pry-nav'
18
+ end
@@ -35,7 +35,11 @@ module Elasticsearch
35
35
  else
36
36
  self.__send__(:exec_queries)
37
37
  end
38
- @records.sort_by { |record| hits.index { |hit| hit['_id'].to_s == record.id.to_s } }
38
+ if !self.order_values.present?
39
+ @records.sort_by { |record| hits.index { |hit| hit['_id'].to_s == record.id.to_s } }
40
+ else
41
+ @records
42
+ end
39
43
  end if self
40
44
  end
41
45
 
@@ -47,27 +51,6 @@ module Elasticsearch
47
51
  def load
48
52
  records.__send__(:load)
49
53
  end
50
-
51
- # Intercept call to the `order` method, so we can ignore the order from Elasticsearch
52
- #
53
- def order(*args)
54
- sql_records = records.__send__ :order, *args
55
-
56
- # Redefine the `to_a` method to the original one
57
- #
58
- sql_records.instance_exec do
59
- define_singleton_method(:to_a) do
60
- if defined?(::ActiveRecord) && ::ActiveRecord::VERSION::MAJOR >= 4
61
- self.load
62
- else
63
- self.__send__(:exec_queries)
64
- end
65
- @records
66
- end
67
- end
68
-
69
- sql_records
70
- end
71
54
  end
72
55
 
73
56
  module Callbacks
@@ -102,8 +85,9 @@ module Elasticsearch
102
85
  scope = scope.__send__(named_scope) if named_scope
103
86
  scope = scope.instance_exec(&query) if query
104
87
 
105
- scope.find_in_batches(options) do |batch|
106
- yield (preprocess ? self.__send__(preprocess, batch) : batch)
88
+ scope.find_in_batches(**options) do |batch|
89
+ batch = self.__send__(preprocess, batch) if preprocess
90
+ yield(batch) if batch.present?
107
91
  end
108
92
  end
109
93
 
@@ -63,10 +63,17 @@ module Elasticsearch
63
63
  # @see https://github.com/karmi/retire/pull/724
64
64
  #
65
65
  def __find_in_batches(options={}, &block)
66
- options[:batch_size] ||= 1_000
66
+ batch_size = options[:batch_size] || 1_000
67
+ query = options[:query]
68
+ named_scope = options[:scope]
69
+ preprocess = options[:preprocess]
70
+
71
+ scope = all
72
+ scope = scope.send(named_scope) if named_scope
73
+ scope = query.is_a?(Proc) ? scope.class_exec(&query) : scope.where(query) if query
67
74
 
68
- all.no_timeout.each_slice(options[:batch_size]) do |items|
69
- yield items
75
+ scope.no_timeout.each_slice(batch_size) do |items|
76
+ yield (preprocess ? self.__send__(preprocess, items) : items)
70
77
  end
71
78
  end
72
79
 
@@ -130,7 +130,7 @@ module Elasticsearch
130
130
  errors += response['items'].select { |k, v| k.values.first['error'] }
131
131
  end
132
132
 
133
- self.refresh_index! if refresh
133
+ self.refresh_index! index: target_index if refresh
134
134
 
135
135
  case return_value
136
136
  when 'errors'
@@ -269,7 +269,8 @@ module Elasticsearch
269
269
  self.client.indices.delete index: target_index
270
270
  rescue Exception => e
271
271
  if e.class.to_s =~ /NotFound/ && options[:force]
272
- STDERR.puts "[!!!] Index does not exist (#{e.class})"
272
+ client.transport.logger.debug("[!!!] Index does not exist (#{e.class})") if client.transport.logger
273
+ nil
273
274
  else
274
275
  raise e
275
276
  end
@@ -295,7 +296,8 @@ module Elasticsearch
295
296
  self.client.indices.refresh index: target_index
296
297
  rescue Exception => e
297
298
  if e.class.to_s =~ /NotFound/ && options[:force]
298
- STDERR.puts "[!!!] Index does not exist (#{e.class})"
299
+ client.transport.logger.debug("[!!!] Index does not exist (#{e.class})") if client.transport.logger
300
+ nil
299
301
  else
300
302
  raise e
301
303
  end
@@ -397,7 +399,7 @@ module Elasticsearch
397
399
  # @see http://rubydoc.info/gems/elasticsearch-api/Elasticsearch/API/Actions:update
398
400
  #
399
401
  def update_document(options={})
400
- if attributes_in_database = self.instance_variable_get(:@__changed_model_attributes)
402
+ if attributes_in_database = self.instance_variable_get(:@__changed_model_attributes).presence
401
403
  attributes = if respond_to?(:as_indexed_json)
402
404
  self.as_indexed_json.select { |k,v| attributes_in_database.keys.map(&:to_s).include? k.to_s }
403
405
  else
@@ -409,7 +411,7 @@ module Elasticsearch
409
411
  type: document_type,
410
412
  id: self.id,
411
413
  body: { doc: attributes } }.merge(options)
412
- )
414
+ ) unless attributes.empty?
413
415
  else
414
416
  index_document(options)
415
417
  end
@@ -5,6 +5,8 @@ module Elasticsearch
5
5
  #
6
6
  module Naming
7
7
 
8
+ DEFAULT_DOC_TYPE = '_doc'.freeze
9
+
8
10
  module ClassMethods
9
11
 
10
12
  # Get or set the name of the index
@@ -77,7 +79,12 @@ module Elasticsearch
77
79
 
78
80
  if Elasticsearch::Model.settings[:inheritance_enabled]
79
81
  self.ancestors.each do |klass|
80
- next if klass == self
82
+ # When Naming is included in Proxy::ClassMethods the actual model
83
+ # is among its ancestors. We don't want to call the actual model
84
+ # since it will result in the same call to the same instance of
85
+ # Proxy::ClassMethods. To prevent this we also skip the ancestor
86
+ # that is the target.
87
+ next if klass == self || self.respond_to?(:target) && klass == self.target
81
88
  break if value = klass.respond_to?(prop) && klass.send(prop)
82
89
  end
83
90
  end
@@ -90,7 +97,7 @@ module Elasticsearch
90
97
  end
91
98
 
92
99
  def default_document_type
93
- self.model_name.element
100
+ DEFAULT_DOC_TYPE
94
101
  end
95
102
 
96
103
  end
@@ -2,7 +2,7 @@ module Elasticsearch
2
2
  module Model
3
3
  module Response
4
4
 
5
- class Aggregations < Hashie::Mash
5
+ class Aggregations < HashWrapper
6
6
  disable_warnings if respond_to?(:disable_warnings)
7
7
 
8
8
  def initialize(attributes={})
@@ -4,7 +4,7 @@ module Elasticsearch
4
4
  # Common funtionality for classes in the {Elasticsearch::Model::Response} module
5
5
  #
6
6
  module Base
7
- attr_reader :klass, :response
7
+ attr_reader :klass, :response, :raw_response
8
8
 
9
9
  # @param klass [Class] The name of the model class
10
10
  # @param response [Hash] The full response returned from Elasticsearch client
@@ -12,7 +12,8 @@ module Elasticsearch
12
12
  #
13
13
  def initialize(klass, response, options={})
14
14
  @klass = klass
15
- @response = response
15
+ @raw_response = response
16
+ @response = response
16
17
  end
17
18
 
18
19
  # @abstract Implement this method in specific class