picky 3.6.7 → 3.6.8

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.
Files changed (63) hide show
  1. data/lib/picky/backends/file/basic.rb +1 -1
  2. data/lib/picky/backends/file/json.rb +5 -1
  3. data/lib/picky/backends/file.rb +7 -0
  4. data/lib/picky/backends/memory.rb +7 -0
  5. data/lib/picky/backends/redis/basic.rb +3 -11
  6. data/lib/picky/backends/redis/directly_manipulable.rb +48 -0
  7. data/lib/picky/backends/redis/list.rb +39 -15
  8. data/lib/picky/backends/redis/string.rb +17 -9
  9. data/lib/picky/backends/redis.rb +102 -66
  10. data/lib/picky/backends/sqlite/array.rb +38 -0
  11. data/lib/picky/backends/sqlite/basic.rb +100 -0
  12. data/lib/picky/backends/sqlite/directly_manipulable.rb +42 -0
  13. data/lib/picky/backends/sqlite/value.rb +34 -0
  14. data/lib/picky/backends/sqlite.rb +14 -4
  15. data/lib/picky/bundle.rb +12 -5
  16. data/lib/picky/bundle_indexed.rb +15 -2
  17. data/lib/picky/bundle_indexing.rb +6 -5
  18. data/lib/picky/bundle_realtime.rb +22 -31
  19. data/lib/picky/categories_realtime.rb +1 -1
  20. data/lib/picky/category_indexed.rb +1 -1
  21. data/lib/picky/category_indexing.rb +7 -5
  22. data/lib/picky/category_realtime.rb +17 -5
  23. data/lib/picky/generators/strategy.rb +4 -0
  24. data/lib/picky/index_indexing.rb +1 -4
  25. data/lib/picky/index_realtime.rb +16 -6
  26. data/lib/picky/indexers/base.rb +7 -1
  27. data/lib/picky/indexes.rb +1 -0
  28. data/lib/picky/loader.rb +11 -7
  29. data/lib/picky/query/allocation.rb +1 -1
  30. data/lib/picky/query/indexes.rb +2 -2
  31. data/lib/picky/query/token.rb +1 -1
  32. data/lib/picky/search.rb +20 -8
  33. data/lib/picky/tokenizer.rb +6 -6
  34. data/lib/picky/wrappers/bundle/delegators.rb +3 -1
  35. data/spec/category_realtime_spec.rb +33 -0
  36. data/spec/functional/backends/file_spec.rb +98 -0
  37. data/spec/functional/backends/memory_spec.rb +96 -0
  38. data/spec/functional/backends/redis_spec.rb +107 -0
  39. data/spec/functional/backends/sqlite_spec.rb +104 -0
  40. data/spec/{specific → functional}/dynamic_weights_spec.rb +0 -0
  41. data/spec/{specific → functional}/exact_first_spec.rb +2 -4
  42. data/spec/functional/max_allocations_spec.rb +33 -0
  43. data/spec/{specific → functional}/realtime_spec.rb +0 -0
  44. data/spec/{specific → functional}/regression_spec.rb +0 -0
  45. data/spec/{specific → functional}/speed_spec.rb +0 -0
  46. data/spec/lib/backends/file/basic_spec.rb +1 -1
  47. data/spec/lib/backends/redis/basic_spec.rb +12 -13
  48. data/spec/lib/backends/redis/directly_manipulable_spec.rb +91 -0
  49. data/spec/lib/backends/redis/float_spec.rb +17 -17
  50. data/spec/lib/backends/redis/list_spec.rb +9 -9
  51. data/spec/lib/backends/sqlite/array_spec.rb +143 -0
  52. data/spec/lib/backends/sqlite/directly_manipulable_spec.rb +65 -0
  53. data/spec/lib/backends/sqlite/{db_spec.rb → value_spec.rb} +2 -7
  54. data/spec/lib/backends/sqlite_spec.rb +22 -20
  55. data/spec/lib/category_indexed_spec.rb +1 -1
  56. data/spec/lib/category_indexing_spec.rb +2 -2
  57. data/spec/lib/index_indexing_spec.rb +0 -7
  58. data/spec/lib/index_realtime_spec.rb +34 -0
  59. data/spec/lib/indexed/bundle_realtime_spec.rb +166 -75
  60. data/spec/lib/indexers/base_spec.rb +13 -1
  61. data/spec/lib/search_spec.rb +31 -20
  62. metadata +58 -34
  63. data/lib/picky/backends/sqlite/db.rb +0 -84
