picky 2.7.0 → 3.0.0.pre1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/picky/adapters/rack/base.rb +20 -16
- data/lib/picky/adapters/rack/live_parameters.rb +28 -24
- data/lib/picky/adapters/rack/search.rb +67 -0
- data/lib/picky/adapters/rack.rb +27 -23
- data/lib/picky/application.rb +246 -236
- data/lib/picky/backend/base.rb +115 -119
- data/lib/picky/backend/file/basic.rb +102 -98
- data/lib/picky/backend/file/json.rb +27 -23
- data/lib/picky/backend/file/marshal.rb +32 -28
- data/lib/picky/backend/file/text.rb +45 -41
- data/lib/picky/backend/files.rb +19 -15
- data/lib/picky/backend/redis/basic.rb +76 -72
- data/lib/picky/backend/redis/list_hash.rb +40 -36
- data/lib/picky/backend/redis/string_hash.rb +30 -26
- data/lib/picky/backend/redis.rb +32 -28
- data/lib/picky/bundle.rb +82 -57
- data/lib/{bundling.rb → picky/bundling.rb} +0 -0
- data/lib/picky/calculations/location.rb +51 -47
- data/lib/picky/categories.rb +60 -56
- data/lib/picky/categories_indexed.rb +73 -82
- data/lib/picky/categories_indexing.rb +12 -8
- data/lib/picky/category.rb +109 -120
- data/lib/picky/category_indexed.rb +39 -41
- data/lib/picky/category_indexing.rb +123 -125
- data/lib/picky/character_substituters/west_european.rb +32 -26
- data/lib/{constants.rb → picky/constants.rb} +0 -0
- data/lib/picky/cores.rb +96 -92
- data/lib/{deployment.rb → picky/deployment.rb} +0 -0
- data/lib/picky/frontend_adapters/rack.rb +133 -118
- data/lib/picky/generators/aliases.rb +5 -3
- data/lib/picky/generators/base.rb +11 -7
- data/lib/picky/generators/partial/default.rb +7 -3
- data/lib/picky/generators/partial/none.rb +24 -20
- data/lib/picky/generators/partial/strategy.rb +20 -16
- data/lib/picky/generators/partial/substring.rb +94 -90
- data/lib/picky/generators/partial_generator.rb +11 -7
- data/lib/picky/generators/similarity/default.rb +9 -5
- data/lib/picky/generators/similarity/double_metaphone.rb +20 -16
- data/lib/picky/generators/similarity/metaphone.rb +20 -16
- data/lib/picky/generators/similarity/none.rb +23 -19
- data/lib/picky/generators/similarity/phonetic.rb +49 -45
- data/lib/picky/generators/similarity/soundex.rb +20 -16
- data/lib/picky/generators/similarity/strategy.rb +10 -6
- data/lib/picky/generators/similarity_generator.rb +11 -7
- data/lib/picky/generators/strategy.rb +14 -10
- data/lib/picky/generators/weights/default.rb +9 -5
- data/lib/picky/generators/weights/logarithmic.rb +30 -26
- data/lib/picky/generators/weights/strategy.rb +10 -6
- data/lib/picky/generators/weights_generator.rb +11 -7
- data/lib/picky/helpers/measuring.rb +20 -16
- data/lib/picky/indexed/bundle/base.rb +39 -37
- data/lib/picky/indexed/bundle/memory.rb +68 -64
- data/lib/picky/indexed/bundle/redis.rb +73 -69
- data/lib/picky/indexed/wrappers/bundle/calculation.rb +26 -22
- data/lib/picky/indexed/wrappers/bundle/location.rb +30 -26
- data/lib/picky/indexed/wrappers/bundle/wrapper.rb +36 -32
- data/lib/picky/indexed/wrappers/category/location.rb +17 -13
- data/lib/picky/indexed/wrappers/exact_first.rb +46 -42
- data/lib/picky/indexers/base.rb +26 -22
- data/lib/picky/indexers/parallel.rb +62 -58
- data/lib/picky/indexers/serial.rb +41 -37
- data/lib/picky/indexes/index.rb +400 -0
- data/lib/picky/indexes/index_indexed.rb +24 -0
- data/lib/picky/indexes/index_indexing.rb +138 -0
- data/lib/picky/indexes/memory.rb +20 -0
- data/lib/picky/indexes/redis.rb +20 -0
- data/lib/picky/indexes.rb +68 -61
- data/lib/picky/indexes_indexed.rb +16 -12
- data/lib/picky/indexes_indexing.rb +41 -37
- data/lib/picky/indexing/bundle/base.rb +216 -205
- data/lib/picky/indexing/bundle/memory.rb +16 -11
- data/lib/picky/indexing/bundle/redis.rb +14 -12
- data/lib/picky/indexing/wrappers/category/location.rb +17 -13
- data/lib/picky/interfaces/live_parameters.rb +159 -154
- data/lib/picky/loader.rb +267 -304
- data/lib/picky/loggers/search.rb +20 -13
- data/lib/picky/no_source_specified_exception.rb +7 -3
- data/lib/picky/performant.rb +6 -2
- data/lib/picky/query/allocation.rb +71 -67
- data/lib/picky/query/allocations.rb +99 -94
- data/lib/picky/query/combination.rb +70 -66
- data/lib/picky/query/combinations/base.rb +56 -52
- data/lib/picky/query/combinations/memory.rb +36 -32
- data/lib/picky/query/combinations/redis.rb +66 -62
- data/lib/picky/query/indexes.rb +175 -160
- data/lib/picky/query/qualifier_category_mapper.rb +43 -0
- data/lib/picky/query/token.rb +165 -172
- data/lib/picky/query/tokens.rb +86 -82
- data/lib/picky/query/weights.rb +44 -48
- data/lib/picky/query.rb +5 -1
- data/lib/picky/rack/harakiri.rb +51 -47
- data/lib/picky/results.rb +81 -77
- data/lib/picky/search.rb +169 -158
- data/lib/picky/sinatra.rb +34 -0
- data/lib/picky/sources/base.rb +73 -70
- data/lib/picky/sources/couch.rb +61 -57
- data/lib/picky/sources/csv.rb +68 -64
- data/lib/picky/sources/db.rb +139 -135
- data/lib/picky/sources/delicious.rb +52 -48
- data/lib/picky/sources/mongo.rb +68 -63
- data/lib/picky/sources/wrappers/base.rb +20 -16
- data/lib/picky/sources/wrappers/location.rb +37 -33
- data/lib/picky/statistics.rb +46 -43
- data/lib/picky/tasks.rb +3 -0
- data/lib/picky/tokenizers/base.rb +192 -187
- data/lib/picky/tokenizers/index.rb +25 -21
- data/lib/picky/tokenizers/location.rb +33 -29
- data/lib/picky/tokenizers/query.rb +49 -43
- data/lib/picky.rb +21 -13
- data/lib/tasks/application.rake +1 -1
- data/lib/tasks/index.rake +3 -3
- data/lib/tasks/routes.rake +1 -1
- data/lib/tasks/server.rake +1 -1
- data/spec/lib/adapters/rack/base_spec.rb +1 -1
- data/spec/lib/adapters/rack/live_parameters_spec.rb +1 -1
- data/spec/lib/adapters/rack/query_spec.rb +1 -1
- data/spec/lib/application_spec.rb +39 -32
- data/spec/lib/backend/file/basic_spec.rb +2 -2
- data/spec/lib/backend/file/json_spec.rb +2 -2
- data/spec/lib/backend/file/marshal_spec.rb +2 -2
- data/spec/lib/backend/file/text_spec.rb +1 -1
- data/spec/lib/backend/files_spec.rb +14 -24
- data/spec/lib/backend/redis/basic_spec.rb +2 -2
- data/spec/lib/backend/redis/list_hash_spec.rb +3 -3
- data/spec/lib/backend/redis/string_hash_spec.rb +3 -3
- data/spec/lib/backend/redis_spec.rb +20 -13
- data/spec/lib/calculations/location_spec.rb +1 -1
- data/spec/lib/categories_indexed_spec.rb +16 -34
- data/spec/lib/category_indexed_spec.rb +9 -27
- data/spec/lib/category_indexing_spec.rb +2 -3
- data/spec/lib/category_spec.rb +10 -10
- data/spec/lib/character_substituters/west_european_spec.rb +6 -5
- data/spec/lib/cores_spec.rb +17 -17
- data/spec/lib/extensions/symbol_spec.rb +15 -1
- data/spec/lib/frontend_adapters/rack_spec.rb +20 -20
- data/spec/lib/generators/aliases_spec.rb +3 -3
- data/spec/lib/generators/cacher_strategy_spec.rb +1 -1
- data/spec/lib/generators/partial/default_spec.rb +3 -3
- data/spec/lib/generators/partial/none_spec.rb +2 -2
- data/spec/lib/generators/partial/substring_spec.rb +1 -1
- data/spec/lib/generators/partial_generator_spec.rb +3 -3
- data/spec/lib/generators/similarity/double_metaphone_spec.rb +1 -1
- data/spec/lib/generators/similarity/metaphone_spec.rb +1 -1
- data/spec/lib/generators/similarity/none_spec.rb +1 -1
- data/spec/lib/generators/similarity/phonetic_spec.rb +1 -1
- data/spec/lib/generators/similarity/soundex_spec.rb +1 -1
- data/spec/lib/generators/similarity_generator_spec.rb +2 -2
- data/spec/lib/generators/weights/logarithmic_spec.rb +1 -1
- data/spec/lib/generators/weights_generator_spec.rb +1 -1
- data/spec/lib/helpers/measuring_spec.rb +2 -2
- data/spec/lib/indexed/bundle/memory_spec.rb +6 -6
- data/spec/lib/indexed/bundle/redis_spec.rb +4 -4
- data/spec/lib/indexed/wrappers/bundle/calculation_spec.rb +2 -3
- data/spec/lib/indexed/wrappers/bundle/wrapper_spec.rb +2 -2
- data/spec/lib/indexed/wrappers/exact_first_spec.rb +5 -5
- data/spec/lib/indexers/base_spec.rb +1 -1
- data/spec/lib/indexers/parallel_spec.rb +1 -1
- data/spec/lib/indexers/serial_spec.rb +1 -1
- data/spec/lib/{index/base_indexed_spec.rb → indexes/index_indexed_spec.rb} +3 -3
- data/spec/lib/{index/base_indexing_spec.rb → indexes/index_indexing_spec.rb} +19 -2
- data/spec/lib/{index/base_spec.rb → indexes/index_spec.rb} +6 -25
- data/spec/lib/{index → indexes}/redis_spec.rb +1 -1
- data/spec/lib/indexes_class_spec.rb +2 -2
- data/spec/lib/indexes_indexed_spec.rb +1 -1
- data/spec/lib/indexes_indexing_spec.rb +1 -1
- data/spec/lib/indexes_spec.rb +1 -1
- data/spec/lib/indexing/bundle/base_spec.rb +7 -5
- data/spec/lib/indexing/bundle/memory_partial_generation_speed_spec.rb +4 -4
- data/spec/lib/indexing/bundle/memory_spec.rb +15 -15
- data/spec/lib/indexing/bundle/redis_spec.rb +9 -9
- data/spec/lib/interfaces/live_parameters_spec.rb +5 -5
- data/spec/lib/loader_spec.rb +17 -19
- data/spec/lib/loggers/search_spec.rb +2 -2
- data/spec/lib/query/allocation_spec.rb +1 -1
- data/spec/lib/query/allocations_spec.rb +1 -1
- data/spec/lib/query/combination_spec.rb +4 -4
- data/spec/lib/query/combinations/base_spec.rb +1 -1
- data/spec/lib/query/combinations/memory_spec.rb +1 -1
- data/spec/lib/query/combinations/redis_spec.rb +1 -1
- data/spec/lib/query/indexes_spec.rb +7 -2
- data/spec/lib/query/qualifier_category_mapper_spec.rb +34 -0
- data/spec/lib/query/token_spec.rb +32 -53
- data/spec/lib/query/tokens_spec.rb +30 -35
- data/spec/lib/query/weights_spec.rb +16 -16
- data/spec/lib/rack/harakiri_spec.rb +5 -5
- data/spec/lib/results_spec.rb +1 -1
- data/spec/lib/search_spec.rb +24 -22
- data/spec/lib/sinatra_spec.rb +36 -0
- data/spec/lib/sources/base_spec.rb +1 -1
- data/spec/lib/sources/couch_spec.rb +9 -9
- data/spec/lib/sources/csv_spec.rb +7 -7
- data/spec/lib/sources/db_spec.rb +2 -2
- data/spec/lib/sources/delicious_spec.rb +5 -5
- data/spec/lib/sources/mongo_spec.rb +7 -7
- data/spec/lib/sources/wrappers/base_spec.rb +2 -2
- data/spec/lib/sources/wrappers/location_spec.rb +1 -1
- data/spec/lib/statistics_spec.rb +1 -1
- data/spec/lib/tokenizers/base_spec.rb +2 -2
- data/spec/lib/tokenizers/index_spec.rb +1 -1
- data/spec/lib/tokenizers/query_spec.rb +1 -1
- metadata +30 -30
- data/lib/picky/adapters/rack/query.rb +0 -65
- data/lib/picky/index/base.rb +0 -409
- data/lib/picky/index/base_indexed.rb +0 -29
- data/lib/picky/index/base_indexing.rb +0 -127
- data/lib/picky/index/memory.rb +0 -16
- data/lib/picky/index/redis.rb +0 -16
- data/lib/picky/query/qualifiers.rb +0 -76
- data/lib/picky/query/solr.rb +0 -60
- data/lib/picky/signals.rb +0 -8
- data/lib/picky-tasks.rb +0 -6
- data/lib/tasks/spec.rake +0 -11
- data/spec/lib/query/qualifiers_spec.rb +0 -31
|
@@ -1,44 +1,48 @@
|
|
|
1
|
-
module
|
|
1
|
+
module Picky
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
#
|
|
5
|
-
# They are the core of an allocation.
|
|
6
|
-
# An allocation consists of a number of combinations.
|
|
7
|
-
#
|
|
8
|
-
module Combinations # :nodoc:all
|
|
3
|
+
module Query
|
|
9
4
|
|
|
10
|
-
#
|
|
11
|
-
# calculating score and ids in memory.
|
|
5
|
+
# Combinations are a number of Combination-s.
|
|
12
6
|
#
|
|
13
|
-
|
|
7
|
+
# They are the core of an allocation.
|
|
8
|
+
# An allocation consists of a number of combinations.
|
|
9
|
+
#
|
|
10
|
+
module Combinations # :nodoc:all
|
|
14
11
|
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# Sorts the ids by size and & through them in the following order (sizes):
|
|
18
|
-
# 0. [100_000, 400, 30, 2]
|
|
19
|
-
# 1. [2, 30, 400, 100_000]
|
|
20
|
-
# 2. (100_000 & (400 & (30 & 2))) # => result
|
|
21
|
-
#
|
|
22
|
-
# Note: Uses a C-optimized intersection routine (in performant.c)
|
|
23
|
-
# for speed and memory efficiency.
|
|
12
|
+
# Memory Combinations contain specific methods for
|
|
13
|
+
# calculating score and ids in memory.
|
|
24
14
|
#
|
|
25
|
-
|
|
26
|
-
# We cannot use the information to speed up the algorithm, unfortunately.
|
|
27
|
-
#
|
|
28
|
-
def ids _, _
|
|
29
|
-
return [] if @combinations.empty?
|
|
15
|
+
class Memory < Base
|
|
30
16
|
|
|
31
|
-
#
|
|
17
|
+
# Returns the result ids for the allocation.
|
|
32
18
|
#
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
#
|
|
19
|
+
# Sorts the ids by size and & through them in the following order (sizes):
|
|
20
|
+
# 0. [100_000, 400, 30, 2]
|
|
21
|
+
# 1. [2, 30, 400, 100_000]
|
|
22
|
+
# 2. (100_000 & (400 & (30 & 2))) # => result
|
|
23
|
+
#
|
|
24
|
+
# Note: Uses a C-optimized intersection routine (in performant.c)
|
|
25
|
+
# for speed and memory efficiency.
|
|
38
26
|
#
|
|
39
|
-
# Note:
|
|
27
|
+
# Note: In the memory based version we ignore the (amount) needed hint.
|
|
28
|
+
# We cannot use the information to speed up the algorithm, unfortunately.
|
|
40
29
|
#
|
|
41
|
-
|
|
30
|
+
def ids _, _
|
|
31
|
+
return [] if @combinations.empty?
|
|
32
|
+
|
|
33
|
+
# Get the ids for each combination.
|
|
34
|
+
#
|
|
35
|
+
id_arrays = @combinations.inject([]) do |total, combination|
|
|
36
|
+
total << combination.ids
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Call the optimized C algorithm.
|
|
40
|
+
#
|
|
41
|
+
# Note: It orders the passed arrays by size.
|
|
42
|
+
#
|
|
43
|
+
Performant::Array.memory_efficient_intersect id_arrays
|
|
44
|
+
end
|
|
45
|
+
|
|
42
46
|
end
|
|
43
47
|
|
|
44
48
|
end
|
|
@@ -1,82 +1,86 @@
|
|
|
1
|
-
module
|
|
1
|
+
module Picky
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
#
|
|
5
|
-
# They are the core of an allocation.
|
|
6
|
-
# An allocation consists of a number of combinations.
|
|
7
|
-
#
|
|
8
|
-
module Combinations # :nodoc:all
|
|
3
|
+
module Query
|
|
9
4
|
|
|
10
|
-
#
|
|
11
|
-
# calculating score and ids in memory.
|
|
5
|
+
# Combinations are a number of Combination-s.
|
|
12
6
|
#
|
|
13
|
-
|
|
7
|
+
# They are the core of an allocation.
|
|
8
|
+
# An allocation consists of a number of combinations.
|
|
9
|
+
#
|
|
10
|
+
module Combinations # :nodoc:all
|
|
14
11
|
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# TODO Use specific Picky Redis wrapper.
|
|
12
|
+
# Redis Combinations contain specific methods for
|
|
13
|
+
# calculating score and ids in memory.
|
|
18
14
|
#
|
|
19
|
-
|
|
20
|
-
@redis ||= ::Redis.new :db => 15
|
|
21
|
-
end
|
|
15
|
+
class Redis < Base
|
|
22
16
|
|
|
23
|
-
|
|
17
|
+
# Connect to the backend.
|
|
18
|
+
#
|
|
19
|
+
# TODO Use specific Picky Redis wrapper.
|
|
20
|
+
#
|
|
21
|
+
def self.redis
|
|
22
|
+
@redis ||= ::Redis.new :db => 15
|
|
23
|
+
end
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
#
|
|
27
|
-
def initialize combinations
|
|
28
|
-
super combinations
|
|
25
|
+
attr_reader :redis
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
#
|
|
35
|
-
def ids amount, offset
|
|
36
|
-
return [] if @combinations.empty?
|
|
27
|
+
#
|
|
28
|
+
#
|
|
29
|
+
def initialize combinations
|
|
30
|
+
super combinations
|
|
37
31
|
|
|
38
|
-
|
|
39
|
-
identifiers << "#{combination.identifier}"
|
|
32
|
+
@redis = self.class.redis
|
|
40
33
|
end
|
|
41
34
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
# Intersect and store.
|
|
35
|
+
# Returns the result ids for the allocation.
|
|
45
36
|
#
|
|
46
|
-
|
|
37
|
+
def ids amount, offset
|
|
38
|
+
return [] if @combinations.empty?
|
|
47
39
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
40
|
+
identifiers = @combinations.inject([]) do |identifiers, combination|
|
|
41
|
+
identifiers << "#{combination.identifier}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
result_id = generate_intermediate_result_id
|
|
45
|
+
|
|
46
|
+
# Intersect and store.
|
|
47
|
+
#
|
|
48
|
+
redis.zinterstore result_id, identifiers
|
|
51
49
|
|
|
52
|
-
|
|
50
|
+
# Get the stored result.
|
|
51
|
+
#
|
|
52
|
+
results = redis.zrange result_id, offset, (offset + amount)
|
|
53
|
+
|
|
54
|
+
# Delete the stored result as it was only for temporary purposes.
|
|
55
|
+
#
|
|
56
|
+
# Note: I could also not delete it, but that would not be clean at all.
|
|
57
|
+
#
|
|
58
|
+
redis.del result_id
|
|
59
|
+
|
|
60
|
+
results
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Generate a multiple host/process safe result id.
|
|
53
64
|
#
|
|
54
|
-
# Note:
|
|
65
|
+
# Note: Generated when this class loads.
|
|
55
66
|
#
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
67
|
+
require 'socket'
|
|
68
|
+
def self.extract_host
|
|
69
|
+
@host ||= Socket.gethostname
|
|
70
|
+
end
|
|
71
|
+
def host
|
|
72
|
+
self.class.extract_host
|
|
73
|
+
end
|
|
74
|
+
extract_host
|
|
75
|
+
def pid
|
|
76
|
+
@pid ||= Process.pid
|
|
77
|
+
end
|
|
78
|
+
# Use the host and pid (generated lazily in child processes) for the result.
|
|
79
|
+
#
|
|
80
|
+
def generate_intermediate_result_id
|
|
81
|
+
:"#{host}:#{pid}:picky:result"
|
|
82
|
+
end
|
|
60
83
|
|
|
61
|
-
# Generate a multiple host/process safe result id.
|
|
62
|
-
#
|
|
63
|
-
# Note: Generated when this class loads.
|
|
64
|
-
#
|
|
65
|
-
require 'socket'
|
|
66
|
-
def self.extract_host
|
|
67
|
-
@host ||= Socket.gethostname
|
|
68
|
-
end
|
|
69
|
-
def host
|
|
70
|
-
self.class.extract_host
|
|
71
|
-
end
|
|
72
|
-
extract_host
|
|
73
|
-
def pid
|
|
74
|
-
@pid ||= Process.pid
|
|
75
|
-
end
|
|
76
|
-
# Use the host and pid (generated lazily in child processes) for the result.
|
|
77
|
-
#
|
|
78
|
-
def generate_intermediate_result_id
|
|
79
|
-
:"#{host}:#{pid}:picky:result"
|
|
80
84
|
end
|
|
81
85
|
|
|
82
86
|
end
|
data/lib/picky/query/indexes.rb
CHANGED
|
@@ -1,193 +1,208 @@
|
|
|
1
|
-
module
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
# # If you call
|
|
7
|
-
# Search.new dvd_index, mp3_index, video_index
|
|
8
|
-
#
|
|
9
|
-
# # What it does is take the three given (API-) indexes and
|
|
10
|
-
# # bundle them in an index bundle.
|
|
11
|
-
#
|
|
12
|
-
class Indexes
|
|
13
|
-
|
|
14
|
-
attr_reader :indexes
|
|
15
|
-
|
|
16
|
-
# Creates a new Query::Indexes.
|
|
1
|
+
module Picky
|
|
2
|
+
|
|
3
|
+
module Query
|
|
4
|
+
|
|
5
|
+
# The query indexes class bundles indexes given to a query.
|
|
17
6
|
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
7
|
+
# Example:
|
|
8
|
+
# # If you call
|
|
9
|
+
# Search.new dvd_index, mp3_index, video_index
|
|
20
10
|
#
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
@combinations_type = combinations_type
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# Returns a number of prepared (sorted, reduced etc.) allocations for the given tokens.
|
|
11
|
+
# # What it does is take the three given (API-) indexes and
|
|
12
|
+
# # bundle them in an index bundle.
|
|
27
13
|
#
|
|
28
|
-
|
|
29
|
-
allocations = allocations_for tokens
|
|
14
|
+
class Indexes
|
|
30
15
|
|
|
31
|
-
|
|
32
|
-
#
|
|
33
|
-
allocations.uniq
|
|
16
|
+
attr_reader :indexes
|
|
34
17
|
|
|
35
|
-
#
|
|
18
|
+
# Creates a new Query::Indexes.
|
|
36
19
|
#
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
# Sort the allocations.
|
|
40
|
-
# (allocations are sorted according to score, highest to lowest)
|
|
20
|
+
# Its job is to generate all possible combinations.
|
|
21
|
+
# Note: We cannot mix memory and redis indexes just yet.
|
|
41
22
|
#
|
|
42
|
-
|
|
23
|
+
def initialize *indexes, combinations_type
|
|
24
|
+
@indexes = indexes
|
|
25
|
+
@combinations_type = combinations_type
|
|
26
|
+
@mapper = Query::QualifierCategoryMapper.new
|
|
27
|
+
map_categories
|
|
28
|
+
end
|
|
29
|
+
def map_categories
|
|
30
|
+
@indexes.each do |index|
|
|
31
|
+
index.each_category do |category|
|
|
32
|
+
@mapper.add category
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
43
36
|
|
|
44
|
-
#
|
|
37
|
+
# Returns a number of prepared (sorted, reduced etc.) allocations for the given tokens.
|
|
45
38
|
#
|
|
46
|
-
|
|
39
|
+
def prepared_allocations_for tokens, weights = {}
|
|
40
|
+
allocations = allocations_for tokens
|
|
47
41
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
42
|
+
# Remove double allocations.
|
|
43
|
+
#
|
|
44
|
+
allocations.uniq
|
|
51
45
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
#
|
|
56
|
-
def allocations_for tokens
|
|
57
|
-
Allocations.new allocations_ary_for(tokens)
|
|
58
|
-
end
|
|
59
|
-
def allocations_ary_for tokens
|
|
60
|
-
indexes.inject([]) do |allocations, index|
|
|
61
|
-
allocations + allocation_for(tokens, index)
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
def allocation_for tokens, index
|
|
65
|
-
# Expand the combinations.
|
|
66
|
-
#
|
|
67
|
-
possible_combinations = tokens.possible_combinations_in index
|
|
46
|
+
# Score the allocations using weights as bias.
|
|
47
|
+
#
|
|
48
|
+
allocations.calculate_score weights
|
|
68
49
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
50
|
+
# Sort the allocations.
|
|
51
|
+
# (allocations are sorted according to score, highest to lowest)
|
|
52
|
+
#
|
|
53
|
+
allocations.sort!
|
|
72
54
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
Allocation.new @combinations_type.new(expanded_combination), index.result_identifier # TODO Do not extract result_identifier.
|
|
77
|
-
end
|
|
78
|
-
end
|
|
55
|
+
# Reduce the amount of allocations.
|
|
56
|
+
#
|
|
57
|
+
# allocations.reduce_to some_amount
|
|
79
58
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
# [
|
|
84
|
-
# [<combinations for token1>],
|
|
85
|
-
# [<combinations for token2>],
|
|
86
|
-
# [<combinations for token3>]
|
|
87
|
-
# ]
|
|
88
|
-
#
|
|
89
|
-
# Generates all possible allocations of combinations.
|
|
90
|
-
# [
|
|
91
|
-
# [first combination of token1, first c of t2, first c of t3],
|
|
92
|
-
# [first combination of token1, first c of t2, second c of t3]
|
|
93
|
-
# ...
|
|
94
|
-
# ]
|
|
95
|
-
#
|
|
96
|
-
# Generates all possible combinations of array elements:
|
|
97
|
-
# [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]]
|
|
98
|
-
# Note: Also calculates the weights and sorts them accordingly.
|
|
99
|
-
#
|
|
100
|
-
# Note: This is a heavily optimized ruby version.
|
|
101
|
-
#
|
|
102
|
-
# Works like this:
|
|
103
|
-
# [1,2,3], [a,b,c], [k,l,m] are expanded to
|
|
104
|
-
# group mult: 1
|
|
105
|
-
# <- single mult ->
|
|
106
|
-
# [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
|
|
107
|
-
# group mult: 3
|
|
108
|
-
# <- -> s/m
|
|
109
|
-
# [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
|
|
110
|
-
# group mult: 9
|
|
111
|
-
# <> s/m
|
|
112
|
-
# [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
|
|
113
|
-
#
|
|
114
|
-
# It is then recombined, where
|
|
115
|
-
# [
|
|
116
|
-
# [a,a,b,b,c,c]
|
|
117
|
-
# [d,e,d,e,d,e]
|
|
118
|
-
# ]
|
|
119
|
-
# becomes
|
|
120
|
-
# [
|
|
121
|
-
# [a,d],
|
|
122
|
-
# [a,e],
|
|
123
|
-
# [b,d],
|
|
124
|
-
# [b,e],
|
|
125
|
-
# [c,d],
|
|
126
|
-
# [c,e]
|
|
127
|
-
# ]
|
|
128
|
-
#
|
|
129
|
-
# Note: Not using transpose as it is slower.
|
|
130
|
-
#
|
|
131
|
-
# Returns nil if there are no combinations.
|
|
132
|
-
#
|
|
133
|
-
# Note: Of course I could split this method up into smaller
|
|
134
|
-
# ones, but I guess I am a bit sentimental.
|
|
135
|
-
#
|
|
136
|
-
def expand_combinations_from possible_combinations
|
|
137
|
-
# If an element has size 0, this means one of the
|
|
138
|
-
# tokens could not be allocated.
|
|
139
|
-
#
|
|
140
|
-
return [] if possible_combinations.any?(&:empty?)
|
|
59
|
+
# Remove identifiers from allocations.
|
|
60
|
+
#
|
|
61
|
+
# allocations.remove some_array_of_identifiers_to_remove
|
|
141
62
|
|
|
142
|
-
|
|
63
|
+
allocations
|
|
64
|
+
end
|
|
65
|
+
# Returns a number of possible allocations for the given tokens.
|
|
143
66
|
#
|
|
144
|
-
|
|
67
|
+
def allocations_for tokens
|
|
68
|
+
tokens.categorize @mapper
|
|
145
69
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
70
|
+
Allocations.new allocations_ary_for(tokens)
|
|
71
|
+
end
|
|
72
|
+
def allocations_ary_for tokens
|
|
73
|
+
indexes.inject([]) do |allocations, index|
|
|
74
|
+
allocations + allocation_for(tokens, index)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
def allocation_for tokens, index
|
|
78
|
+
# Expand the combinations.
|
|
79
|
+
#
|
|
80
|
+
possible_combinations = tokens.possible_combinations_in index
|
|
149
81
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
possible_combinations.collect! do |combinations|
|
|
82
|
+
# Generate all possible combinations.
|
|
83
|
+
#
|
|
84
|
+
expanded_combinations = expand_combinations_from possible_combinations
|
|
154
85
|
|
|
155
|
-
#
|
|
86
|
+
# Add the wrapped possible allocations to the ones we already have.
|
|
156
87
|
#
|
|
157
|
-
|
|
88
|
+
expanded_combinations.map! do |expanded_combination|
|
|
89
|
+
Allocation.new @combinations_type.new(expanded_combination), index.result_identifier # TODO Do not extract result_identifier.
|
|
90
|
+
end
|
|
91
|
+
end
|
|
158
92
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
93
|
+
# This is the core of the search engine. No kidding.
|
|
94
|
+
#
|
|
95
|
+
# Gets an array of
|
|
96
|
+
# [
|
|
97
|
+
# [<combinations for token1>],
|
|
98
|
+
# [<combinations for token2>],
|
|
99
|
+
# [<combinations for token3>]
|
|
100
|
+
# ]
|
|
101
|
+
#
|
|
102
|
+
# Generates all possible allocations of combinations.
|
|
103
|
+
# [
|
|
104
|
+
# [first combination of token1, first c of t2, first c of t3],
|
|
105
|
+
# [first combination of token1, first c of t2, second c of t3]
|
|
106
|
+
# ...
|
|
107
|
+
# ]
|
|
108
|
+
#
|
|
109
|
+
# Generates all possible combinations of array elements:
|
|
110
|
+
# [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]]
|
|
111
|
+
# Note: Also calculates the weights and sorts them accordingly.
|
|
112
|
+
#
|
|
113
|
+
# Note: This is a heavily optimized ruby version.
|
|
114
|
+
#
|
|
115
|
+
# Works like this:
|
|
116
|
+
# [1,2,3], [a,b,c], [k,l,m] are expanded to
|
|
117
|
+
# group mult: 1
|
|
118
|
+
# <- single mult ->
|
|
119
|
+
# [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
|
|
120
|
+
# group mult: 3
|
|
121
|
+
# <- -> s/m
|
|
122
|
+
# [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
|
|
123
|
+
# group mult: 9
|
|
124
|
+
# <> s/m
|
|
125
|
+
# [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
|
|
126
|
+
#
|
|
127
|
+
# It is then recombined, where
|
|
128
|
+
# [
|
|
129
|
+
# [a,a,b,b,c,c]
|
|
130
|
+
# [d,e,d,e,d,e]
|
|
131
|
+
# ]
|
|
132
|
+
# becomes
|
|
133
|
+
# [
|
|
134
|
+
# [a,d],
|
|
135
|
+
# [a,e],
|
|
136
|
+
# [b,d],
|
|
137
|
+
# [b,e],
|
|
138
|
+
# [c,d],
|
|
139
|
+
# [c,e]
|
|
140
|
+
# ]
|
|
141
|
+
#
|
|
142
|
+
# Note: Not using transpose as it is slower.
|
|
143
|
+
#
|
|
144
|
+
# Returns nil if there are no combinations.
|
|
145
|
+
#
|
|
146
|
+
# Note: Of course I could split this method up into smaller
|
|
147
|
+
# ones, but I guess I am a bit sentimental.
|
|
148
|
+
#
|
|
149
|
+
def expand_combinations_from possible_combinations
|
|
150
|
+
# If an element has size 0, this means one of the
|
|
151
|
+
# tokens could not be allocated.
|
|
164
152
|
#
|
|
165
|
-
|
|
153
|
+
return [] if possible_combinations.any?(&:empty?)
|
|
166
154
|
|
|
167
|
-
#
|
|
168
|
-
# [a,b,c]
|
|
169
|
-
# [a,a,a, b,b,b, c,c,c]
|
|
170
|
-
# Then, expand the result by the group mult:
|
|
171
|
-
# [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]
|
|
155
|
+
# Generate the first multiplicator "with which" (well, not quite) to multiply the smallest amount of combinations.
|
|
172
156
|
#
|
|
173
|
-
|
|
174
|
-
total + Array.new(single_mult, combination)
|
|
175
|
-
end * group_mult
|
|
157
|
+
single_mult = possible_combinations.inject(1) { |total, combinations| total * combinations.size }
|
|
176
158
|
|
|
177
|
-
#
|
|
178
|
-
# since the next combinations' single mult is smaller
|
|
179
|
-
# and we need to adjust for that.
|
|
159
|
+
# Initialize a group multiplicator.
|
|
180
160
|
#
|
|
181
|
-
group_mult =
|
|
161
|
+
group_mult = 1
|
|
182
162
|
|
|
183
|
-
#
|
|
163
|
+
# The expanding part to line up the combinations
|
|
164
|
+
# for later combination in allocations.
|
|
184
165
|
#
|
|
185
|
-
combinations
|
|
166
|
+
possible_combinations.collect! do |combinations|
|
|
167
|
+
|
|
168
|
+
# Get the size of the combinations of the first token.
|
|
169
|
+
#
|
|
170
|
+
combinations_size = combinations.size
|
|
171
|
+
|
|
172
|
+
# Special case: If there is no combination for one of the tokens.
|
|
173
|
+
# In that case, we just use the same single mult for
|
|
174
|
+
# the next iteration.
|
|
175
|
+
# If there are combinations, we divide the single mult
|
|
176
|
+
# by the number of combinations.
|
|
177
|
+
#
|
|
178
|
+
single_mult /= combinations_size unless combinations_size.zero?
|
|
179
|
+
|
|
180
|
+
# Expand each combination by the single mult:
|
|
181
|
+
# [a,b,c]
|
|
182
|
+
# [a,a,a, b,b,b, c,c,c]
|
|
183
|
+
# Then, expand the result by the group mult:
|
|
184
|
+
# [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]
|
|
185
|
+
#
|
|
186
|
+
combinations = combinations.inject([]) do |total, combination|
|
|
187
|
+
total + Array.new(single_mult, combination)
|
|
188
|
+
end * group_mult
|
|
189
|
+
|
|
190
|
+
# Multiply the group mult by the combinations size,
|
|
191
|
+
# since the next combinations' single mult is smaller
|
|
192
|
+
# and we need to adjust for that.
|
|
193
|
+
#
|
|
194
|
+
group_mult = group_mult * combinations_size
|
|
195
|
+
|
|
196
|
+
# Return the combinations.
|
|
197
|
+
#
|
|
198
|
+
combinations
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
return [] if possible_combinations.empty?
|
|
202
|
+
|
|
203
|
+
possible_combinations.shift.zip *possible_combinations
|
|
186
204
|
end
|
|
187
205
|
|
|
188
|
-
return [] if possible_combinations.empty?
|
|
189
|
-
|
|
190
|
-
possible_combinations.shift.zip *possible_combinations
|
|
191
206
|
end
|
|
192
207
|
|
|
193
208
|
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Picky
|
|
2
|
+
|
|
3
|
+
# coding: utf-8
|
|
4
|
+
#
|
|
5
|
+
module Query
|
|
6
|
+
|
|
7
|
+
# Collection class for qualifiers.
|
|
8
|
+
#
|
|
9
|
+
class QualifierCategoryMapper # :nodoc:all
|
|
10
|
+
|
|
11
|
+
attr_reader :mapping
|
|
12
|
+
|
|
13
|
+
#
|
|
14
|
+
#
|
|
15
|
+
def initialize
|
|
16
|
+
@mapping = {}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
#
|
|
20
|
+
#
|
|
21
|
+
def add category
|
|
22
|
+
category.qualifiers.each do |qualifier|
|
|
23
|
+
sym_qualifier = qualifier.to_sym
|
|
24
|
+
warn %Q{Warning: Qualifier "#{qualifier}" already mapped to category #{mapping[sym_qualifier].identifier} (ambiguous qualifier mapping).} if mapping.has_key? sym_qualifier
|
|
25
|
+
mapping[sym_qualifier] = category
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Normalizes the given qualifier.
|
|
30
|
+
#
|
|
31
|
+
# Returns nil if it is not allowed, the normalized qualifier if it is.
|
|
32
|
+
#
|
|
33
|
+
def map qualifier
|
|
34
|
+
return nil if qualifier.blank?
|
|
35
|
+
|
|
36
|
+
@mapping[qualifier.to_sym]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|