elasticsearch-model 5.1.0 → 6.1.1

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