elasticsearch-model-queryable 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/CHANGELOG.md +26 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +13 -0
- data/README.md +695 -0
- data/Rakefile +59 -0
- data/elasticsearch-model.gemspec +57 -0
- data/examples/activerecord_article.rb +77 -0
- data/examples/activerecord_associations.rb +162 -0
- data/examples/couchbase_article.rb +66 -0
- data/examples/datamapper_article.rb +71 -0
- data/examples/mongoid_article.rb +68 -0
- data/examples/ohm_article.rb +70 -0
- data/examples/riak_article.rb +52 -0
- data/gemfiles/3.0.gemfile +12 -0
- data/gemfiles/4.0.gemfile +11 -0
- data/lib/elasticsearch/model/adapter.rb +145 -0
- data/lib/elasticsearch/model/adapters/active_record.rb +104 -0
- data/lib/elasticsearch/model/adapters/default.rb +50 -0
- data/lib/elasticsearch/model/adapters/mongoid.rb +92 -0
- data/lib/elasticsearch/model/callbacks.rb +35 -0
- data/lib/elasticsearch/model/client.rb +61 -0
- data/lib/elasticsearch/model/ext/active_record.rb +14 -0
- data/lib/elasticsearch/model/hash_wrapper.rb +15 -0
- data/lib/elasticsearch/model/importing.rb +144 -0
- data/lib/elasticsearch/model/indexing.rb +472 -0
- data/lib/elasticsearch/model/naming.rb +101 -0
- data/lib/elasticsearch/model/proxy.rb +127 -0
- data/lib/elasticsearch/model/response/base.rb +44 -0
- data/lib/elasticsearch/model/response/pagination.rb +173 -0
- data/lib/elasticsearch/model/response/records.rb +69 -0
- data/lib/elasticsearch/model/response/result.rb +63 -0
- data/lib/elasticsearch/model/response/results.rb +31 -0
- data/lib/elasticsearch/model/response.rb +71 -0
- data/lib/elasticsearch/model/searching.rb +107 -0
- data/lib/elasticsearch/model/serializing.rb +35 -0
- data/lib/elasticsearch/model/version.rb +5 -0
- data/lib/elasticsearch/model.rb +157 -0
- data/test/integration/active_record_associations_parent_child.rb +139 -0
- data/test/integration/active_record_associations_test.rb +307 -0
- data/test/integration/active_record_basic_test.rb +179 -0
- data/test/integration/active_record_custom_serialization_test.rb +62 -0
- data/test/integration/active_record_import_test.rb +100 -0
- data/test/integration/active_record_namespaced_model_test.rb +49 -0
- data/test/integration/active_record_pagination_test.rb +132 -0
- data/test/integration/mongoid_basic_test.rb +193 -0
- data/test/test_helper.rb +63 -0
- data/test/unit/adapter_active_record_test.rb +140 -0
- data/test/unit/adapter_default_test.rb +41 -0
- data/test/unit/adapter_mongoid_test.rb +102 -0
- data/test/unit/adapter_test.rb +69 -0
- data/test/unit/callbacks_test.rb +31 -0
- data/test/unit/client_test.rb +27 -0
- data/test/unit/importing_test.rb +176 -0
- data/test/unit/indexing_test.rb +478 -0
- data/test/unit/module_test.rb +57 -0
- data/test/unit/naming_test.rb +76 -0
- data/test/unit/proxy_test.rb +89 -0
- data/test/unit/response_base_test.rb +40 -0
- data/test/unit/response_pagination_kaminari_test.rb +189 -0
- data/test/unit/response_pagination_will_paginate_test.rb +208 -0
- data/test/unit/response_records_test.rb +91 -0
- data/test/unit/response_result_test.rb +90 -0
- data/test/unit/response_results_test.rb +31 -0
- data/test/unit/response_test.rb +67 -0
- data/test/unit/searching_search_request_test.rb +78 -0
- data/test/unit/searching_test.rb +41 -0
- data/test/unit/serializing_test.rb +17 -0
- metadata +466 -0
data/Rakefile
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
desc "Run unit tests"
|
4
|
+
task :default => 'test:unit'
|
5
|
+
task :test => 'test:unit'
|
6
|
+
|
7
|
+
# ----- Test tasks ------------------------------------------------------------
|
8
|
+
|
9
|
+
require 'rake/testtask'
|
10
|
+
namespace :test do
|
11
|
+
task :ci_reporter do
|
12
|
+
ENV['CI_REPORTS'] ||= 'tmp/reports'
|
13
|
+
require 'ci/reporter/rake/minitest'
|
14
|
+
Rake::Task['ci:setup:minitest'].invoke
|
15
|
+
end
|
16
|
+
|
17
|
+
Rake::TestTask.new(:unit) do |test|
|
18
|
+
Rake::Task['test:ci_reporter'].invoke if ENV['CI']
|
19
|
+
test.libs << 'lib' << 'test'
|
20
|
+
test.test_files = FileList["test/unit/**/*_test.rb"]
|
21
|
+
# test.verbose = true
|
22
|
+
# test.warning = true
|
23
|
+
end
|
24
|
+
|
25
|
+
Rake::TestTask.new(:run_integration) do |test|
|
26
|
+
Rake::Task['test:ci_reporter'].invoke if ENV['CI']
|
27
|
+
test.libs << 'lib' << 'test'
|
28
|
+
test.test_files = FileList["test/integration/**/*_test.rb"]
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "Run integration tests against ActiveModel 3 and 4"
|
32
|
+
task :integration do
|
33
|
+
sh "BUNDLE_GEMFILE='#{File.expand_path('../gemfiles/3.0.gemfile', __FILE__)}' bundle exec rake test:run_integration" unless defined?(RUBY_VERSION) && RUBY_VERSION > '2.2'
|
34
|
+
sh "BUNDLE_GEMFILE='#{File.expand_path('../gemfiles/4.0.gemfile', __FILE__)}' bundle exec rake test:run_integration"
|
35
|
+
end
|
36
|
+
|
37
|
+
Rake::TestTask.new(:all) do |test|
|
38
|
+
Rake::Task['test:ci_reporter'].invoke if ENV['CI']
|
39
|
+
test.libs << 'lib' << 'test'
|
40
|
+
test.test_files = FileList["test/unit/**/*_test.rb", "test/integration/**/*_test.rb"]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# ----- Documentation tasks ---------------------------------------------------
|
45
|
+
|
46
|
+
require 'yard'
|
47
|
+
YARD::Rake::YardocTask.new(:doc) do |t|
|
48
|
+
t.options = %w| --embed-mixins --markup=markdown |
|
49
|
+
end
|
50
|
+
|
51
|
+
# ----- Code analysis tasks ---------------------------------------------------
|
52
|
+
|
53
|
+
if defined?(RUBY_VERSION) && RUBY_VERSION > '1.9'
|
54
|
+
require 'cane/rake_task'
|
55
|
+
Cane::RakeTask.new(:quality) do |cane|
|
56
|
+
cane.abc_max = 15
|
57
|
+
cane.no_style = true
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "elasticsearch/model/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "elasticsearch-model-queryable"
|
8
|
+
s.version = Elasticsearch::Model::VERSION
|
9
|
+
s.authors = ["Karel Minarik"]
|
10
|
+
s.email = ["karel.minarik@elasticsearch.org"]
|
11
|
+
s.description = "ActiveModel/Record integrations for Elasticsearch."
|
12
|
+
s.summary = "ActiveModel/Record integrations for Elasticsearch."
|
13
|
+
s.homepage = "https://github.com/elasticsearch/elasticsearch-rails/"
|
14
|
+
s.license = "Apache 2"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split($/)
|
17
|
+
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.extra_rdoc_files = ["README.md", "LICENSE.txt"]
|
22
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
23
|
+
|
24
|
+
s.required_ruby_version = ">= 1.9.3"
|
25
|
+
|
26
|
+
s.add_dependency "elasticsearch", "> 0.4"
|
27
|
+
s.add_dependency "activesupport", "> 3"
|
28
|
+
s.add_dependency "hashie"
|
29
|
+
|
30
|
+
s.add_development_dependency "bundler", "~> 1.3"
|
31
|
+
s.add_development_dependency "rake"
|
32
|
+
|
33
|
+
s.add_development_dependency "elasticsearch-extensions"
|
34
|
+
|
35
|
+
s.add_development_dependency "sqlite3"
|
36
|
+
s.add_development_dependency "activemodel", "> 3.0"
|
37
|
+
|
38
|
+
s.add_development_dependency "oj"
|
39
|
+
s.add_development_dependency "kaminari"
|
40
|
+
s.add_development_dependency "will_paginate"
|
41
|
+
|
42
|
+
s.add_development_dependency "minitest", "~> 4"
|
43
|
+
s.add_development_dependency "test-unit" if defined?(RUBY_VERSION) && RUBY_VERSION > "2.2"
|
44
|
+
s.add_development_dependency "shoulda-context"
|
45
|
+
s.add_development_dependency "mocha"
|
46
|
+
s.add_development_dependency "turn"
|
47
|
+
s.add_development_dependency "yard"
|
48
|
+
s.add_development_dependency "ruby-prof"
|
49
|
+
s.add_development_dependency "pry"
|
50
|
+
s.add_development_dependency "ci_reporter", "~> 1.9"
|
51
|
+
|
52
|
+
if defined?(RUBY_VERSION) && RUBY_VERSION > "1.9"
|
53
|
+
s.add_development_dependency "simplecov"
|
54
|
+
s.add_development_dependency "cane"
|
55
|
+
s.add_development_dependency "require-prof"
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# ActiveRecord and Elasticsearch
|
2
|
+
# ==============================
|
3
|
+
#
|
4
|
+
# https://github.com/rails/rails/tree/master/activerecord
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
7
|
+
|
8
|
+
require 'pry'
|
9
|
+
Pry.config.history.file = File.expand_path('../../tmp/elasticsearch_development.pry', __FILE__)
|
10
|
+
|
11
|
+
require 'logger'
|
12
|
+
require 'ansi/core'
|
13
|
+
require 'active_record'
|
14
|
+
require 'kaminari'
|
15
|
+
|
16
|
+
require 'elasticsearch/model'
|
17
|
+
|
18
|
+
ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT)
|
19
|
+
ActiveRecord::Base.establish_connection( adapter: 'sqlite3', database: ":memory:" )
|
20
|
+
|
21
|
+
ActiveRecord::Schema.define(version: 1) do
|
22
|
+
create_table :articles do |t|
|
23
|
+
t.string :title
|
24
|
+
t.date :published_at
|
25
|
+
t.timestamps
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Kaminari::Hooks.init
|
30
|
+
|
31
|
+
class Article < ActiveRecord::Base
|
32
|
+
end
|
33
|
+
|
34
|
+
# Store data
|
35
|
+
#
|
36
|
+
Article.delete_all
|
37
|
+
Article.create title: 'Foo'
|
38
|
+
Article.create title: 'Bar'
|
39
|
+
Article.create title: 'Foo Foo'
|
40
|
+
|
41
|
+
# Index data
|
42
|
+
#
|
43
|
+
client = Elasticsearch::Client.new log:true
|
44
|
+
|
45
|
+
# client.indices.delete index: 'articles' rescue nil
|
46
|
+
# client.indices.create index: 'articles', body: { mappings: { article: { dynamic: 'strict' }, properties: {} } }
|
47
|
+
|
48
|
+
client.indices.delete index: 'articles' rescue nil
|
49
|
+
client.bulk index: 'articles',
|
50
|
+
type: 'article',
|
51
|
+
body: Article.all.as_json.map { |a| { index: { _id: a.delete('id'), data: a } } },
|
52
|
+
refresh: true
|
53
|
+
|
54
|
+
# Extend the model with Elasticsearch support
|
55
|
+
#
|
56
|
+
Article.__send__ :include, Elasticsearch::Model
|
57
|
+
# Article.__send__ :include, Elasticsearch::Model::Callbacks
|
58
|
+
|
59
|
+
# ActiveRecord::Base.logger.silence do
|
60
|
+
# 10_000.times do |i|
|
61
|
+
# Article.create title: "Foo #{i}"
|
62
|
+
# end
|
63
|
+
# end
|
64
|
+
|
65
|
+
puts '', '-'*Pry::Terminal.width!
|
66
|
+
|
67
|
+
Elasticsearch::Model.client = Elasticsearch::Client.new log: true
|
68
|
+
|
69
|
+
response = Article.search 'foo';
|
70
|
+
|
71
|
+
p response.size
|
72
|
+
p response.results.size
|
73
|
+
p response.records.size
|
74
|
+
|
75
|
+
Pry.start(binding, prompt: lambda { |obj, nest_level, _| '> ' },
|
76
|
+
input: StringIO.new('response.records.to_a'),
|
77
|
+
quiet: true)
|
@@ -0,0 +1,162 @@
|
|
1
|
+
# ActiveRecord associations and Elasticsearch
|
2
|
+
# ===========================================
|
3
|
+
#
|
4
|
+
# https://github.com/rails/rails/tree/master/activerecord
|
5
|
+
# http://guides.rubyonrails.org/association_basics.html
|
6
|
+
#
|
7
|
+
# Run me with:
|
8
|
+
#
|
9
|
+
# ruby -I lib examples/activerecord_associations.rb
|
10
|
+
#
|
11
|
+
|
12
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
13
|
+
|
14
|
+
require 'pry'
|
15
|
+
Pry.config.history.file = File.expand_path('../../tmp/elasticsearch_development.pry', __FILE__)
|
16
|
+
|
17
|
+
require 'logger'
|
18
|
+
require 'ansi/core'
|
19
|
+
require 'active_record'
|
20
|
+
|
21
|
+
require 'elasticsearch/model'
|
22
|
+
|
23
|
+
ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT)
|
24
|
+
ActiveRecord::Base.establish_connection( adapter: 'sqlite3', database: ":memory:" )
|
25
|
+
|
26
|
+
# ----- Schema definition -------------------------------------------------------------------------
|
27
|
+
|
28
|
+
ActiveRecord::Schema.define(version: 1) do
|
29
|
+
create_table :categories do |t|
|
30
|
+
t.string :title
|
31
|
+
t.timestamps
|
32
|
+
end
|
33
|
+
|
34
|
+
create_table :authors do |t|
|
35
|
+
t.string :first_name, :last_name
|
36
|
+
t.timestamps
|
37
|
+
end
|
38
|
+
|
39
|
+
create_table :authorships do |t|
|
40
|
+
t.references :article
|
41
|
+
t.references :author
|
42
|
+
t.timestamps
|
43
|
+
end
|
44
|
+
|
45
|
+
create_table :articles do |t|
|
46
|
+
t.string :title
|
47
|
+
t.timestamps
|
48
|
+
end
|
49
|
+
|
50
|
+
create_table :articles_categories, id: false do |t|
|
51
|
+
t.references :article, :category
|
52
|
+
end
|
53
|
+
|
54
|
+
create_table :comments do |t|
|
55
|
+
t.string :text
|
56
|
+
t.references :article
|
57
|
+
t.timestamps
|
58
|
+
end
|
59
|
+
add_index(:comments, :article_id)
|
60
|
+
end
|
61
|
+
|
62
|
+
# ----- Model definitions -------------------------------------------------------------------------
|
63
|
+
|
64
|
+
class Category < ActiveRecord::Base
|
65
|
+
has_and_belongs_to_many :articles
|
66
|
+
end
|
67
|
+
|
68
|
+
class Author < ActiveRecord::Base
|
69
|
+
has_many :authorships
|
70
|
+
|
71
|
+
after_update { self.authorships.each(&:touch) }
|
72
|
+
|
73
|
+
def full_name
|
74
|
+
[first_name, last_name].compact.join(' ')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class Authorship < ActiveRecord::Base
|
79
|
+
belongs_to :author
|
80
|
+
belongs_to :article, touch: true
|
81
|
+
end
|
82
|
+
|
83
|
+
class Article < ActiveRecord::Base
|
84
|
+
has_and_belongs_to_many :categories, after_add: [ lambda { |a,c| a.__elasticsearch__.index_document } ],
|
85
|
+
after_remove: [ lambda { |a,c| a.__elasticsearch__.index_document } ]
|
86
|
+
has_many :authorships
|
87
|
+
has_many :authors, through: :authorships
|
88
|
+
has_many :comments
|
89
|
+
end
|
90
|
+
|
91
|
+
class Article < ActiveRecord::Base; delegate :size, to: :comments, prefix: true; end
|
92
|
+
|
93
|
+
class Comment < ActiveRecord::Base
|
94
|
+
belongs_to :article, touch: true
|
95
|
+
end
|
96
|
+
|
97
|
+
# ----- Search integration ------------------------------------------------------------------------
|
98
|
+
|
99
|
+
module Searchable
|
100
|
+
extend ActiveSupport::Concern
|
101
|
+
|
102
|
+
included do
|
103
|
+
include Elasticsearch::Model
|
104
|
+
include Elasticsearch::Model::Callbacks
|
105
|
+
|
106
|
+
__elasticsearch__.client = Elasticsearch::Client.new log: true
|
107
|
+
__elasticsearch__.client.transport.logger.formatter = proc { |s, d, p, m| "\e[32m#{m}\n\e[0m" }
|
108
|
+
|
109
|
+
include Indexing
|
110
|
+
after_touch() { __elasticsearch__.index_document }
|
111
|
+
end
|
112
|
+
|
113
|
+
module Indexing
|
114
|
+
|
115
|
+
# Customize the JSON serialization for Elasticsearch
|
116
|
+
def as_indexed_json(options={})
|
117
|
+
self.as_json(
|
118
|
+
include: { categories: { only: :title},
|
119
|
+
authors: { methods: [:full_name], only: [:full_name] },
|
120
|
+
comments: { only: :text }
|
121
|
+
})
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
Article.__send__ :include, Searchable
|
127
|
+
|
128
|
+
# ----- Insert data -------------------------------------------------------------------------------
|
129
|
+
|
130
|
+
# Create category
|
131
|
+
#
|
132
|
+
category = Category.create title: 'One'
|
133
|
+
|
134
|
+
# Create author
|
135
|
+
#
|
136
|
+
author = Author.create first_name: 'John', last_name: 'Smith'
|
137
|
+
|
138
|
+
# Create article
|
139
|
+
|
140
|
+
article = Article.create title: 'First Article'
|
141
|
+
|
142
|
+
# Assign category
|
143
|
+
#
|
144
|
+
article.categories << category
|
145
|
+
|
146
|
+
# Assign author
|
147
|
+
#
|
148
|
+
article.authors << author
|
149
|
+
|
150
|
+
# Add comment
|
151
|
+
#
|
152
|
+
article.comments.create text: 'First comment'
|
153
|
+
|
154
|
+
# Load
|
155
|
+
#
|
156
|
+
article = Article.all.includes(:categories, :authors, :comments).first
|
157
|
+
|
158
|
+
# ----- Pry ---------------------------------------------------------------------------------------
|
159
|
+
|
160
|
+
Pry.start(binding, prompt: lambda { |obj, nest_level, _| '> ' },
|
161
|
+
input: StringIO.new('puts "\n\narticle.as_indexed_json\n"; article.as_indexed_json'),
|
162
|
+
quiet: true)
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# Couchbase and Elasticsearch
|
2
|
+
# ===========================
|
3
|
+
#
|
4
|
+
# https://github.com/couchbase/couchbase-ruby-model
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
7
|
+
|
8
|
+
require 'pry'
|
9
|
+
Pry.config.history.file = File.expand_path('../../tmp/elasticsearch_development.pry', __FILE__)
|
10
|
+
|
11
|
+
require 'logger'
|
12
|
+
require 'couchbase/model'
|
13
|
+
|
14
|
+
require 'elasticsearch/model'
|
15
|
+
|
16
|
+
# Documents are stored as JSON objects in Riak but have rich
|
17
|
+
# semantics, including validations and associations.
|
18
|
+
class Article < Couchbase::Model
|
19
|
+
attribute :title
|
20
|
+
attribute :published_at
|
21
|
+
|
22
|
+
# view :all, :limit => 10, :descending => true
|
23
|
+
# TODO: Implement view a la
|
24
|
+
# bucket.save_design_doc <<-JSON
|
25
|
+
# {
|
26
|
+
# "_id": "_design/article",
|
27
|
+
# "language": "javascript",
|
28
|
+
# "views": {
|
29
|
+
# "all": {
|
30
|
+
# "map": "function(doc, meta) { emit(doc.id, doc.title); }"
|
31
|
+
# }
|
32
|
+
# }
|
33
|
+
# }
|
34
|
+
# JSON
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
# Extend the model with Elasticsearch support
|
39
|
+
#
|
40
|
+
Article.__send__ :extend, Elasticsearch::Model::Client::ClassMethods
|
41
|
+
Article.__send__ :extend, Elasticsearch::Model::Searching::ClassMethods
|
42
|
+
Article.__send__ :extend, Elasticsearch::Model::Naming::ClassMethods
|
43
|
+
|
44
|
+
# Create documents in Riak
|
45
|
+
#
|
46
|
+
Article.create id: '1', title: 'Foo' rescue nil
|
47
|
+
Article.create id: '2', title: 'Bar' rescue nil
|
48
|
+
Article.create id: '3', title: 'Foo Foo' rescue nil
|
49
|
+
|
50
|
+
# Index data into Elasticsearch
|
51
|
+
#
|
52
|
+
client = Elasticsearch::Client.new log:true
|
53
|
+
|
54
|
+
client.indices.delete index: 'articles' rescue nil
|
55
|
+
client.bulk index: 'articles',
|
56
|
+
type: 'article',
|
57
|
+
body: Article.find(['1', '2', '3']).map { |a|
|
58
|
+
{ index: { _id: a.id, data: a.attributes } }
|
59
|
+
},
|
60
|
+
refresh: true
|
61
|
+
|
62
|
+
response = Article.search 'foo', index: 'articles', type: 'article';
|
63
|
+
|
64
|
+
Pry.start(binding, prompt: lambda { |obj, nest_level, _| '> ' },
|
65
|
+
input: StringIO.new('response.records.to_a'),
|
66
|
+
quiet: true)
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# DataMapper and Elasticsearch
|
2
|
+
# ============================
|
3
|
+
#
|
4
|
+
# https://github.com/datamapper/dm-core
|
5
|
+
# https://github.com/datamapper/dm-active_model
|
6
|
+
|
7
|
+
|
8
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
9
|
+
|
10
|
+
require 'pry'
|
11
|
+
Pry.config.history.file = File.expand_path('../../tmp/elasticsearch_development.pry', __FILE__)
|
12
|
+
|
13
|
+
require 'logger'
|
14
|
+
require 'ansi/core'
|
15
|
+
|
16
|
+
require 'data_mapper'
|
17
|
+
require 'dm-active_model'
|
18
|
+
|
19
|
+
require 'active_support/all'
|
20
|
+
|
21
|
+
require 'elasticsearch/model'
|
22
|
+
|
23
|
+
DataMapper::Logger.new(STDOUT, :debug)
|
24
|
+
DataMapper.setup(:default, 'sqlite::memory:')
|
25
|
+
|
26
|
+
class Article
|
27
|
+
include DataMapper::Resource
|
28
|
+
|
29
|
+
property :id, Serial
|
30
|
+
property :title, String
|
31
|
+
property :published_at, DateTime
|
32
|
+
end
|
33
|
+
|
34
|
+
DataMapper.auto_migrate!
|
35
|
+
DataMapper.finalize
|
36
|
+
|
37
|
+
Article.create title: 'Foo'
|
38
|
+
Article.create title: 'Bar'
|
39
|
+
Article.create title: 'Foo Foo'
|
40
|
+
|
41
|
+
# Extend the model with Elasticsearch support
|
42
|
+
#
|
43
|
+
Article.__send__ :include, Elasticsearch::Model
|
44
|
+
|
45
|
+
# The DataMapper adapter
|
46
|
+
#
|
47
|
+
module DataMapperAdapter
|
48
|
+
|
49
|
+
# Implement the interface for fetching records
|
50
|
+
#
|
51
|
+
module Records
|
52
|
+
def records
|
53
|
+
klass.all(id: @ids)
|
54
|
+
end
|
55
|
+
|
56
|
+
# ...
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Register the adapter
|
61
|
+
#
|
62
|
+
Elasticsearch::Model::Adapter.register(
|
63
|
+
DataMapperAdapter,
|
64
|
+
lambda { |klass| defined?(::DataMapper::Resource) and klass.ancestors.include?(::DataMapper::Resource) }
|
65
|
+
)
|
66
|
+
|
67
|
+
response = Article.search 'foo';
|
68
|
+
|
69
|
+
Pry.start(binding, prompt: lambda { |obj, nest_level, _| '> ' },
|
70
|
+
input: StringIO.new('response.records.to_a'),
|
71
|
+
quiet: true)
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# Mongoid and Elasticsearch
|
2
|
+
# =========================
|
3
|
+
#
|
4
|
+
# http://mongoid.org/en/mongoid/index.html
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
7
|
+
|
8
|
+
require 'pry'
|
9
|
+
Pry.config.history.file = File.expand_path('../../tmp/elasticsearch_development.pry', __FILE__)
|
10
|
+
|
11
|
+
require 'benchmark'
|
12
|
+
require 'logger'
|
13
|
+
require 'ansi/core'
|
14
|
+
require 'mongoid'
|
15
|
+
|
16
|
+
require 'elasticsearch/model'
|
17
|
+
require 'elasticsearch/model/callbacks'
|
18
|
+
|
19
|
+
Mongoid.logger.level = Logger::DEBUG
|
20
|
+
Moped.logger.level = Logger::DEBUG
|
21
|
+
|
22
|
+
Mongoid.connect_to 'articles'
|
23
|
+
|
24
|
+
Elasticsearch::Model.client = Elasticsearch::Client.new host: 'localhost:9250', log: true
|
25
|
+
|
26
|
+
class Article
|
27
|
+
include Mongoid::Document
|
28
|
+
field :id, type: String
|
29
|
+
field :title, type: String
|
30
|
+
field :published_at, type: DateTime
|
31
|
+
attr_accessible :id, :title, :published_at if respond_to? :attr_accessible
|
32
|
+
|
33
|
+
def as_indexed_json(options={})
|
34
|
+
as_json(except: [:id, :_id])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Extend the model with Elasticsearch support
|
39
|
+
#
|
40
|
+
Article.__send__ :include, Elasticsearch::Model
|
41
|
+
# Article.__send__ :include, Elasticsearch::Model::Callbacks
|
42
|
+
|
43
|
+
# Store data
|
44
|
+
#
|
45
|
+
Article.delete_all
|
46
|
+
Article.create id: '1', title: 'Foo'
|
47
|
+
Article.create id: '2', title: 'Bar'
|
48
|
+
Article.create id: '3', title: 'Foo Foo'
|
49
|
+
|
50
|
+
# Index data
|
51
|
+
#
|
52
|
+
client = Elasticsearch::Client.new host:'localhost:9250', log:true
|
53
|
+
|
54
|
+
client.indices.delete index: 'articles' rescue nil
|
55
|
+
client.bulk index: 'articles',
|
56
|
+
type: 'article',
|
57
|
+
body: Article.all.map { |a| { index: { _id: a.id, data: a.attributes } } },
|
58
|
+
refresh: true
|
59
|
+
|
60
|
+
# puts Benchmark.realtime { 9_875.times { |i| Article.create title: "Foo #{i}" } }
|
61
|
+
|
62
|
+
puts '', '-'*Pry::Terminal.width!
|
63
|
+
|
64
|
+
response = Article.search 'foo';
|
65
|
+
|
66
|
+
Pry.start(binding, prompt: lambda { |obj, nest_level, _| '> ' },
|
67
|
+
input: StringIO.new('response.records.to_a'),
|
68
|
+
quiet: true)
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# Ohm for Redis and Elasticsearch
|
2
|
+
# ===============================
|
3
|
+
#
|
4
|
+
# https://github.com/soveran/ohm#example
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
7
|
+
|
8
|
+
require 'pry'
|
9
|
+
Pry.config.history.file = File.expand_path('../../tmp/elasticsearch_development.pry', __FILE__)
|
10
|
+
|
11
|
+
require 'logger'
|
12
|
+
require 'ansi/core'
|
13
|
+
require 'active_model'
|
14
|
+
require 'ohm'
|
15
|
+
|
16
|
+
require 'elasticsearch/model'
|
17
|
+
|
18
|
+
class Article < Ohm::Model
|
19
|
+
# Include JSON serialization from ActiveModel
|
20
|
+
include ActiveModel::Serializers::JSON
|
21
|
+
|
22
|
+
attribute :title
|
23
|
+
attribute :published_at
|
24
|
+
end
|
25
|
+
|
26
|
+
# Extend the model with Elasticsearch support
|
27
|
+
#
|
28
|
+
Article.__send__ :include, Elasticsearch::Model
|
29
|
+
|
30
|
+
# Register a custom adapter
|
31
|
+
#
|
32
|
+
module Elasticsearch
|
33
|
+
module Model
|
34
|
+
module Adapter
|
35
|
+
module Ohm
|
36
|
+
Adapter.register self,
|
37
|
+
lambda { |klass| defined?(::Ohm::Model) and klass.ancestors.include?(::Ohm::Model) }
|
38
|
+
module Records
|
39
|
+
def records
|
40
|
+
klass.fetch(@ids)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Configure the Elasticsearch client to log operations
|
49
|
+
#
|
50
|
+
Elasticsearch::Model.client = Elasticsearch::Client.new log: true
|
51
|
+
|
52
|
+
puts '', '-'*Pry::Terminal.width!
|
53
|
+
|
54
|
+
Article.all.map { |a| a.delete }
|
55
|
+
Article.create id: '1', title: 'Foo'
|
56
|
+
Article.create id: '2', title: 'Bar'
|
57
|
+
Article.create id: '3', title: 'Foo Foo'
|
58
|
+
|
59
|
+
Article.__elasticsearch__.client.indices.delete index: 'articles' rescue nil
|
60
|
+
Article.__elasticsearch__.client.bulk index: 'articles',
|
61
|
+
type: 'article',
|
62
|
+
body: Article.all.map { |a| { index: { _id: a.id, data: a.attributes } } },
|
63
|
+
refresh: true
|
64
|
+
|
65
|
+
|
66
|
+
response = Article.search 'foo', index: 'articles', type: 'article';
|
67
|
+
|
68
|
+
Pry.start(binding, prompt: lambda { |obj, nest_level, _| '> ' },
|
69
|
+
input: StringIO.new('response.records.to_a'),
|
70
|
+
quiet: true)
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# Riak and Elasticsearch
|
2
|
+
# ======================
|
3
|
+
#
|
4
|
+
# https://github.com/basho-labs/ripple
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
7
|
+
|
8
|
+
require 'pry'
|
9
|
+
Pry.config.history.file = File.expand_path('../../tmp/elasticsearch_development.pry', __FILE__)
|
10
|
+
|
11
|
+
require 'logger'
|
12
|
+
require 'ripple'
|
13
|
+
|
14
|
+
require 'elasticsearch/model'
|
15
|
+
|
16
|
+
# Documents are stored as JSON objects in Riak but have rich
|
17
|
+
# semantics, including validations and associations.
|
18
|
+
class Article
|
19
|
+
include Ripple::Document
|
20
|
+
|
21
|
+
property :title, String
|
22
|
+
property :published_at, Time, :default => proc { Time.now }
|
23
|
+
end
|
24
|
+
|
25
|
+
# Extend the model with Elasticsearch support
|
26
|
+
#
|
27
|
+
Article.__send__ :include, Elasticsearch::Model
|
28
|
+
|
29
|
+
# Create documents in Riak
|
30
|
+
#
|
31
|
+
Article.destroy_all
|
32
|
+
Article.create id: '1', title: 'Foo'
|
33
|
+
Article.create id: '2', title: 'Bar'
|
34
|
+
Article.create id: '3', title: 'Foo Foo'
|
35
|
+
|
36
|
+
# Index data into Elasticsearch
|
37
|
+
#
|
38
|
+
client = Elasticsearch::Client.new log:true
|
39
|
+
|
40
|
+
client.indices.delete index: 'articles' rescue nil
|
41
|
+
client.bulk index: 'articles',
|
42
|
+
type: 'article',
|
43
|
+
body: Article.all.map { |a|
|
44
|
+
{ index: { _id: a.key, data: JSON.parse(a.robject.raw_data) } }
|
45
|
+
}.as_json,
|
46
|
+
refresh: true
|
47
|
+
|
48
|
+
response = Article.search 'foo';
|
49
|
+
|
50
|
+
Pry.start(binding, prompt: lambda { |obj, nest_level, _| '> ' },
|
51
|
+
input: StringIO.new('response.records.to_a'),
|
52
|
+
quiet: true)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# Usage:
|
2
|
+
#
|
3
|
+
# $ BUNDLE_GEMFILE=./gemfiles/3.0.gemfile bundle install
|
4
|
+
# $ BUNDLE_GEMFILE=./gemfiles/3.0.gemfile bundle exec rake test:integration
|
5
|
+
|
6
|
+
source 'https://rubygems.org'
|
7
|
+
|
8
|
+
gemspec path: '../'
|
9
|
+
|
10
|
+
gem 'activemodel', '>= 3.0'
|
11
|
+
gem 'activerecord', '~> 3.2'
|
12
|
+
gem 'mongoid', '>= 3.0'
|