@@ -135,14 +135,14 @@ Case sensitive? #{@case_sensitive ? "Yes." : "-"}
135
135
  !@case_sensitive
136
136
  end
137
137
 
138
- def maximum_tokens amount
139
- @maximum_tokens = amount
138
+ def max_words amount
139
+ @max_words = amount
140
140
  end
141
141
  def cap words
142
- words.slice!(@maximum_tokens..-1) if cap?(words)
142
+ words.slice!(@max_words..-1) if cap?(words)
143
143
  end
144
144
  def cap? words
145
- @maximum_tokens && words.size > @maximum_tokens
145
+ @max_words && words.size > @max_words
146
146
  end
147
147
 
148
148
  # Checks if the right argument type has been given.
@@ -160,7 +160,7 @@ Case sensitive? #{@case_sensitive ? "Yes." : "-"}
160
160
  stopwords options[:stopwords] if options[:stopwords]
161
161
  splits_text_on options[:splits_text_on] || /\s/
162
162
  normalizes_words options[:normalizes_words] if options[:normalizes_words]
163
- maximum_tokens options[:maximum_tokens]
163
+ max_words options[:max_words]
164
164
  rejects_token_if &(options[:rejects_token_if] || :blank?)
165
165
  case_sensitive options[:case_sensitive] unless options[:case_sensitive].nil?
166
166
  end
@@ -203,7 +203,7 @@ Case sensitive? #{@case_sensitive ? "Yes." : "-"}
203
203
  #
204
204
  # Does:
205
205
  # * Split the text into words.
206
- # * Cap the amount of tokens if maximum_tokens is set.
206
+ # * Cap the amount of tokens if max_words is set.
207
207
  #
208
208
  def pretokenize text
209
209
  words = split text
@@ -7,7 +7,6 @@ module Picky
7
7
  module Delegator
8
8
 
9
9
  delegate :add,
10
- :clear_realtime_mapping,
11
10
 
12
11
  :inverted,
13
12
  :weights,
@@ -18,6 +17,8 @@ module Picky
18
17
  :restore,
19
18
  :delete,
20
19
 
20
+ :reset_backend,
21
+
21
22
  :raise_unless_cache_exists,
22
23
  :raise_unless_index_exists,
23
24
  :raise_unless_similarity_exists,
@@ -53,6 +54,7 @@ module Picky
53
54
  :clear_weights,
54
55
  :clear_similarity,
55
56
  :clear_configuration,
57
+ :clear_realtime,
56
58
  :identifier,
57
59
  :ids,
