Class: Sensei::Client
- Inherits:
-
Object
- Object
- Sensei::Client
- Defined in:
- lib/sensei/client.rb
Constant Summary collapse
- DATA_TRANSACTION_KEY =
"sensei_client_data_transaction"
- TEST_TRANSACTION_KEY =
"sensei_client_test_transaction"
- DEFAULT_FACET_OPTIONS =
{:max => 6, :minCount => 1}
Instance Attribute Summary collapse
-
#search_timeout ⇒ Object
Returns the value of attribute search_timeout.
Class Method Summary collapse
- .begin_transaction(key) ⇒ Object
- .configure(path = "config/sensei.yml") {|_self| ... } ⇒ Object
- .construct(options = {}, &block) ⇒ Object
- .current_data_transaction ⇒ Object
- .current_test_transaction ⇒ Object
- .delete(uids) ⇒ Object
- .in_data_transaction? ⇒ Boolean
- .in_sensei_transaction?(key) ⇒ Boolean
- .in_test_transaction? ⇒ Boolean
- .kafka_commit(items) ⇒ Object
-
.kafka_rollback(data_events) ⇒ Object
Undo all of the data events that just occurred.
- .kafka_send(items) ⇒ Object
- .q(h) ⇒ Object
- .sensei_url ⇒ Object
- .test_transaction(&block) ⇒ Object
-
.transaction(&block) ⇒ Object
This does a “data transaction,” in which any update events will get buffered until the block is finished, after which everything gets sent.
- .update(documents) ⇒ Object
Instance Method Summary collapse
- #all(q) ⇒ Object
- #any(q) ⇒ Object
-
#facet(field, options = {}) ⇒ Object
Add a desired facet to the results.
-
#facet_requests ⇒ Object
This method builds the requests necessary to perform the ‘select_search’ method.
-
#initialize(optargs = {}) ⇒ Client
constructor
A new instance of Client.
- #not(q) ⇒ Object
- #options(opts = {}) ⇒ Object
- #query(q) ⇒ Object
- #relevance(r) ⇒ Object
- #search ⇒ Object
-
#select_search ⇒ Object
This method performs several separate queries with different selection settings as necessary.
-
#selection(fields = {}) ⇒ Object
Do facet selection.
- #to_h ⇒ Object
Constructor Details
#initialize(optargs = {}) ⇒ Client
Returns a new instance of Client.
96 97 98 99 100 101 |
# File 'lib/sensei/client.rb', line 96 def initialize optargs={} @query = optargs[:query].try(:to_sensei) @facets = (optargs[:facets] || {}) @selections = (optargs[:selections] || {}) @other_options = optargs.dup.keep_if {|k,v| ![:query, :facets, :selections].member?(k)} end |
Instance Attribute Details
#search_timeout ⇒ Object
Returns the value of attribute search_timeout.
10 11 12 |
# File 'lib/sensei/client.rb', line 10 def search_timeout @search_timeout end |
Class Method Details
.begin_transaction(key) ⇒ Object
43 44 45 46 |
# File 'lib/sensei/client.rb', line 43 def self.begin_transaction key Thread.current[key] ||= [] Thread.current[key] << [] end |
.configure(path = "config/sensei.yml") {|_self| ... } ⇒ Object
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/sensei/client.rb', line 15 def self.configure(path = "config/sensei.yml") if File.exists? path config = YAML.load(ERB.new(File.read(path)).result) # Limit config to specific environment if Rails is defined defined? ::Rails and config = config[::Rails.env] self.sensei_hosts = config['sensei_hosts'] self.sensei_port = config['sensei_port'] self.http_kafka_port = config['http_kafka_port'] self.uid_key = config['uid_key'] self.http_kafka_hosts = config['http_kafka_hosts'] self.fake_update = config['fake_update'] || false end yield self if block_given? end |
.construct(options = {}, &block) ⇒ Object
200 201 202 203 204 |
# File 'lib/sensei/client.rb', line 200 def self.construct ={}, &block out = self.new() search_query = class_eval(&block) out.query(search_query) end |
.current_data_transaction ⇒ Object
35 36 37 |
# File 'lib/sensei/client.rb', line 35 def self.current_data_transaction Thread.current[DATA_TRANSACTION_KEY].last end |
.current_test_transaction ⇒ Object
39 40 41 |
# File 'lib/sensei/client.rb', line 39 def self.current_test_transaction Thread.current[TEST_TRANSACTION_KEY].last end |
.delete(uids) ⇒ Object
125 126 127 128 129 |
# File 'lib/sensei/client.rb', line 125 def self.delete uids kafka_send(uids.map do |uid| {:type => 'delete', :uid => uid.to_s} end) end |
.in_data_transaction? ⇒ Boolean
83 84 85 |
# File 'lib/sensei/client.rb', line 83 def self.in_data_transaction? self.in_sensei_transaction? DATA_TRANSACTION_KEY end |
.in_sensei_transaction?(key) ⇒ Boolean
48 49 50 51 |
# File 'lib/sensei/client.rb', line 48 def self.in_sensei_transaction? key Thread.current[key] ||= [] Thread.current[key].count > 0 end |
.in_test_transaction? ⇒ Boolean
87 88 89 |
# File 'lib/sensei/client.rb', line 87 def self.in_test_transaction? self.in_sensei_transaction? TEST_TRANSACTION_KEY end |
.kafka_commit(items) ⇒ Object
116 117 118 119 120 121 122 123 |
# File 'lib/sensei/client.rb', line 116 def self.kafka_commit items if !fake_update req = Curl::Easy.new("http://#{http_kafka_hosts.sample}:#{http_kafka_port}/") req.http_post(items.map(&:to_json).join("\n")) raise Sensei::HTTPBadResponse, "Kafka url=#{req.url}, response_code=#{req.response_code}, response_body=#{req.body_str}" if req.response_code != 200 req.body_str end end |
.kafka_rollback(data_events) ⇒ Object
Undo all of the data events that just occurred. This is only really useful during tests. Also, it’s only capable of rolling back insertions.
78 79 80 81 |
# File 'lib/sensei/client.rb', line 78 def self.kafka_rollback(data_events) to_delete = data_events.select{|x| x[uid_key]}.map{|x| {:_type => '_delete', :_uid => x[uid_key]}} kafka_commit to_delete end |
.kafka_send(items) ⇒ Object
103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/sensei/client.rb', line 103 def self.kafka_send items if in_data_transaction? current_data_transaction << items else kafka_commit items end if in_test_transaction? Thread.current[TEST_TRANSACTION_KEY].last << items end true end |
.q(h) ⇒ Object
196 197 198 |
# File 'lib/sensei/client.rb', line 196 def self.q h h.to_sensei end |
.sensei_url ⇒ Object
91 92 93 94 |
# File 'lib/sensei/client.rb', line 91 def self.sensei_url raise unless sensei_hosts "http://#{sensei_hosts.sample}:#{sensei_port || 8080}/sensei" end |
.test_transaction(&block) ⇒ Object
65 66 67 68 69 70 71 72 73 |
# File 'lib/sensei/client.rb', line 65 def self.test_transaction &block begin begin_transaction TEST_TRANSACTION_KEY block.call ensure kafka_rollback(current_test_transaction) Thread.current[TEST_TRANSACTION_KEY].pop end end |
.transaction(&block) ⇒ Object
This does a “data transaction,” in which any update events will get buffered until the block is finished, after which everything gets sent.
55 56 57 58 59 60 61 62 63 |
# File 'lib/sensei/client.rb', line 55 def self.transaction &block begin begin_transaction DATA_TRANSACTION_KEY block.call kafka_commit(current_data_transaction) ensure Thread.current[DATA_TRANSACTION_KEY].pop end end |
.update(documents) ⇒ Object
131 132 133 |
# File 'lib/sensei/client.rb', line 131 def self.update(documents) kafka_send documents end |
Instance Method Details
#all(q) ⇒ Object
153 154 155 156 |
# File 'lib/sensei/client.rb', line 153 def all(q) @query ? (@query &= q.to_sensei) : (@query = q.to_sensei) self end |
#any(q) ⇒ Object
158 159 160 161 |
# File 'lib/sensei/client.rb', line 158 def any(q) @query ? (@query |= q.to_sensei) : (@query = q.to_sensei) self end |
#facet(field, options = {}) ⇒ Object
Add a desired facet to the results
138 139 140 141 |
# File 'lib/sensei/client.rb', line 138 def facet(field, ={}) @facets[field] = DEFAULT_FACET_OPTIONS.merge() self end |
#facet_requests ⇒ Object
This method builds the requests necessary to perform the ‘select_search’ method.
234 235 236 237 238 239 240 241 |
# File 'lib/sensei/client.rb', line 234 def facet_requests @selections.map do |field, values| Sensei::Client.new(:query => @query, :facets => @facets.dup.keep_if {|name, opts| name==field}, :selections => @selections.dup.keep_if {|name, opts| name != field}, :size => 0) end end |
#not(q) ⇒ Object
163 164 165 166 |
# File 'lib/sensei/client.rb', line 163 def not(q) @query ? (@query &= q.to_sensei.must_not) : (@query = q.to_sensei.must_not) self end |
#options(opts = {}) ⇒ Object
174 175 176 177 |
# File 'lib/sensei/client.rb', line 174 def (opts = {}) @other_options.merge!(opts) self end |
#query(q) ⇒ Object
148 149 150 151 |
# File 'lib/sensei/client.rb', line 148 def query(q) @query=q.to_sensei self end |
#relevance(r) ⇒ Object
143 144 145 146 |
# File 'lib/sensei/client.rb', line 143 def relevance(r) @relevance = r self end |
#search ⇒ Object
206 207 208 209 210 211 212 |
# File 'lib/sensei/client.rb', line 206 def search req = Curl::Easy.new(self.class.sensei_url) req.timeout = self.search_timeout req.http_post(self.to_h.to_json) raise Sensei::HTTPBadResponse, "url=#{req.url}, response_code=#{req.response_code}, response_body=#{req.body_str}" if req.response_code != 200 JSON.parse(req.body_str) end |
#select_search ⇒ Object
This method performs several separate queries with different selection settings as necessary. This is needed to perform the common interaction pattern for faceted search, in which it is desired that selections from other facets affect a particular facet’s counts, but a facet’s own selections do facet do not affect its own counts.
220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/sensei/client.rb', line 220 def select_search all_selection_results = search facet_requests.map(&:search).each do |result| field, counts = result['facets'].first all_selection_results['facets'][field] += counts end all_selection_results['facets'] = Hash[*all_selection_results['facets'].map do |k,v| [k, v.uniq_by{|x| x['value']}] end.flatten(1)] all_selection_results end |
#selection(fields = {}) ⇒ Object
Do facet selection
169 170 171 172 |
# File 'lib/sensei/client.rb', line 169 def selection(fields = {}) @selections.merge!(fields) self end |
#to_h ⇒ Object
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/sensei/client.rb', line 179 def to_h out = {} if @query out[:query] = @query.to_h if @relevance out[:query] = Sensei::BoolQuery.new(:operands => [@query], :operation => :must).to_h out[:query][:bool][:relevance] = @relevance end end (out[:facets] = @facets) if @facets.count > 0 selections = @selections.map { |field, terms| {:terms => {field => {values: terms, :operator => "or"}}} } (out[:selections] = selections) if selections.count > 0 out.merge!(@other_options) out end |