Class: Elastictastic::Scope

Inherits:
BasicObject
Defined in:
lib/elastictastic/scope.rb

Direct Known Subclasses

ChildCollectionProxy

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(index, clazz, search = Search.new, parent = nil, routing = nil) ⇒ Scope

Returns a new instance of Scope.



8
9
10
# File 'lib/elastictastic/scope.rb', line 8

def initialize(index, clazz, search = Search.new, parent = nil, routing = nil)
  @index, @clazz, @search, @parent, @routing = index, clazz, search, parent, routing
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object



236
237
238
239
240
241
242
243
244
245
246
# File 'lib/elastictastic/scope.rb', line 236

def method_missing(method, *args, &block)
  if ::Enumerable.method_defined?(method)
    each.__send__(method, *args, &block)
  elsif @clazz.respond_to?(method)
    @clazz.with_scope(self) do
      @clazz.__send__(method, *args, &block)
    end
  else
    super
  end
end

Instance Attribute Details

#clazzObject (readonly)

Returns the value of attribute clazz.



6
7
8
# File 'lib/elastictastic/scope.rb', line 6

def clazz
  @clazz
end

#indexObject (readonly)

Returns the value of attribute index.



6
7
8
# File 'lib/elastictastic/scope.rb', line 6

def index
  @index
end

#routing(routing) ⇒ Object



230
231
232
233
234
# File 'lib/elastictastic/scope.rb', line 230

def routing(routing)
  scope = scoped({})
  scope.routing = routing
  scope
end

Instance Method Details

#[](index_or_range) ⇒ Object



113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/elastictastic/scope.rb', line 113

def [](index_or_range)
  case index_or_range
  when ::Integer
    from(index_or_range).size(1).to_a.first
  when ::Range
    range_size = index_or_range.last - index_or_range.first
    range_size += 1 unless index_or_range.exclude_end?
    from(index_or_range.first).
      size(range_size)
  else
    raise ::ArgumentError, "Expected Integer or Range"
  end
end

#allObject



109
110
111
# File 'lib/elastictastic/scope.rb', line 109

def all
  scoped({})
end

#all_facetsObject



127
128
129
130
131
# File 'lib/elastictastic/scope.rb', line 127

def all_facets
  return @all_facets if defined? @all_facets
  populate_counts
  @all_facets ||= nil
end

#any?(&block) ⇒ Boolean

Returns:

  • (Boolean)


95
96
97
# File 'lib/elastictastic/scope.rb', line 95

def any?(&block)
  block ? each.any?(&block) : !empty?
end

#countObject



85
86
87
88
89
# File 'lib/elastictastic/scope.rb', line 85

def count
  return @count if defined? @count
  populate_counts
  @count
end

#counts=(response) ⇒ Object



263
264
265
266
267
268
# File 'lib/elastictastic/scope.rb', line 263

def counts=(response)
  @count ||= response['hits']['total']
  if response['facets']
    @all_facets ||= ::Hashie::Mash.new(response['facets'])
  end
end

#destroy(*ids) ⇒ Object

Destroy one or more documents by ID, without reading them first



146
147
148
149
150
151
152
153
154
155
156
# File 'lib/elastictastic/scope.rb', line 146

def destroy(*ids)
  ids.each do |id|
    ::Elastictastic.persister.destroy!(
      @index,
      @clazz.type,
      id,
      @routing,
      (@parent.id if @parent)
    )
  end
end

#destroy_allObject

Note:

This will not take into account filters or queries in this scope.

Destroy all documents in this index.



163
164
165
166
# File 'lib/elastictastic/scope.rb', line 163

def destroy_all
  #FIXME support delete-by-query
  ::Elastictastic.client.delete(@index, @clazz.type)
end

#eachObject



21
22
23
24
25
26
27
# File 'lib/elastictastic/scope.rb', line 21

def each
  if ::Kernel.block_given?
    find_each { |result, hit| yield result }
  else
    ::Enumerator.new(self, :each)
  end
end

#empty?Boolean

Returns:

  • (Boolean)