58
60
  :load,
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'spec_helper'
4
+
5
+ describe Picky::Category, "Realtime API" do
6
+
7
+ Thing = Struct.new :id, :text
8
+
9
+ let(:category) do
10
+ index = Picky::Index.new :some_index_name
11
+ category = described_class.new :text, index
12
+ end
13
+
14
+ it 'offers an add method' do
15
+ category.add Thing.new(1, 'text')
16
+ end
17
+ it 'offers a remove method' do
18
+ category.remove 1
19
+ end
20
+ it 'offers a replace method' do
21
+ category.replace Thing.new(1, 'text')
22
+ end
23
+ it 'offers a << method' do
24
+ category << Thing.new(1, 'text')
25
+ end
26
+ # it 'offers a >> method' do
27
+ # Thing.new(1, 'text') >> category # I mean, as long as we're dreaming.
28
+ # end
29
+ it 'offers an unshift method' do
30
+ category.unshift Thing.new(1, 'text')
31
+ end
32
+
33
+ end
@@ -0,0 +1,98 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'spec_helper'
4
+
5
+ # Describes a Picky index that uses the File backend
6
+ # for data storage.
7
+ #
8
+ # TODO Everything should just fail.
9
+ #
10
+ describe Picky::Backends::File do
11
+
12
+ class Book
13
+ attr_reader :id, :title, :author
14
+ def initialize id, title, author
15
+ @id, @title, @author = id, title, author
16
+ end
17
+ end
18
+
19
+ attr_reader :data, :books
20
+
21
+ let(:data) do
22
+ Picky::Index.new(:books) do
23
+ key_format :to_s # TODO Also make to_i work.
24
+ source []
25
+ category :title, partial: Picky::Partial::Postfix.new(from: 1)
26
+ category :author, similarity: Picky::Generators::Similarity::DoubleMetaphone.new(3)
27
+ end
28
+ end
29
+ let(:books) { Picky::Search.new data }
30
+
31
+ its = ->(*) do
32
+ it 'searching for it' do
33
+ books.search('title').ids.should == ['1']
34
+ end
35
+ it 'searching for it using multiple words' do
36
+ books.search('title author').ids.should == ['1']
37
+ end
38
+ it 'searching for it using partial' do
39
+ books.search('tit').ids.should == ['1']
40
+ end
41
+ it 'searching for it using similarity' do
42
+ books.search('aothor~').ids.should == ['1']
43
+ end
44
+ it 'handles removing' do
45
+ data.remove 1
46
+
47
+ books.search('title').ids.should == []
48
+ end
49
+ it 'handles removing with more than one entry' do
50
+ data.add Book.new(2, 'title', 'author')
51
+
52
+ books.search('title').ids.should == ['2', '1'] # TODO Should be ['2', '1']
53
+
54
+ data.remove '1'
55
+
56
+ books.search('title').ids.should == ['2']
57
+ end
58
+ it 'handles removing with three entries' do
59
+ data.add Book.new(2, 'title', 'author')
60
+ data.add Book.new(3, 'title', 'author')
61
+
62
+ books.search('title').ids.should == ['3', '2', '1'] # TODO Should be ['3', '2', '1']
63
+
64
+ data.remove '1'
65
+
66
+ books.search('title').ids.should == ['3', '2']
67
+ end
68
+ it 'handles replacing' do
69
+ data.replace Book.new(1, 'toitle', 'oithor')
70
+
71
+ books.search('title').ids.should == []
72
+ books.search('toitle').ids.should == ['1']
73
+ end
74
+ it 'handles clearing' do
75
+ data.clear
76
+
77
+ books.search('title').ids.should == []
78
+ end
79
+ it 'handles dumping and loading' do
80
+ data.dump
81
+ data.load
82
+
83
+ books.search('title').ids.should == ['1']
84
+ end
85
+ end
86
+
87
+ context 'immediately indexing backend (no dump needed)' do
88
+ before(:each) do
89
+ data.backend described_class.new
90
+ data.clear
91
+
92
+ data.add Book.new(1, 'title', 'author')
93
+ end
94
+
95
+ instance_eval &its
96
+ end
97
+
98
+ end
@@ -0,0 +1,96 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'spec_helper'
4
+
5
+ # Describes a Picky index that uses the Memory backend
6
+ # for data storage.
7
+ #
8
+ describe Picky::Backends::Memory do
9
+
10
+ class Book
11
+ attr_reader :id, :title, :author
12
+ def initialize id, title, author
13
+ @id, @title, @author = id, title, author
14
+ end
15
+ end
16
+
17
+ attr_reader :data, :books
18
+
19
+ let(:data) do
20
+ Picky::Index.new(:books) do
21
+ key_format :to_s # TODO Also make to_i work.
22
+ source []
23
+ category :title, partial: Picky::Partial::Postfix.new(from: 1)
24
+ category :author, similarity: Picky::Generators::Similarity::DoubleMetaphone.new(3)
25
+ end
26
+ end
27
+ let(:books) { Picky::Search.new data }
28
+
29
+ its = ->(*) do
30
+ it 'searching for it' do
31
+ books.search('title').ids.should == ['1']
32
+ end
33
+ it 'searching for it using multiple words' do
34
+ books.search('title author').ids.should == ['1']
35
+ end
36
+ it 'searching for it using partial' do
37
+ books.search('tit').ids.should == ['1']
38
+ end
39
+ it 'searching for it using similarity' do
40
+ books.search('aothor~').ids.should == ['1']
41
+ end
42
+ it 'handles removing' do
43
+ data.remove 1
44
+
45
+ books.search('title').ids.should == []
46
+ end
47
+ it 'handles removing with more than one entry' do
48
+ data.add Book.new(2, 'title', 'author')
49
+
50
+ books.search('title').ids.should == ['2', '1'] # TODO Should be ['2', '1']
51
+
52
+ data.remove '1'
53
+
54
+ books.search('title').ids.should == ['2']
55
+ end
56
+ it 'handles removing with three entries' do
57
+ data.add Book.new(2, 'title', 'author')
58
+ data.add Book.new(3, 'title', 'author')
59
+
60
+ books.search('title').ids.should == ['3', '2', '1'] # TODO Should be ['3', '2', '1']
61
+
62
+ data.remove '1'
63
+
64
+ books.search('title').ids.should == ['3', '2']
65
+ end
66
+ it 'handles replacing' do
67
+ data.replace Book.new(1, 'toitle', 'oithor')
68
+
69
+ books.search('title').ids.should == []
70
+ books.search('toitle').ids.should == ['1']
71
+ end
72
+ it 'handles clearing' do
73
+ data.clear
74
+
75
+ books.search('title').ids.should == []
76
+ end
77
+ it 'handles dumping and loading' do
78
+ data.dump
79
+ data.load
80
+
81
+ books.search('title').ids.should == ['1']
82
+ end
83
+ end
84
+
85
+ context 'immediately indexing backend (no dump needed)' do
86
+ before(:each) do
87
+ data.backend described_class.new
88
+ data.clear
89
+
90
+ data.add Book.new(1, 'title', 'author')
91
+ end
92
+
93
+ instance_eval &its
94
+ end
95
+
96
+ end
@@ -0,0 +1,107 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'spec_helper'
4
+
5
+ # Describes a Picky index that uses the Redis backend
6
+ # for data storage.
7
+ #
8
+ describe Picky::Backends::Redis do
9
+
10
+ class Book
11
+ attr_reader :id, :title, :author
12
+ def initialize id, title, author
13
+ @id, @title, @author = id, title, author
14
+ end
15
+ end
16
+
17
+ attr_reader :data, :books
18
+
19
+ let(:data) do
20
+ Picky::Index.new(:books) do
21
+ key_format :to_s # TODO Also make to_i work.
22
+ source []
23
+ category :title, partial: Picky::Partial::Postfix.new(from: 1)
24
+ category :author, similarity: Picky::Generators::Similarity::DoubleMetaphone.new(3)
25
+ end
26
+ end
27
+ let(:books) { Picky::Search.new data }
28
+
29
+ its = ->(*) do
30
+ it 'searching for it' do
31
+ books.search('title').ids.should == ['1']
32
+ end
33
+ it 'searching for it using multiple words' do
34
+ books.search('title author').ids.should == ['1']
35
+ end
36
+ it 'searching for it using partial' do
37
+ books.search('tit').ids.should == ['1']
38
+ end
39
+ it 'searching for it using similarity' do
40
+ books.search('aothor~').ids.should == ['1']
41
+ end
42
+ it 'handles removing' do
43
+ data.remove 1
44
+
45
+ books.search('title').ids.should == []
46
+ end
47
+ it 'handles removing with more than one entry' do
48
+ data.add Book.new(2, 'title', 'author')
49
+
50
+ books.search('title').ids.should == ['2', '1']
51
+
52
+ data.remove '1'
53
+
54
+ books.search('title').ids.should == ['2']
55
+ end
56
+ it 'handles removing with three entries' do
57
+ data.add Book.new(2, 'title', 'author')
58
+ data.add Book.new(3, 'title', 'author')
59
+
60
+ books.search('title').ids.should == ['3', '2', '1'] # TODO Should be ['3', '2', '1']
61
+
62
+ data.remove '1'
63
+
64
+ books.search('title').ids.should == ['3', '2']
65
+ end
66
+ it 'handles replacing' do
67
+ data.replace Book.new(1, 'toitle', 'oithor')
68
+
69
+ books.search('title').ids.should == []
70
+ books.search('toitle').ids.should == ['1']
71
+ end
72
+ it 'handles clearing' do
73
+ data.clear
74
+
75
+ books.search('title').ids.should == []
76
+ end
77
+ it 'handles dumping and loading' do
78
+ data.dump
79
+ data.load
80
+
81
+ books.search('title').ids.should == ['1']
82
+ end
83
+ end
84
+
85
+ context 'default backend (dump needed)' do
86
+ before(:each) do
87
+ data.backend Picky::Backends::Redis.new
88
+ data.clear
89
+
90
+ data.add Book.new(1, 'title', 'author')
91
+ end
92
+
93
+ instance_eval &its
94
+ end
95
+
96
+ # context 'immediately indexing backend (no dump needed)' do
97
+ # before(:each) do
98
+ # data.backend Picky::Backends::Redis.new(immediate: true)
99
+ # data.clear
100
+ #
101
+ # data.add Book.new(1, 'title', 'author')
102
+ # end
103
+ #
104
+ # instance_eval &its
105
+ # end
106
+
107
+ end
@@ -0,0 +1,104 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'spec_helper'
4
+
5
+ # Describes a Picky index that uses the SQLite backend
6
+ # for data storage.
7
+ #
8
+ describe Picky::Backends::SQLite do
9
+
10
+ class Book
11
+ attr_reader :id, :title, :author
12
+ def initialize id, title, author
13
+ @id, @title, @author = id, title, author
14
+ end
15
+ end
16
+
17
+ let(:data) do
18
+ Picky::Index.new(:books) do
19
+ source []
20
+ category :title, partial: Picky::Partial::Postfix.new(from: 1)
21
+ category :author, similarity: Picky::Generators::Similarity::DoubleMetaphone.new(3)
22
+ end
23
+ end
24
+ let(:books) { Picky::Search.new data }
25
+
26
+ its = ->(*) do
27
+ it 'searching for it' do
28
+ books.search('title').ids.should == [1]
29
+ end
30
+ it 'searching for it using multiple words' do
31
+ books.search('title author').ids.should == [1]
32
+ end
33
+ it 'searching for it using partial' do
34
+ books.search('tit').ids.should == [1]
35
+ end
36
+ it 'searching for it using similarity' do
37
+ books.search('aothor~').ids.should == [1]
38
+ end
39
+ it 'handles removing' do
40
+ data.remove 1
41
+
42
+ books.search('title').ids.should == []
43
+ end
44
+ it 'handles removing with more than one entry' do
45
+ data.add Book.new(2, 'title', 'author')
46
+
47
+ books.search('title').ids.should == [2, 1]
48
+
49
+ data.remove 1
50
+
51
+ books.search('title').ids.should == [2]
52
+ end
53
+ it 'handles clearing' do
54
+ data.clear
55
+
56
+ books.search('title').ids.should == []
57
+ end
58
+ it 'handles dumping and loading' do
59
+ data.dump
60
+ data.load
61
+
62
+ books.search('title').ids.should == [1]
63
+ end
64
+ it 'handles removing with three entries' do
65
+ data.add Book.new(2, 'title', 'author')
66
+ data.add Book.new(3, 'title', 'author')
67
+
68
+ books.search('title').ids.should == [3, 2, 1]
69
+
70
+ data.remove 1
71
+
72
+ books.search('title').ids.should == [3, 2]
73
+ end
74
+ it 'handles replacing' do
75
+ data.replace Book.new(1, 'toitle', 'oithor')
76
+
77
+ books.search('title').ids.should == []
78
+ books.search('toitle').ids.should == [1]
79
+ end
80
+ end
81
+
82
+ context 'default backend (dump needed)' do
83
+ before(:each) do
84
+ data.backend described_class.new
85
+ data.clear
86
+
87
+ data.add Book.new(1, 'title', 'author')
88
+ end
89
+
90
+ instance_eval &its
91
+ end
92
+
93
+ # context 'immediately indexing backend (no dump needed)' do
94
+ # before(:each) do
95
+ # data.backend described_class.new(self_indexed: true)
96
+ # data.clear
97
+ #
98
+ # data.add Book.new(1, 'title', 'author')
99
+ # end
100
+ #
101
+ # instance_eval &its
102
+ # end
103
+
104
+ end
@@ -2,11 +2,9 @@
2
2
  #
