Class: Elastic

Inherits:
Object
  • Object
show all
Defined in:
lib/co-elastic-query.rb

Defined Under Namespace

Classes: Query

Constant Summary collapse

HOST =
if ENV['ELASTIC']
    ENV['ELASTIC'].split(' ').map {|item| "#{item}:9200"}
else
    ['localhost:9200']
end
COUNT =
'count'.freeze
HITS =
'hits'.freeze
TOTAL =
'total'.freeze
ID =
'_id'.freeze
SCORE =
['_score'.freeze]
INDEX =
(ENV['ELASTIC_INDEX'] || 'default').freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(klass = nil, **opts) ⇒ Elastic

Returns a new instance of Elastic.



322
323
324
325
326
327
328
329
# File 'lib/co-elastic-query.rb', line 322

def initialize(klass = nil, **opts)
    if klass
        @klass = klass
        @filter = klass.design_document
    end
    @index = opts[:index] || INDEX
    @use_couch_type = opts[:use_couch_type] || false
end

Class Method Details

.clientObject



307
308
309
# File 'lib/co-elastic-query.rb', line 307

def self.client
    @@client
end

.count(*args) ⇒ Object



303
304
305
# File 'lib/co-elastic-query.rb', line 303

def self.count *args
    @@client.count *args
end

.new_clientObject



311
312
313
# File 'lib/co-elastic-query.rb', line 311

def self.new_client
  @@client = Elasticsearch::Client.new hosts: HOST, reload_connections: true, adapter: :libuv
end

.search(*args) ⇒ Object



299
300
301
# File 'lib/co-elastic-query.rb', line 299

def self.search *args
    @@client.search *args
end

Instance Method Details

#count(builder) ⇒ Object



368
369
370
371
372
373
374
375
376
377
# File 'lib/co-elastic-query.rb', line 368

def count(builder)
    query = generate_body(builder)

    # Simplify the query
    query[:body].delete(:from)
    query[:body].delete(:size)
    query[:body].delete(:sort)

    Elastic.count(query)[COUNT]
end

#generate_body(builder) ⇒ Object



380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
# File 'lib/co-elastic-query.rb', line 380

def generate_body(builder)
    opt = builder.build

    sort = (opt[:sort] || []) + SCORE

    queries = opt[:queries] || []
    queries.unshift(opt[:query])

    filters = opt[:filters] || []

    if @filter
        if @use_couch_type
            filters.unshift({term: {'doc.type' => @filter}})
        else
            filters.unshift({type: {value: @filter}})
        end
    end

    {
        index: @index,
        body: {
            sort: sort,
            query: {
                bool: {
                    must: {
                        query: {
                            bool: {
                                must: queries
                            }
                        }
                    },
                    filter: {
                        bool: {
                            must: filters
                        }
                    }
                }
            },
            from: opt[:offset],
            size: opt[:limit]
        }
    }
end

#query(params = nil, filters = nil) ⇒ Object

Safely build the query



332
333
334
335
336
# File 'lib/co-elastic-query.rb', line 332

def query(params = nil, filters = nil)
    builder = ::Elastic::Query.new(params)
    builder.filter(filters) if filters
    builder
end

#search(builder, &block) ⇒ Object



338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/co-elastic-query.rb', line 338

def search(builder, &block)
    query = generate_body(builder)

    # if a formatter block is supplied, each loaded record is passed to it
    # allowing annotation/conversion of records using data from the model
    # and current request (e.g groups are annotated with 'admin' if the
    # currently logged in user is an admin of the group). nils are removed
    # from the list.
    result = Elastic.search(query)
    records = if @klass
        Array(@klass.find_by_id(result[HITS][HITS].map {|entry| entry[ID]}))
    else
        Array(result[HITS][HITS].map { |entry| ::CouchbaseOrm.try_load(entry[ID]) }).compact
    end
    results = block_given? ? (records.map {|record| yield record}).compact : records

    # Ensure the total is accurate
    total = result[HITS][TOTAL] || 0
    total = total - (records.length - results.length) # adjust for compaction

    # We check records against limit (pre-compaction) and total against actual result length
    # Worst case senario is there is one additional request for results at an offset that returns no results.
    # The total results number will be accurate on the final page of results from the clients perspective.
    total = results.length + builder.offset if records.length < builder.limit && total > (results.length + builder.offset)
    {
        total: total,
        results: results
    }
end