91
92
93
# File 'lib/elastictastic/scope.rb', line 91

def empty?
  count == 0
end

#exists?(id) ⇒ Boolean

Returns:

  • (Boolean)


173
174
175
176
# File 'lib/elastictastic/scope.rb', line 173

def exists?(id)
  ::Elastictastic.client.
    exists?(index, type, id, params_for_find.slice('routing'))
end

#find(*ids) ⇒ Elastictastic::BasicDocument, Array #find(id) ⇒ Elastictastic::BasicDocument #find(ids) ⇒ Array

Look up one or more documents by ID.

Retrieve one or more Elastictastic documents by ID

Overloads:

  • #find(*ids) ⇒ Elastictastic::BasicDocument, Array

    Retrieve a single document or a collection of documents

    Parameters:

    • ids (String)

      Document IDs

    Returns:

  • #find(id) ⇒ Elastictastic::BasicDocument

    Retrieve a single Elastictastic document

    Parameters:

    • id (String)

      ID of the document

    Returns:

  • #find(ids) ⇒ Array

    Retrieve a collection of Elastictastic documents by ID. This will return an Array even if the ids argument is a one-element Array.

    Parameters:

    • ids (Array)

      Document IDs

    Returns:

    • (Array)

      Collection of documents with the given IDs



202
203
204
205
206
207
208
209
210
211
212
# File 'lib/elastictastic/scope.rb', line 202

def find(*ids)
  #TODO support combining this with other filters/query
  force_array = ::Array === ids.first
  ids = ids.flatten
  if ids.length == 1
    instance = find_one(ids.first)
    force_array ? [instance] : instance
  else
    find_many(ids)
  end
end

#find_each(batch_options = {}) {|document, hit| ... } ⇒ Enumerator

Iterate over all documents matching this scope. The underlying mechanism used differs depending on the construction of this scope:

  • If the scope has a size, documents will be retrieved in a single request

  • If the scope has a sort but no size, documents will be retrieved in batches using a ‘query_then_fetch` search. *In this case, it is impossible to guarantee a consistent result set if concurrent modification is occurring.*

  • If the scope has neither a sort nor a size, documents will be retrieved in batches using a cursor (search type ‘scan`). In this case, the result set is guaranteed to be consistent even if concurrent modification occurs.

Parameters:

  • batch_options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (batch_options):

  • :batch_size (Fixnum) — default: Elastictastic.config.default_batch_size

    How many documents to retrieve from the server in each batch.

  • :ttl (Fixnum) — default: 60

    How long to keep the cursor alive, in the case where search is performed with a cursor.

Yields:

  • (document, hit)

    Each result is yielded to the block

Yield Parameters:

  • document (Document)

    A materialized Document instance

  • hit (Hashie::Mash)

    The raw hit from ElasticSearch, wrapped in a Hashie::Mash. Useful for extracting metadata, e.g. highlighting

Returns:

  • (Enumerator)

    An enumerator, if no block is passed

See Also:



52
53
54
55
56
57
58
# File 'lib/elastictastic/scope.rb', line 52

def find_each(batch_options = {}, &block)
  if block
    find_in_batches(batch_options) { |batch| batch.each(&block) }
  else
    ::Enumerator.new(self, :find_each, batch_options)
  end
end

#find_in_batches(batch_options = {}) {|batch| ... } ⇒ Enumerator

Yield batches of documents matching this scope. See #find_each for a discussion of different strategies for retrieving documents from ElasticSearch depending on the construction of this scope.

Parameters:

  • batch_options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (batch_options):

  • :batch_size (Fixnum) — default: Elastictastic.config.default_batch_size

    How many documents to retrieve from the server in each batch.

  • :ttl (Fixnum) — default: 60

    How long to keep the cursor alive, in the case where search is performed with a cursor.

Yields:

  • (batch)

    Once for each batch of hits

Yield Parameters:

  • batch (Enumerator)

    An enumerator for this batch of hits. The enumerator will yield a materialized Document and a Hashie::Mash wrapping each raw hit.