3
3
  require 'spec_helper'
4
4
 
5
- describe "Weights" do
5
+ describe "exact first" do
6
6
 
7
- # This tests the weights option.
8
- #
9
- it 'can handle dynamic weights' do
7
+ it 'returns exact results first' do
10
8
  index = Picky::Index.new :exact_first do
11
9
  source { [] }
12
10
  category :text, partial: Picky::Partial::Substring.new(from: 1)
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'spec_helper'
4
+
5
+ describe 'Search#max_allocations' do
6
+
7
+ it 'offers the option max_allocations' do
8
+ index = Picky::Index.new :dynamic_weights do
9
+ category :text1
10
+ category :text2
11
+ end
12
+
13
+ index.add Struct.new(:id, :text1, :text2).new(1, 'hello world', 'hello world')
14
+
15
+ try = Picky::Search.new index
16
+
17
+ try.search('hello world').allocations.size.should == 4
18
+ try.search('hello world').ids.should == [1,1,1,1]
19
+
20
+ try_again = Picky::Search.new index do
21
+ max_allocations 2
22
+ end
23
+
24
+ try_again.search('hello world').allocations.size.should == 2
25
+ try_again.search('hello world').ids.should == [1,1]
26
+
27
+ try_again.max_allocations 1
28
+
29
+ try_again.search('hello world').allocations.size.should == 1
30
+ try_again.search('hello world').ids.should == [1]
31
+ end
32
+
33
+ end
File without changes
File without changes
File without changes
@@ -13,7 +13,7 @@ describe Picky::Backends::File::Basic do
13
13
 
