picky 2.1.0 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,147 @@
1
+ class Terminal
2
+
3
+ attr_reader :client
4
+
5
+ def initialize given_uri
6
+ check_highline_gem
7
+ check_picky_client_gem
8
+
9
+ require 'uri'
10
+ uri = URI.parse given_uri
11
+ unless uri.path
12
+ uri = URI.parse "http://#{given_uri}"
13
+ end
14
+ unless uri.path =~ /^\//
15
+ uri.path = "/#{uri.path}"
16
+ end
17
+
18
+ @searches = 0
19
+ @durations = 0
20
+ @client = Picky::Client.new :host => (uri.host || 'localhost'), :port => (uri.port || 8080), :path => uri.path
21
+
22
+ install_trap
23
+ end
24
+ def check_highline_gem # :nodoc:
25
+ require "highline/system_extensions"
26
+ extend HighLine::SystemExtensions
27
+ rescue LoadError
28
+ warn_gem_missing 'highline', 'the terminal interface'
29
+ exit 1
30
+ end
31
+ def check_picky_client_gem # :nodoc:
32
+ require 'picky-client'
33
+ rescue LoadError
34
+ warn_gem_missing 'picky-client', 'the terminal interface'
35
+ exit 1
36
+ end
37
+
38
+ def install_trap
39
+ Signal.trap('INT') do
40
+ print "\e[100D"
41
+ flush
42
+ puts "\n"
43
+ puts "You performed #{@searches} searches, totalling #{"%.3f" % @durations} seconds."
44
+ print "\e[100D"
45
+ flush
46
+ exit
47
+ end
48
+ end
49
+
50
+ def flush
51
+ STDOUT.flush
52
+ end
53
+ def left amount = 1
54
+ print "\e[#{amount}D"
55
+ flush
56
+ end
57
+ def right amount = 1
58
+ print "\e[#{amount}C"
59
+ flush
60
+ end
61
+ def move_to position
62
+ relative = position - @cursor_offset
63
+ if relative > 0
64
+ right relative
65
+ else
66
+ left relative
67
+ end
68
+ @cursor_offset = position
69
+ flush
70
+ end
71
+ def backspace
72
+ @current_text.chop!
73
+ print "\e[1D"
74
+ print " "
75
+ print "\e[1D"
76
+ flush
77
+ end
78
+ def write text
79
+ print text
80
+ @cursor_offset += text.size
81
+ flush
82
+ end
83
+ def type_search character
84
+ @current_text << character
85
+ write character
86
+ end
87
+ def write_results results
88
+ move_to 0
89
+ write "%9d" % (results && results.total || 0)
90
+ move_to 10 + @current_text.size
91
+ end
92
+ def move_to_ids
93
+ move_to 10 + @current_text.size + 2
94
+ end
95
+ def write_ids results
96
+ move_to_ids
97
+ write "=> #{results.total ? results.ids : []}"
98
+ end
99
+ def clear_ids
100
+ move_to_ids
101
+ write " "*200
102
+ end
103
+ def log results
104
+ @searches += 1
105
+ @durations += (results[:duration] || 0)
106
+ end
107
+ def search full = false
108
+ client.search @current_text, :ids => (full ? 20 : 0)
109
+ end
110
+ def search_and_write full = false
111
+ results = search full
112
+ results.extend Picky::Convenience
113
+
114
+ log results
115
+
116
+ full ? write_ids(results) : clear_ids
117
+
118
+ write_results results
119
+ end
120
+
121
+ def run
122
+ puts "Type and see the result count update. Press enter for the first 20 result ids."
123
+ puts "Break with Ctrl-C."
124
+
125
+ @current_text = ''
126
+ @cursor_offset = 0
127
+ @last_ids = ''
128
+ move_to 10
129
+ search_and_write
130
+
131
+ loop do
132
+ input = get_character
133
+
134
+ case input
135
+ when 127
136
+ backspace
137
+ search_and_write
138
+ when 13
139
+ search_and_write true
140
+ else
141
+ type_search input.chr
142
+ search_and_write
143
+ end
144
+ end
145
+ end
146
+
147
+ end
@@ -37,8 +37,6 @@ module Indexing # :nodoc:all
37
37
 
38
38
  # Runs the indexers in parallel (index + cache).
39
39
  #
40
- # TODO Spec.
41
- #
42
40
  def index randomly = true
43
41
  take_snapshot
44
42
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  // Copying internal ruby methods.
4
4
  //
5
- static inline VALUE rb_ary_elt(ary, offset)
5
+ inline VALUE rb_ary_elt(ary, offset)
6
6
  VALUE ary;
7
7
  long offset;
8
8
  {
@@ -12,7 +12,7 @@ static inline VALUE rb_ary_elt(ary, offset)
12
12
  }