Returns:

  • (Enumerator)

    An enumerator that yields batches, if no block is passed.



74
75
76
77
78
79
80
81
82
83
# File 'lib/elastictastic/scope.rb', line 74

def find_in_batches(batch_options = {}, &block)
  return ::Enumerator.new(self, :find_in_batches, batch_options) unless block
  if params.key?('size') || params.key?('from')
    yield search_all
  elsif params.key?('sort') || params.key('facets')
    search_in_batches(&block)
  else
    scan_in_batches(batch_options, &block)
  end
end

#find_many(ids, params = {}) ⇒ Object



288
289
290
291
292
293
294
295
296
# File 'lib/elastictastic/scope.rb', line 288

def find_many(ids, params = {})
  docspec = ids.map do |id|
    { '_id' => id }.merge!(params_for_find_many).
      merge!(params.stringify_keys)
  end
  materialize_hits(
    ::Elastictastic.client.mget(docspec, index, type)['docs']
  ).map { |result, hit| result }
end

#find_one(id, params = {}) ⇒ Object



273
274
275
276
277
278
279
280
281
282
283
# File 'lib/elastictastic/scope.rb', line 273

def find_one(id, params = {})
  data = ::Elastictastic.client.
    get(index, type, id, params_for_find_one.merge(params.stringify_keys))
  return nil if data['exists'] == false
  case data['status']
  when nil
    materialize_hit(data)
  when 404
    nil
  end
end

#firstObject



99
100
101
102
103
104
105
106
107
# File 'lib/elastictastic/scope.rb', line 99

def first
  params = from(0).size(1).params
  hit = ::Elastictastic.client.search(
    @index,
    @clazz.type,
    params
  )['hits']['hits'].first
  materialize_hit(hit) if hit
end

#initialize_instance(instance) ⇒ Object



12
13
14
15
# File 'lib/elastictastic/scope.rb', line 12

def initialize_instance(instance)
  index = @index
  instance.instance_eval { @index = index }
end

#inspectObject



248
249
250
# File 'lib/elastictastic/scope.rb', line 248

def inspect
  self.entries.inspect
end

#materialize_hit(hit) ⇒ Object



322
323
324
325
326
327
# File 'lib/elastictastic/scope.rb', line 322

def materialize_hit(hit)
  @clazz.new.tap do |result|
    result.parent = @parent if @parent
    result.elasticsearch_hit = hit
  end
end

#multi_get_paramsObject



298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/elastictastic/scope.rb', line 298

def multi_get_params
  {
    '_type' => type,
    '_index' => @index.name
  }.tap do |params|
    params['fields'] = ::Kernel.Array(@search['fields']) if @search['fields']
    if @routing
      params['routing'] = @routing
    elsif @clazz.routing_required?
      ::Kernel.raise ::Elastictastic::MissingParameter,
        "Must specify routing parameter to look up #{@clazz.name} by ID"
    end
  end
end

#multi_search_headersObject



313
314
315
316
317
# File 'lib/elastictastic/scope.rb', line 313

def multi_search_headers
  {'type' => type, 'index' => @index.name}.tap do |params|
    params['routing'] = @routing if @routing
  end
end

#paramsObject



17
18
19
# File 'lib/elastictastic/scope.rb', line 17

def params
  @search.params
end

#response=(response) ⇒ Object



255
256
257
258
# File 'lib/elastictastic/scope.rb', line 255

def response=(response)
  self.counts = response
  @materialized_hits = materialize_hits(response['hits']['hits'])
end

#scoped(params, index = @index) ⇒ Object



133
134
135
136
137
138
139
140
141
# File 'lib/elastictastic/scope.rb', line 133

def scoped(params, index = @index)
  ::Elastictastic::Scope.new(
    @index,
    @clazz,
    @search.merge(Search.new(params)),
    @parent,
    @routing
  )
end

#sync_mappingObject



168
169
170
171
# File 'lib/elastictastic/scope.rb', line 168

def sync_mapping
  #XXX is this a weird place to have this?
  ::Elastictastic.client.put_mapping(index, type, @clazz.mapping)
end