Class: Picky::Query::Indexes

Inherits:
Object
  • Object
show all
Defined in:
lib/picky/query/indexes.rb,
lib/picky/query/indexes/check.rb

Overview

The query indexes class bundles indexes given to a query.

Example:

# If you call
Search.new dvd_index, mp3_index, video_index

# What it does is take the three given (API-) indexes and
# bundle them in an index bundle.

Defined Under Namespace

Classes: Check, DifferentBackendsError

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*indexes) ⇒ Indexes

Creates a new Query::Indexes.

Its job is to generate all possible combinations. Note: We cannot mix memory and redis indexes just yet.



28
29
30
31
32
# File 'lib/picky/query/indexes.rb', line 28

def initialize *indexes
  Check.check_backends indexes

  @indexes = indexes
end

Instance Attribute Details

#exclusive_allocationsObject (readonly)

Returns the value of attribute exclusive_allocations.



18
19
20
# File 'lib/picky/query/indexes.rb', line 18

def exclusive_allocations
  @exclusive_allocations
end

#ignored_allocationsObject (readonly)

Returns the value of attribute ignored_allocations.



18
19
20
# File 'lib/picky/query/indexes.rb', line 18

def ignored_allocations
  @ignored_allocations
end

#ignored_categoriesObject (readonly)

Returns the value of attribute ignored_categories.



18
19
20
# File 'lib/picky/query/indexes.rb', line 18

def ignored_categories
  @ignored_categories
end

#indexesObject (readonly)

Returns the value of attribute indexes.



18
19
20
# File 'lib/picky/query/indexes.rb', line 18

def indexes
  @indexes
end

Instance Method Details

#allocation_for(tokens, index) ⇒ Object



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/picky/query/indexes.rb', line 110

def allocation_for tokens, index
  # Expand the combinations.
  #
  possible_combinations = tokens.possible_combinations_in index.categories

  # Generate all possible combinations.
  #
  all_possible_combinations = expand_combinations_from possible_combinations

  # Add the wrapped possible allocations to the ones we already have.
  #
  all_possible_combinations.map! do |expanded_combinations|
    Allocation.new index, Query::Combinations.new(expanded_combinations)
  end
end

#allocations_ary_for(tokens) ⇒ Object



105
106
107
108
109
# File 'lib/picky/query/indexes.rb', line 105

def allocations_ary_for tokens
  indexes.inject([]) do |allocations, index|
    allocations + allocation_for(tokens, index)
  end
end

#allocations_for(tokens) ⇒ Object

Returns a number of possible allocations for the given tokens.



102
103
104
# File 'lib/picky/query/indexes.rb', line 102

def allocations_for tokens
  Allocations.new allocations_ary_for(tokens)
end

#expand_combinations_from(possible_combinations) ⇒ Object

This is the core of the search engine. No kidding.

Gets an array of [

[<combinations for token1>],
[<combinations for token2>],
[<combinations for token3>]

]

Generates all possible allocations of combinations. [

[first combination of token1, first c of t2, first c of t3],
[first combination of token1, first c of t2, second c of t3]
...

]

Generates all possible combinations of array elements:

1,2,3

x [a,b,c] x [k,l,m] => [[1,a,k], [1,a,l], [1,a,m], [1,b,k], [1,b,l], [1,b,m], [1,c,k], …, [3,c,m]]

Note: Also calculates the weights and sorts them accordingly.

Note: This is a heavily optimized ruby version.

Works like this:

1,2,3], [a,b,c], [k,l,m

are expanded to

group mult: 1
<- single mult ->
1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3

27 elements

group mult: 3
<- -> s/m
a,a,a,b,b,b,c,c,c,a,a,a,b,b,b,c,c,c,a,a,a,b,b,b,c,c,c

27 elements

group mult: 9
<> s/m
k,l,m,k,l,m,k,l,m,k,l,m,k,l,m,k,l,m,k,l,m,k,l,m,k,l,m

27 elements

It is then recombined, where [

[a,a,b,b,c,c]
[d,e,d,e,d,e]

] becomes [

[a,d],
[a,e],
[b,d],
[b,e],
[c,d],
[c,e]

]

Note: Not using transpose as it is slower.

Returns nil if there are no combinations.

Note: Of course I could split this method up into smaller

ones, but I guess I am a bit sentimental.


182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/picky/query/indexes.rb', line 182

def expand_combinations_from possible_combinations
  # If an element has size 0, this means one of the
  # tokens could not be allocated.
  #
  return [] if possible_combinations.any? { |possible_combination| possible_combination.empty? }

  # Generate the first multiplicator "with which" (well, not quite) to multiply the smallest amount of combinations.
  #
  single_mult = possible_combinations.inject(1) { |total, combinations| total * combinations.size }

  # Initialize a group multiplicator.
  #
  group_mult = 1

  # The expanding part to line up the combinations
  # for later combination in allocations.
  #
  possible_combinations.collect! do |combinations|

    # Get the size of the combinations of the first token.
    #
    combinations_size = combinations.size

    # Special case: If there is no combination for one of the tokens.
    #               In that case, we just use the same single mult for
    #               the next iteration.
    #               If there are combinations, we divide the single mult
    #               by the number of combinations.
    #
    single_mult /= combinations_size unless combinations_size.zero?

    # Expand each combination by the single mult:
    # [a,b,c]
    # [a,a,a,  b,b,b,  c,c,c]
    # Then, expand the result by the group mult:
    # [a,a,a,b,b,b,c,c,c,  a,a,a,b,b,b,c,c,c,  a,a,a,b,b,b,c,c,c]
    #
    combinations = combinations.inject([]) do |total, combination|
      total + Array.new(single_mult, combination)
    end * group_mult

    # Multiply the group mult by the combinations size,
    # since the next combinations' single mult is smaller
    # and we need to adjust for that.
    #
    group_mult = group_mult * combinations_size

    # Return the combinations.
    #
    combinations
  end

  return [] if possible_combinations.empty?

  possible_combinations.shift.zip *possible_combinations
end

#ignore_allocations(*qualifier_arrays) ⇒ Object

Ignore the allocations with the given qualifiers.



45
46
47
48
49
50
51
# File 'lib/picky/query/indexes.rb', line 45

def ignore_allocations *qualifier_arrays
  @ignored_allocations ||= []
  @ignored_allocations += qualifier_arrays #.map do |qualifier_array|
    # qualifier_array.map { |qualifier| @qualifier_mapper.map qualifier }
  # end.compact
  @ignored_allocations.uniq!
end

#ignore_categories(*qualifiers) ⇒ Object

Ignore the categories with the given qualifiers.



36
37
38
39
40
41
# File 'lib/picky/query/indexes.rb', line 36

def ignore_categories *qualifiers
  @ignored_categories ||= []
  # @ignored_categories += qualifiers.map { |qualifier| @qualifier_mapper.map qualifier }.compact
  @ignored_categories += qualifiers
  @ignored_categories.uniq!
end

#keep_allocations(*qualifier_arrays) ⇒ Object

Exclusively keep the allocations with the given qualifiers.



55
56
57
58
59
60
61
# File 'lib/picky/query/indexes.rb', line 55

def keep_allocations *qualifier_arrays
  @exclusive_allocations ||= []
  @exclusive_allocations += qualifier_arrays #.map do |qualifier_array|
    # qualifier_array.map { |qualifier| @qualifier_mapper.map qualifier }
  # end.compact
  @exclusive_allocations.uniq!
end

#prepared_allocations_for(tokens, boosts = {}, amount = nil) ⇒ Object

Returns a number of prepared (sorted, reduced etc.) allocations for the given tokens.



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/picky/query/indexes.rb', line 65

def prepared_allocations_for tokens, boosts = {}, amount = nil
  allocations = allocations_for tokens

  # Score the allocations using weights as bias.
  #
  # Note: Before we can sort them we need to score them.
  #
  allocations.calculate_score boosts

  # Filter the allocations – ignore/only.
  #
  allocations.keep_allocations exclusive_allocations if exclusive_allocations
  allocations.remove_allocations ignored_allocations if ignored_allocations

  # Sort the allocations.
  # (allocations are sorted according to score, highest to lowest)
  #
  # Before we can chop off unimportant allocations, we need to sort them.
  #
  allocations.sort!
  
  # allocations.remove_allocations ignored_allocations if ignored_allocations
  
  # Reduce the amount of allocations.
  #
  # Before we remove categories, we should reduce the amount of allocations.
  #
  allocations.reduce_to amount if amount

  # Remove categories from allocations.
  #
  allocations.remove_categories ignored_categories if ignored_categories

  allocations
end