13
13
  return RARRAY_PTR(ary)[offset];
14
14
  }
15
- static VALUE ary_make_hash(ary1, ary2)
15
+ inline VALUE ary_make_hash(ary1, ary2)
16
16
  VALUE ary1, ary2;
17
17
  {
18
18
  VALUE hash = rb_hash_new();
@@ -28,48 +28,53 @@ static VALUE ary_make_hash(ary1, ary2)
28
28
  }
29
29
  return hash;
30
30
  }
31
+ inline VALUE rb_ary_length(VALUE ary) {
32
+ long length = RARRAY_LEN(ary);
33
+ return LONG2NUM(length);
34
+ }
31
35
 
32
- // This version just calls the & consecutively for all arrays.
33
- //
34
- // The arrays need to be pre-sorted small to large.
35
- //
36
- inline VALUE memory_efficient_intersect(VALUE self, VALUE length_sorted_array_of_arrays) {
36
+ // This version:
37
+ // * orders the arrays by ascending size, small to large.
38
+ // * calls the & consecutively for all arrays.
39
+ //
40
+ inline VALUE memory_efficient_intersect(VALUE self, VALUE unsorted_array_of_arrays) {
37
41
  // Counters.
38
42
  //
39
43
  long i, j;
40
-
44
+
41
45
  // Vars.
42
46
  //
43
47
  struct RArray *rb_array_of_arrays;
44
48
  VALUE smallest_array;
45
49
  VALUE current_array;
46
50
  VALUE hash;
47
-
51
+
48
52
  // Temps.
49
53
  //
50
54
  VALUE v, vv;
51
-
52
- // Conversions.
55
+
56
+ // Conversions & presorting.
53
57
  //
54
- rb_array_of_arrays = RARRAY(length_sorted_array_of_arrays);
58
+ rb_array_of_arrays = rb_block_call(unsorted_array_of_arrays, rb_intern("sort_by!"), 0, 0, rb_ary_length, 0);
55
59
  smallest_array = (VALUE) RARRAY(rb_ary_dup(RARRAY_PTR(rb_array_of_arrays)[0]));
56
-
60
+
57
61
  // Iterate through all arrays.
58
62
  //
59
63
  for (i = 1; i < RARRAY_LEN(rb_array_of_arrays); i++) {
60
64
  // Break if the smallest array is empty
65
+ //
61
66
  if (RARRAY_LEN(smallest_array) == 0) {
62
67
  break;
63
68
  }
64
-
69
+
65
70
  // Make a hash from the currently smallest version.
66
71
  //
67
72
  hash = ary_make_hash(smallest_array, 0);
68
-
73
+
69
74
  // Clear for use as temp array.
70
75
  //
71
76
  rb_ary_clear(smallest_array);
72
-
77
+
73
78
  // Iterate through all array elements.
74
79
  //
75
80
  current_array = RARRAY_PTR(rb_array_of_arrays)[i];
@@ -80,7 +85,7 @@ inline VALUE memory_efficient_intersect(VALUE self, VALUE length_sorted_array_of
80
85
  }
81
86
  }
82
87
  }
83
-
88
+
84
89
  return smallest_array;
85
90
  }
86
91
 
@@ -8,8 +8,6 @@ module Internals
8
8
 
9
9
  # Writes the hash into Redis.
10
10
  #
11
- # TODO Performance: rpush as you get the values instead of putting it together in an array first.
12
- #
13
11
  def dump hash
14
12
  hash.each_pair do |key, values|
15
13
  redis_key = "#{namespace}:#{key}"
@@ -34,7 +34,7 @@ module Internals
34
34
  def weight sym
35
35
  @backend.weight sym
36
36
  end
37
- # TODO Spec. Doc.
37
+ # Settings of this bundle can be accessed via [].
38
38
  #
39
39
  def [] sym
40
40
  @backend.setting sym
@@ -67,8 +67,6 @@ CATEGORY
67
67
  #
68
68
  # Note: The idea is not to run this while the search engine is running.
69
69
  #
70
- # TODO Spec. Identifier is ok?
71
- #
72
70
  def analyze collector
