Skip to content

Commit 4b68c48

Browse files
committed
Support Cequel adapter
1 parent 835ea07 commit 4b68c48

File tree

3 files changed

+183
-0
lines changed

3 files changed

+183
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Cequel and Elasticsearch
2+
# ==================================
3+
#
4+
# https://github.com/cequel/cequel
5+
6+
7+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
8+
9+
require 'pry'
10+
Pry.config.history.file = File.expand_path('/tmp/elasticsearch_development.pry', __FILE__)
11+
12+
require 'benchmark'
13+
require 'logger'
14+
15+
require 'ansi/core'
16+
require 'cequel'
17+
18+
require 'elasticsearch/model'
19+
require 'elasticsearch/model/callbacks'
20+
21+
require 'rake'
22+
23+
# Load default tasks from Cequel
24+
#
25+
spec = Gem::Specification.find_by_name 'cequel'
26+
load "#{spec.gem_dir}/lib/cequel/record/tasks.rb"
27+
28+
# Cassandra connection settings
29+
#
30+
cequel_config = {
31+
host: '127.0.0.1',
32+
port: 9042,
33+
keyspace: 'cequel_test',
34+
max_retries: 3,
35+
retry_delay: 1,
36+
replication: {
37+
class: 'SimpleStrategy',
38+
replication_factor: 1
39+
}
40+
}
41+
42+
# Elastic config
43+
#
44+
elastic_config = {
45+
host: 'localhost:9200',
46+
log: true
47+
}
48+
49+
connection = Cequel.connect cequel_config
50+
Cequel::Record.connection = connection
51+
52+
Elasticsearch::Model.client = Elasticsearch::Client.new elastic_config
53+
54+
55+
class Article
56+
include Cequel::Record
57+
58+
key :id, :int
59+
60+
column :title, :text
61+
column :published_at, :timestamp
62+
63+
def as_indexed_json(options = {})
64+
as_json(except: [:id, :_id])
65+
end
66+
end
67+
68+
Article.__send__ :include, Elasticsearch::Model
69+
Article.__send__ :include, Elasticsearch::Model::Callbacks
70+
71+
# Initialize Cassandra and synchronize schema
72+
#
73+
Rake.application['cequel:reset'].invoke
74+
Article.synchronize_schema
75+
76+
Article.delete_all
77+
Article.new(id: 1, title: 'Foo').save!
78+
Article.new(id: 2, title: 'Bar').save!
79+
80+
client = Elasticsearch::Client.new elastic_config
81+
82+
client.indices.delete index: 'articles' rescue nil
83+
84+
85+
client.bulk index: 'articles',
86+
type: 'article',
87+
body: Article.all.map { |a| { index: { _id: a.id, data: a.attributes } } },
88+
refresh: true
89+
90+
Article.new(id: 3, title: 'Foo Bar').save!
91+
92+
response = Article.search 'bar'
93+
#x = response.records
94+
#puts x.class
95+
#puts x.to_a.to_s
96+
97+
98+
#puts x.records.where({ :id => [3] })
99+
100+
# puts Benchmark.realtime { 9_875.times { |i| Article.new( id: i, title: "Foo #{i}").save! } }
101+
102+
Pry.start(binding, prompt: lambda { |obj, nest_level, _| '> ' },
103+
input: StringIO.new('response.records.to_a'),
104+
quiet: true)

elasticsearch-model/lib/elasticsearch/model.rb

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
require 'elasticsearch/model/adapter'
3232
require 'elasticsearch/model/adapters/default'
3333
require 'elasticsearch/model/adapters/active_record'
34+
require 'elasticsearch/model/adapters/cequel'
3435
require 'elasticsearch/model/adapters/mongoid'
3536
require 'elasticsearch/model/adapters/multiple'
3637

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
module Elasticsearch
2+
module Model
3+
module Adapter
4+
5+
# An adapter for Cequel-based models
6+
#
7+
# @see https://github.com/cequel/cequel
8+
#
9+
module Cequel
10+
11+
Adapter.register self, lambda { |klass| !!defined?(::Cequel::Record) && klass.respond_to?(:ancestors) && klass.ancestors.include?(::Cequel::Record) }
12+
13+
module Records
14+
15+
# Return a `Cequel::RecordSet` instance
16+
#
17+
def records
18+
pk = klass.key_column_names[0]
19+
res = klass.where(pk => ids)
20+
21+
res.instance_exec(response.response['hits']['hits']) do |hits|
22+
define_singleton_method :to_a do
23+
self.entries.sort_by do |e|
24+
hits.index do |hit|
25+
hit['_id'].to_s == e.id.to_s
26+
end
27+
end
28+
end
29+
end
30+
31+
return res
32+
end
33+
end
34+
35+
module Callbacks
36+
37+
# Handle index updates (creating, updating or deleting documents)
38+
# when the model changes, by hooking into the lifecycle
39+
#
40+
# @see https://github.com/cequel/cequel/blob/master/lib/cequel/record/callbacks.rb
41+
#
42+
def self.included(base)
43+
[:save, :create, :update].each do |item|
44+
base.send("after_#{ item }", lambda { __elasticsearch__.index_document })
45+
end
46+
47+
base.after_destroy { __elasticsearch__.delete_document }
48+
end
49+
end
50+
51+
module Importing
52+
# Fetch batches of records from the database (used by the import method)
53+
#
54+
# @see http://api.rubyonrails.org/classes/ActiveRecord/Batches.html ActiveRecord::Batches.find_in_batches
55+
#
56+
def __find_in_batches(options={}, &block)
57+
query = options.delete(:query)
58+
named_scope = options.delete(:scope)
59+
preprocess = options.delete(:preprocess)
60+
61+
scope = self
62+
scope = scope.__send__(named_scope) if named_scope
63+
scope = scope.instance_exec(&query) if query
64+
65+
scope.find_in_batches(**options) do |batch|
66+
batch = self.__send__(preprocess, batch) if preprocess
67+
yield(batch) if batch.present?
68+
end
69+
end
70+
71+
def __transform
72+
lambda { |model| { index: { _id: model.id, data: model.__elasticsearch__.as_indexed_json } } }
73+
end
74+
end
75+
end
76+
end
77+
end
78+
end

0 commit comments

Comments
 (0)