14
14
  describe 'initial' do
15
15
  it 'returns the container that is used for indexing' do
16
- basic.initial.should == nil
16
+ basic.initial.should == {}
17
17
  end
18
18
  end
19
19
 
@@ -5,39 +5,38 @@ describe Picky::Backends::Redis::Basic do
5
5
  let(:client) { stub :client }
6
6
 
7
7
  context 'without options' do
8
- let(:index) { described_class.new client, :some_namespace }
8
+ let(:backend) { described_class.new client, :some_namespace }
9
9
 
10
10
  describe 'load, retrieve, delete' do
11
11
  it 'is nothing they do (at least on the backend)' do
12
- index.should_receive(:client).never
12
+ backend.should_receive(:client).never
13
13
 
14
- index.load
15
- index.retrieve
16
- index.delete
14
+ backend.load
15
+ backend.retrieve
17
16
  end
18
17
  end
19
18
 
20
19
  describe 'empty' do
21
- it 'returns the container that is used for indexing' do
22
- index.empty.should == {}
20
+ it 'returns the container that is used for backending' do
21
+ backend.empty.should == {}
23
22
  end
24
23
  end
25
24
 
26
25
  describe 'initial' do
27
26
  it 'is correct' do
28
- index.initial.class.should == described_class
27
+ backend.initial.should == {}
29
28
  end
30
29
  end
31
30
 
32
31
  describe 'to_s' do
33
32
  it 'returns the cache path with the default file extension' do
34
- index.to_s.should == 'Picky::Backends::Redis::Basic(some_namespace:*)'
33
+ backend.to_s.should == 'Picky::Backends::Redis::Basic(some_namespace:*)'
35
34
  end
36
35
  end
37
36
  end
38
37
 
39
38
  context 'with options' do
40
- let(:index) do
39
+ let(:backend) do
41
40
  described_class.new client,
42
41
  :some_namespace,
43
42
  empty: [],
@@ -45,14 +44,14 @@ describe Picky::Backends::Redis::Basic do
45
44
  end
46
45
 
47
46
  describe 'empty' do
48
- it 'returns the container that is used for indexing' do
49
- index.empty.should == []
47
+ it 'returns the container that is used for backending' do
48
+ backend.empty.should == []
50
49
  end
51
50
  end
52
51
 
53
52
  describe 'initial' do
54
53
  it 'is correct' do
55
- index.initial.class.should == Array
54
+ backend.initial.class.should == Array
56
55
  end
57
56
  end
58
57
  end