73
71
  collector[identifier] = {
74
72
  :exact => Analyzer.new.analyze(exact),
@@ -1,8 +1,10 @@
1
1
  # encoding: utf-8
2
2
  #
3
3
  require 'rsolr'
4
+
4
5
  module Indexers
5
- # TODO Deprecated. This should be handled in a special bundle which goes through Solr.
6
+
7
+ # Deprecated. Only here as an example.
6
8
  #
7
9
  class Solr
8
10
 
@@ -19,19 +21,19 @@ module Indexers
19
21
  def index
20
22
  timed_exclaim "Indexing solr for #{type.name}:#{fields.join(', ')}"
21
23
  statement = "SELECT indexed_id, #{fields.join(',')} FROM #{type.snapshot_table_name}"
22
-
24
+
23
25
  DB.connect
24
26
  results = DB.connection.execute statement
25
-
26
- return unless results # TODO check
27
-
27
+
28
+ return unless results
29
+
28
30
  type_name = @type.name.to_s
29
-
31
+
30
32
  solr.delete_by_query "type:#{type_name}"
31
33
  solr.commit
32
-
34
+
33
35
  documents = []
34
-
36
+
35
37
  results.each do |indexed_id, *values|
36
38
  values.each &:downcase!
37
39
  documents << hashed(values).merge(id: indexed_id, type: type_name)
@@ -194,9 +194,6 @@ module Internals
194
194
  raise_cache_missing :similarity unless backend.similarity_cache_ok?
195
195
  end
196
196
 
197
- # TODO Spec on down.
198
- #
199
-
200
197
  # Warns the user if the core or weights indexes are small.
201
198
  #
202
199
  def warn_if_index_small
@@ -1,6 +1,6 @@
1
1
  module Internals
2
2
 
3
- # FIXME Merge into Base, extract common with Indexed::Base.
3
+ # TODO Merge into Base, extract common with Indexed::Base.
4
4
  #
5
5
  module Indexing # :nodoc:all
6
6
  # A Bundle is a number of indexes
@@ -10,7 +10,7 @@ module Internals
10
10
  # * *core* index (always used)
11
11
  # * *weights* index (always used)
12
12
  # * *similarity* index (used with similarity)
13
- #
13
+ #
14
14
  # In Picky, indexing is separated from the index
15
15
  # handling itself through a parallel structure.
16
16
  #
@@ -24,27 +24,27 @@ module Internals
24
24
  # memory and looking up search data as fast as possible.
25
25
  #
26
26
  module Bundle
27
-
27
+
28
28
  class SuperBase
29
-
29
+
30
30
  attr_reader :identifier, :files
31
31
  attr_accessor :index, :weights, :similarity, :configuration, :similarity_strategy
32
-
32
+
33
33
  delegate :clear, :to => :index
34
34
  delegate :[], :[]=, :to => :configuration
35
-
35
+
36
36
  def initialize name, configuration, similarity_strategy
37
37
  @identifier = "#{configuration.identifier}:#{name}"
38
38
  @files = Internals::Index::Files.new name, configuration
39
-
39
+
40
40
  @index = {}
41
41
  @weights = {}
42
42
  @similarity = {}
43
43
  @configuration = {} # A hash with config options.
44
-
44
+
45
45
  @similarity_strategy = similarity_strategy
46
46
  end
47
-
47
+
48
48
  # Get a list of similar texts.
49
49
  #
50
50
  # Note: Does not return itself.
@@ -55,11 +55,11 @@ module Internals
55
55
  similar_codes.delete text if similar_codes
56
56
  similar_codes || []
57
57
  end
58
-
58
+
59
59
  end
60
-
60
+
61
61
  end
62
-
62
+
63
63
  end
64
-
64
+
65
65
  end
@@ -12,16 +12,17 @@ module Internals
12
12
  # Mandatory params:
13
13
  # * name: Category name to use as identifier and file names.
14
14
  # * index: Index to which this category is attached to.
15
+ #
15
16
  # Options:
16
17
  # * partial: Partial::None.new, Partial::Substring.new(from:start_char, to:up_to_char) (defaults from:-3, to:-1)
17
18
  # * similarity: Similarity::None.new (default), Similarity::DoubleMetaphone.new(amount_of_similarly_linked_words)
18
19
  # * source: Use if the category should use a different source.
19
20
  # * from: The source category identifier to take the data from.
20
21
  #
21
- # Advanced Options (TODO):
22
+ # Advanced Options:
22
23
  #
23
- # * weights:
24
- # * tokenizer:
24
+ # * weights: Query::Weights.new( [:category1, :category2] => +2, ... )
25
+ # * tokenizer: Use a subclass of Tokenizers::Base that implements #tokens_for and #empty_tokens.
25
26
  #
26
27
  # TODO Should source be not optional, or taken from the index?
27
28
  #
@@ -42,8 +42,6 @@ module Internals
42
42
  #
43
43
  # Only those passed in are removed.
44
44
  #
45
- # TODO Rewrite.
46
- #
47
45
  def remove identifiers = []
48
46
  @allocations.each { |allocation| allocation.remove identifiers } unless identifiers.empty?
49
47
  end
@@ -21,31 +21,25 @@ module Internals
21
21
  # 1. [2, 30, 400, 100_000]
22
22
  # 2. (100_000 & (400 & (30 & 2))) # => result
23
23
  #
24
- # Note: Uses a C-optimized intersection routine for speed and memory efficiency.
24
+ # Note: Uses a C-optimized intersection routine (in performant.c)
25
+ # for speed and memory efficiency.
25
26
  #
26
27
  # Note: In the memory based version we ignore the (amount) needed hint.
27
- # We might use the fact to optimize the algorithm.
28
+ # We cannot use the information to speed up the algorithm, unfortunately.
28
29
  #
29
30
  def ids _, _
30
31
  return [] if @combinations.empty?
31
32
 
32
33
  # Get the ids for each combination.
33
34
  #
34
- # TODO For combinations with Redis
35
- #
36
35
  id_arrays = @combinations.inject([]) do |total, combination|
37
36
  total << combination.ids
38
37
  end
39
38
 
40
- # Order by smallest size first such that the intersect can be performed faster.
41
- #
42
- # TODO Move into the memory_efficient_intersect such that
43
- # this precondition for a fast algorithm is always given.
44
- #
45
- id_arrays.sort! { |this_array, that_array| this_array.size <=> that_array.size }
46
-
47
39
  # Call the optimized C algorithm.
48
40
  #
41
+ # Note: It orders the passed arrays by size.
42
+ #
49
43
  Performant::Array.memory_efficient_intersect id_arrays
50
44
  end
51
45
 
@@ -25,11 +25,36 @@ module Internals
25
25
  @indexes = index_definitions.map &:indexed
26
26
  end
27
27
 
28
- # Returns a number of possible allocations for the given tokens.
28
+ # Returns a number of prepared (sorted, reduced etc.) allocations for the given tokens.
29
29
  #
30
- def sorted_allocations_for tokens
30
+ def prepared_allocations_for tokens, weights = {}
31
+ allocations = allocations_for tokens
32
+
33
+ # Remove double allocations.
34
+ #
35
+ allocations.uniq
36
+
37
+ # Score the allocations using weights as bias.
38
+ #
39
+ allocations.calculate_score weights
40
+
41
+ # Sort the allocations.
42
+ # (allocations are sorted according to score, highest to lowest)
43
+ #
44
+ allocations.sort!
31
45
 
46
+ # Reduce the amount of allocations.
47
+ #
48
+ # allocations.reduce_to some_amount
49
+
50
+ # Remove identifiers from allocations.
51
+ #
52
+ # allocations.remove some_array_of_identifiers_to_remove
53
+
54
+ allocations
32
55
  end
56
+ # Returns a number of possible allocations for the given tokens.
57
+ #
33
58
  def allocations_for tokens
34
59
  Allocations.new allocations_ary_for(tokens)
35
60
  end
@@ -14,11 +14,17 @@ module Internals
14
14
  #
15
15
  self.delegate *[Enumerable.instance_methods, :slice!, :[], :uniq!, :last, :reject!, :length, :size, :empty?, :each, :exit, { :to => :@tokens }].flatten
16
16
 
17
- #
17
+ # Create a new Tokens object with the array of tokens passed in.
18
18
  #
19
19
  def initialize tokens = []
20
20
  @tokens = tokens
21
21
  end
22
+
23
+ # Creates a new Tokens object from a number of Strings.
24
+ #
25
+ # Options:
26
+ # * downcase: Whether to downcase the passed strings (default is true)
27
+ #
22
28
  def self.processed words, downcase = true
23
29
  new words.collect! { |word| Token.processed word, downcase }
24
30
  end
data/lib/picky/search.rb CHANGED
@@ -14,8 +14,8 @@ class Search
14
14
  include Helpers::Measuring
15
15
 
16
16
  attr_reader :indexes
17
- attr_writer :tokenizer, :identifiers_to_remove
18
- attr_accessor :reduce_to_amount, :weights
17
+ attr_writer :tokenizer
18
+ attr_accessor :weights
19
19
 
20
20
  # Takes:
21
21
  # * A number of indexes
@@ -23,6 +23,8 @@ class Search
23
23
  # * tokenizer: Tokenizers::Query.default by default.
24
24
  # * weights: A hash of weights, or a Query::Weights object.
25
25
  #
26
+ # TODO Add identifiers_to_remove (rename) and reduce_allocations_to_amount (rename).
27
+ #
26
28
  def initialize *index_definitions
27
29
  options = Hash === index_definitions.last ? index_definitions.pop : {}
28
30
 
@@ -113,58 +115,8 @@ class Search
113
115
 
114
116
  # Gets sorted allocations for the tokens.
115
117
  #
116
- # This generates the possible allocations, sorted.
117
- #
118
- # TODO Smallify.
119
- #
120
- # TODO Rename: allocations
121
- #
122
118
  def sorted_allocations tokens # :nodoc:
123
- # Get the allocations.
124
- #
125
- # TODO Pass in reduce_to_amount (aka max_allocations)
126
- #
127
- # TODO uniq, score, sort in there
128
- #
129
- allocations = @indexes.allocations_for tokens
130
-
131
- # Callbacks.
132
- #
133
- # TODO Reduce before sort?
134
- #
135
- reduce allocations
136
- remove_from allocations
137
-
138
- # Remove double allocations.
139
- #
140
- allocations.uniq
141
-
142
- # Score the allocations using weights as bias.
143
- #
144
- allocations.calculate_score weights
145
-
146
- # Sort the allocations.
147
- # (allocations are sorted according to score, highest to lowest)
148
- #
149
- allocations.sort!
150
-
151
- # Return the allocations.
152
- #
153
- allocations
154
- end
155
- def reduce allocations # :nodoc:
156
- allocations.reduce_to reduce_to_amount if reduce_to_amount
157
- end
158
-
159
- #
160
- #
161
- def remove_from allocations # :nodoc:
162
- allocations.remove identifiers_to_remove
163
- end
164
- #
165
- #
166
- def identifiers_to_remove # :nodoc:
167
- @identifiers_to_remove ||= []
119
+ @indexes.prepared_allocations_for tokens, weights
168
120
  end
169
121
 
170
122
  # Display some nice information for the user.
@@ -0,0 +1,7 @@
1
+ # Tasks for testing your engine configuration in the terminal.
2
+ #
3
+ task :search do
4
+ load File.expand_path '../../picky/auxiliary/terminal.rb', __FILE__
5
+ terminal = Terminal.new ARGV[1] || raise("Usage:\n rake search <URL>\n E.g. rake search /books\n rake search localhost:8080/books")
6
+ terminal.run
7
+ end
@@ -5,44 +5,43 @@ describe Performant::Array do
5
5
  describe "memory_efficient_intersect" do
6
6
  it "should intersect empty arrays correctly" do
7
7
  arys = [[3,4], [1,2,3], []]
8
-
9
- Performant::Array.memory_efficient_intersect(arys.sort_by(&:size)).should == []
8
+
9
+ Performant::Array.memory_efficient_intersect(arys).should == []
10
10
  end
11
11
  it "should handle intermediate empty results correctly" do
12
12
  arys = [[5,4], [1,2,3], [3,4,5,8,9]]
13
-
14
- Performant::Array.memory_efficient_intersect(arys.sort_by(&:size)).should == []
13
+
14
+ Performant::Array.memory_efficient_intersect(arys).should == []
15
15
  end
16
16
  it "should intersect correctly" do
17
17
  arys = [[3,4], [1,2,3], [3,4,5,8,9]]
18
-
19
- Performant::Array.memory_efficient_intersect(arys.sort_by(&:size)).should == [3]
18
+
19
+ Performant::Array.memory_efficient_intersect(arys).should == [3]
20
20
  end
21
21
  it "should intersect correctly again" do
22
- arys = [[3,4,5,6,7], [1,2,3,5,6,7], [3,4,5,6,7,8,9]]
23
-
24
- Performant::Array.memory_efficient_intersect(arys.sort_by(&:size)).should == [3,5,6,7]
22
+ arys = [[1,2,3,5,6,7], [3,4,5,6,7,8,9], [3,4,5,6,7]]
23
+ Performant::Array.memory_efficient_intersect(arys).should == [3,5,6,7]
25
24
  end
26
25
  it "should intersect many arrays" do
27
26
  arys = [[3,4,5,6,7], [1,2,3,5,6,7], [3,4,5,6,7,8,9], [1,2,3,4,5,6,7,8,9,10], [2,3,5,6,7,19], [1,2,3,4,5,6,7,8,9,10], [2,3,5,6,7,19]]
28
-
29
- Performant::Array.memory_efficient_intersect(arys.sort_by(&:size)).should == [3,5,6,7]
27
+
28
+ Performant::Array.memory_efficient_intersect(arys).should == [3,5,6,7]
30
29
  end
31
30
  it "should handle random arrays" do
32
31
  proto = Array.new(100, 3_500_000)
33
32
  arys = [proto.map { |e| rand e }, proto.map { |e| rand e }, proto.map { |e| rand e }]
34
-
35
- Performant::Array.memory_efficient_intersect(arys.sort_by(&:size)).should == arys.inject(arys.shift.dup) { |total, ary| total & arys }
33
+
34
+ Performant::Array.memory_efficient_intersect(arys).should == arys.inject(arys.shift.dup) { |total, ary| total & arys }
36
35
  end
37
36
  it "should be optimal for 2 small arrays of 50/10_000" do
38
37
  arys = [(1..50).to_a, (10_000..20_000).to_a << 7]
39
-
38
+
40
39
  # brute force
41
- performance_of { Performant::Array.memory_efficient_intersect(arys.sort_by(&:size)) }.should < 0.001
40
+ performance_of { Performant::Array.memory_efficient_intersect(arys) }.should < 0.001
42
41
  end
43
42
  it "should be optimal for 2 small arrays of 50/10_000" do
44
43
  arys = [(1..50).to_a, (10_000..20_000).to_a << 7]
45
-
44
+
46
45
  # &
47
46
  performance_of do
48
47
  arys.inject(arys.shift.dup) do |total, ary|
@@ -55,33 +54,33 @@ describe Performant::Array do
55
54
  describe "memory_efficient_intersect with symbols" do
56
55
  it "should intersect empty arrays correctly" do
57
56
  arys = [[:c,:d], [:a,:b,:c], []]
58
-
59
- Performant::Array.memory_efficient_intersect(arys.sort_by(&:size)).should == []
57
+
58
+ Performant::Array.memory_efficient_intersect(arys).should == []
60
59
  end
61
60
  it "should handle intermediate empty results correctly" do
62
61
  arys = [[:e,:d], [:a,:b,:c], [:c,:d,:e,:h,:i]]
63
-
64
- Performant::Array.memory_efficient_intersect(arys.sort_by(&:size)).should == []
62
+
63
+ Performant::Array.memory_efficient_intersect(arys).should == []
65
64
  end
66
65
  it "should intersect correctly" do
67
66
  arys = [[:c,:d], [:a,:b,:c], [:c,:d,:e,:h,:i]]
68
-
69
- Performant::Array.memory_efficient_intersect(arys.sort_by(&:size)).should == [:c]
67
+
68
+ Performant::Array.memory_efficient_intersect(arys).should == [:c]
70
69
  end
71
70
  it "should intersect many arrays" do
72
71
  arys = [[:c,:d,:e,:f,:g], [:a,:b,:c,:e,:f,:g], [:c,:d,:e,:f,:g,:h,:i], [:a,:b,:c,:d,:e,:f,:g,:h,:i,:j], [:b,:c,:e,:f,:g,:s], [:a,:b,:c,:d,:e,:f,:g,:h,:i,:j], [:b,:c,:e,:f,:g,:s]]
73
-
74
- Performant::Array.memory_efficient_intersect(arys.sort_by(&:size)).should == [:c,:e,:f,:g]
72
+
73
+ Performant::Array.memory_efficient_intersect(arys).should == [:c,:e,:f,:g]
75
74
  end
76
75
  it "should be optimal for 2 small arrays of 50/10_000" do
77
- arys = [(:'1'..:'50').to_a, (:'10_000'..:'20_000').to_a << 7]
78
-
76
+ arys = [(:'1'..:'50').to_a, (:'10_000'..:'20_000').to_a]
77
+
79
78
  # brute force
80
- performance_of { Performant::Array.memory_efficient_intersect(arys.sort_by(&:size)) }.should < 0.001
79
+ performance_of { Performant::Array.memory_efficient_intersect(arys) }.should < 0.001
81
80
  end
82
81
  it "should be optimal for 2 small arrays of 50/10_000" do
83
82
  arys = [(:'1'..:'50').to_a, (:'10_000'..:'20_000').to_a << 7]
84
-
83
+
85
84
  # &
86
85
  performance_of do
87
86
  arys.inject(arys.shift.dup) do |total, ary|
@@ -0,0 +1,56 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'spec_helper'
4
+
5
+ # We need to load the Statistics file explicitly as the Statistics
6
+ # are not loaded with the Loader (not needed in the server, only for script runs).
7
+ #
8
+ require File.expand_path '../../../../lib/picky/auxiliary/terminal', __FILE__
9
+
10
+ describe Terminal do
11
+
12
+ let(:terminal) { described_class.new('/some/url') }
13
+
14
+ before(:each) do
15
+ terminal.stub! :search => { :total => 0, :duration => 0.01 }
16
+ end
17
+
18
+ describe 'left' do
19
+ it 'moves by amount' do
20
+ terminal.should_receive(:print).once.ordered.with "\e[13D"
21
+ terminal.should_receive(:flush).once.ordered
22
+
23
+ terminal.left 13
24
+ end
25
+ it 'default is 1' do
26
+ terminal.should_receive(:print).once.ordered.with "\e[1D"
27
+ terminal.should_receive(:flush).once.ordered
28
+
29
+ terminal.left
30
+ end
31
+ end
32
+
33
+ describe 'right' do
34
+ it 'moves by amount' do
35
+ terminal.should_receive(:print).once.ordered.with "\e[13C"
36
+ terminal.should_receive(:flush).once.ordered
37
+
38
+ terminal.right 13
39
+ end
40
+ it 'default is 1' do
41
+ terminal.should_receive(:print).once.ordered.with "\e[1C"
42
+ terminal.should_receive(:flush).once.ordered
43
+
44
+ terminal.right
45
+ end
46
+ end
47
+
48
+ describe 'flush' do
49
+ it 'flushes STDOUT' do
50
+ STDOUT.should_receive(:flush).once.with()
51
+
52
+ terminal.flush
53
+ end
54
+ end
55
+
56
+ end
@@ -1 +1,15 @@
1
- # TODO aliases spec.
1
+ require 'spec_helper'
2
+
3
+ describe 'aliases' do
4
+
5
+ it 'aliases correctly' do
6
+ Partial.should == Internals::Generators::Partial
7
+ end
8
+ it 'aliases correctly' do
9
+ Similarity.should == Internals::Generators::Similarity
10
+ end
11
+ it 'aliases correctly' do
12
+ Weights.should == Internals::Generators::Weights
13
+ end
14
+
15
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe Internals::Indexed::Bundle::Redis do
4
+
5
+ before(:each) do
6
+ @backend = stub :backend
7
+
8
+ Internals::Index::Redis.stub! :new => @backend
9
+
10
+ @category = stub :category, :name => :some_category
11
+ @index = stub :index, :name => :some_index
12
+ @configuration = Configuration::Index.new @index, @category
13
+
14
+ @similarity = stub :similarity
15
+ @bundle = described_class.new :some_name, @configuration, @similarity
16
+ end
17
+
18
+ describe 'ids' do
19
+ it 'delegates to the backend' do
20
+ @backend.should_receive(:ids).once.with :some_sym
21
+
22
+ @bundle.ids :some_sym
23
+ end
24
+ end
25
+
26
+ describe 'weight' do
27
+ it 'delegates to the backend' do
28
+ @backend.should_receive(:weight).once.with :some_sym
29
+
30
+ @bundle.weight :some_sym
31
+ end
32
+ end
33
+
34
+ describe '[]' do
35
+ it 'delegates to the backend' do
36
+ @backend.should_receive(:setting).once.with :some_sym
37
+
38
+ @bundle[:some_sym]
39
+ end
40
+ end
41
+
42
+ end
@@ -77,5 +77,26 @@ describe Internals::Query::Indexes do
77
77
  performance_of { indexes.expand_combinations_from(combinations) }.should < 0.00045
78
78
  end
79
79
  end
80
+
81
+ describe 'prepared_allocations_for' do
82
+ before(:each) do
83
+ @allocations = stub :allocations
84
+ indexes.stub! :allocations_for => @allocations
85
+ end
86
+ it 'calls the right method in order' do
87
+ @allocations.should_receive(:uniq).once.ordered.with()
88
+ @allocations.should_receive(:calculate_score).once.ordered.with(:some_weights)
89
+ @allocations.should_receive(:sort!).once.ordered.with()
90
+
91
+ indexes.prepared_allocations_for :some_tokens, :some_weights
92
+ end
93
+ it 'calls the right method in order' do
94
+ @allocations.should_receive(:uniq).once.ordered.with()
95
+ @allocations.should_receive(:calculate_score).once.ordered.with({})
96
+ @allocations.should_receive(:sort!).once.ordered.with()
97
+
98
+ indexes.prepared_allocations_for :some_tokens
99
+ end
100
+ end
80
101
 
81
102
  end
@@ -4,6 +4,11 @@ require 'spec_helper'
4
4
 
5
5
  describe Search do
6
6
 
7
+ before(:each) do
8
+ @type = stub :type
9
+ @index = stub :some_index, :indexed => @type
10
+ end
11
+
7
12
  describe 'combinations_type_for' do
8
13
  let(:search) { described_class.new }
9
14
  it 'returns a specific Combination for a specific input' do
@@ -46,25 +51,6 @@ describe Search do
46
51
  end
47
52
  end
48
53
 
49
- # describe "empty_results" do
50
- # before(:each) do
51
- # @search = search::Full.new
52
- #
53
- # @result_type = stub :result_type
54
- # @search.stub! :result_type => @result_type
55
- # end
56
- # it "returns a new result type" do
57
- # @result_type.should_receive(:new).once.with :some_offset
58
- #
59
- # @search.empty_results :some_offset
60
- # end
61
- # it "returns a new result type with default offset" do
62
- # @result_type.should_receive(:new).once.with 0
63
- #
64
- # @search.empty_results
65
- # end
66
- # end
67
-
68
54
  describe "search_with_text" do
69
55
  before(:each) do
70
56
  @search = Search.new
@@ -83,60 +69,6 @@ describe Search do
83
69
  end
84
70
  end
85
71
 
86
- describe 'reduce' do
87
- context 'real' do
88
- before(:each) do
89
- @allocations = stub :allocations
90
- @search = Search.new
91
- end
92
- context 'reduce_to_amount not set' do
93
- it 'should not call anything on the allocations' do
94
- @allocations.should_receive(:reduce_to).never
95
-
96
- @search.reduce @allocations
97
- end
98
- end
99
- context 'reduce_to_amount set' do
100
- before(:each) do
101
- @search.reduce_to_amount = :some_amount
102
- end
103
- it 'should call reduce_to on the allocations' do
104
- @allocations.should_receive(:reduce_to).once.with :some_amount
105
-
106
- @search.reduce @allocations
107
- end
108
- end
109
- end
110
- context 'stubbed' do
111
- before(:each) do
112
- @allocations = stub :allocations
113
- @search = Search.new
114
- end
115
- context 'reduce_to_amount not set' do
116
- it 'should not call anything on the allocations' do
117
- @allocations.should_receive(:reduce_to).never
118
-
119
- @search.reduce @allocations
120
- end
121
- end
122
- context 'reduce_to_amount set' do
123
- before(:each) do
124
- @search.stub! :reduce_to_amount => :some_amount
125
- end
126
- it 'should call reduce_to on the allocations' do
127
- @allocations.should_receive(:reduce_to).once.with :some_amount
128
-
129
- @search.reduce @allocations
130
- end
131
- end
132
- end
133
- end
134
-
135
- before(:each) do
136
- @type = stub :type
137
- @index = stub :some_index, :indexed => @type
138
- end
139
-
140
72
  describe 'initializer' do
141
73
  context 'with tokenizer' do
142
74
  before(:each) do
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: picky
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 2.1.0
5
+ version: 2.1.1
6
6
  platform: ruby
7
7
  authors:
8
8
  - Florian Hanke
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-04-07 00:00:00 +10:00
13
+ date: 2011-04-11 00:00:00 +10:00
14
14
  default_executable: picky
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -39,6 +39,7 @@ files:
39
39
  - lib/picky/aliases.rb
40
40
  - lib/picky/analyzer.rb
41
41
  - lib/picky/application.rb
42
+ - lib/picky/auxiliary/terminal.rb
42
43
  - lib/picky/character_substituters/west_european.rb
43
44
  - lib/picky/cli.rb
44
45
  - lib/picky/cores.rb
@@ -154,6 +155,7 @@ files:
154
155
  - lib/tasks/framework.rake
155
156
  - lib/tasks/index.rake
156
157
  - lib/tasks/routes.rake
158
+ - lib/tasks/search.rake
157
159
  - lib/tasks/server.rake
158
160
  - lib/tasks/shortcuts.rake
159
161
  - lib/tasks/solr.rake
@@ -166,6 +168,7 @@ files:
166
168
  - spec/lib/aliases_spec.rb
167
169
  - spec/lib/analyzer_spec.rb
168
170
  - spec/lib/application_spec.rb
171
+ - spec/lib/auxiliary/terminal_spec.rb
169
172
  - spec/lib/bundling_spec.rb
170
173
  - spec/lib/character_substituters/west_european_spec.rb
171
174
  - spec/lib/cli_spec.rb
@@ -211,6 +214,7 @@ files:
211
214
  - spec/lib/internals/index/redis/string_hash_spec.rb
212
215
  - spec/lib/internals/index/redis_spec.rb
213
216
  - spec/lib/internals/indexed/bundle/memory_spec.rb
217
+ - spec/lib/internals/indexed/bundle/redis_spec.rb
214
218
  - spec/lib/internals/indexed/categories_spec.rb
215
219
  - spec/lib/internals/indexed/category_spec.rb
216
220
  - spec/lib/internals/indexed/index_spec.rb
@@ -290,6 +294,7 @@ test_files:
290
294
  - spec/lib/aliases_spec.rb
291
295
  - spec/lib/analyzer_spec.rb
292
296
  - spec/lib/application_spec.rb
297
+ - spec/lib/auxiliary/terminal_spec.rb
293
298
  - spec/lib/bundling_spec.rb
294
299
  - spec/lib/character_substituters/west_european_spec.rb
295
300
  - spec/lib/cli_spec.rb
@@ -335,6 +340,7 @@ test_files:
335
340
  - spec/lib/internals/index/redis/string_hash_spec.rb
336
341
  - spec/lib/internals/index/redis_spec.rb
337
342
  - spec/lib/internals/indexed/bundle/memory_spec.rb
343
+ - spec/lib/internals/indexed/bundle/redis_spec.rb
338
344
  - spec/lib/internals/indexed/categories_spec.rb
339
345
  - spec/lib/internals/indexed/category_spec.rb
340
346
  - spec/lib/internals/indexed/index_spec.rb