supernova 0.2.2 → 0.3.0
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/TODO +3 -0
- data/VERSION +1 -1
- data/lib/supernova.rb +2 -0
- data/lib/supernova/condition.rb +27 -0
- data/lib/supernova/criteria.rb +19 -7
- data/lib/supernova/solr_criteria.rb +45 -8
- data/lib/supernova/solr_indexer.rb +120 -1
- data/lib/supernova/symbol_extensions.rb +7 -0
- data/lib/supernova/thinking_sphinx_criteria.rb +1 -1
- data/spec/integration/solr_spec.rb +54 -2
- data/spec/integration/thinking_sphinx_spec.rb +1 -1
- data/spec/supernova/condition_spec.rb +41 -0
- data/spec/supernova/criteria_spec.rb +17 -2
- data/spec/supernova/solr_criteria_spec.rb +79 -2
- data/spec/supernova/solr_indexer_spec.rb +277 -3
- data/spec/supernova/symbol_extensions_spec.rb +15 -0
- data/supernova.gemspec +8 -3
- metadata +10 -4
data/TODO
ADDED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/lib/supernova.rb
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
class Supernova::Condition
|
2
|
+
attr_accessor :key, :type
|
3
|
+
|
4
|
+
def initialize(key, type)
|
5
|
+
self.key = key
|
6
|
+
self.type = type
|
7
|
+
end
|
8
|
+
|
9
|
+
def solr_filter_for(value)
|
10
|
+
case type
|
11
|
+
when :not, :ne
|
12
|
+
if value.nil?
|
13
|
+
"#{self.key}:[* TO *]"
|
14
|
+
else
|
15
|
+
"!#{self.key}:#{value}"
|
16
|
+
end
|
17
|
+
when :gt
|
18
|
+
"#{self.key}:{#{value} TO *}"
|
19
|
+
when :gte
|
20
|
+
"#{self.key}:[#{value} TO *]"
|
21
|
+
when :lt
|
22
|
+
"#{self.key}:{* TO #{value}}"
|
23
|
+
when :lte
|
24
|
+
"#{self.key}:[* TO #{value}]"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/supernova/criteria.rb
CHANGED
@@ -28,6 +28,10 @@ class Supernova::Criteria
|
|
28
28
|
def for_classes(clazzes)
|
29
29
|
merge_filters :classes, [clazzes].flatten
|
30
30
|
end
|
31
|
+
|
32
|
+
def attribute_mapping(mapping)
|
33
|
+
merge_search_options :attribute_mapping, mapping
|
34
|
+
end
|
31
35
|
|
32
36
|
def order(order_option)
|
33
37
|
merge_search_options :order, order_option
|
@@ -41,14 +45,18 @@ class Supernova::Criteria
|
|
41
45
|
merge_search_options :group_by, group_option
|
42
46
|
end
|
43
47
|
|
44
|
-
def search(
|
45
|
-
|
48
|
+
def search(*terms)
|
49
|
+
merge_filters_array :search, terms
|
46
50
|
end
|
47
51
|
|
48
52
|
def with(filters)
|
49
53
|
merge_filters :with, filters
|
50
54
|
end
|
51
55
|
|
56
|
+
def where(*args)
|
57
|
+
with(*args)
|
58
|
+
end
|
59
|
+
|
52
60
|
def without(filters)
|
53
61
|
self.filters[:without] ||= Hash.new
|
54
62
|
filters.each do |key, value|
|
@@ -59,11 +67,7 @@ class Supernova::Criteria
|
|
59
67
|
end
|
60
68
|
|
61
69
|
def select(*fields)
|
62
|
-
|
63
|
-
fields.flatten.each do |field|
|
64
|
-
self.search_options[:select] << field if !self.search_options[:select].include?(field)
|
65
|
-
end
|
66
|
-
self
|
70
|
+
merge_filters_array :select, fields
|
67
71
|
end
|
68
72
|
|
69
73
|
def conditions(filters)
|
@@ -105,6 +109,14 @@ class Supernova::Criteria
|
|
105
109
|
def merge_filters(key, value)
|
106
110
|
merge_filters_or_search_options(self.filters, key, value)
|
107
111
|
end
|
112
|
+
|
113
|
+
def merge_filters_array(key, fields)
|
114
|
+
self.search_options[key] ||= Array.new
|
115
|
+
fields.flatten.each do |field|
|
116
|
+
self.search_options[key] << field if !self.search_options[key].include?(field)
|
117
|
+
end
|
118
|
+
self
|
119
|
+
end
|
108
120
|
|
109
121
|
def merge_search_options(key, value)
|
110
122
|
merge_filters_or_search_options(self.search_options, key, value)
|
@@ -1,26 +1,29 @@
|
|
1
1
|
require "rsolr"
|
2
2
|
|
3
3
|
class Supernova::SolrCriteria < Supernova::Criteria
|
4
|
+
# move this into separate methods (test each separatly)
|
4
5
|
def to_params
|
5
6
|
solr_options = { :fq => [], :q => "*:*" }
|
6
|
-
solr_options[:fq] += self.filters[:with]
|
7
|
+
solr_options[:fq] += fq_from_with(self.filters[:with])
|
7
8
|
if self.filters[:without]
|
8
|
-
self.filters[:without].each do |
|
9
|
-
solr_options[:fq] += values.map { |value| "!#{
|
9
|
+
self.filters[:without].each do |field, values|
|
10
|
+
solr_options[:fq] += values.map { |value| "!#{solr_field_from_field(field)}:#{value}" }
|
10
11
|
end
|
11
12
|
end
|
12
|
-
solr_options[:sort] = self.search_options[:order] if self.search_options[:order]
|
13
|
-
|
13
|
+
solr_options[:sort] = convert_search_order(self.search_options[:order]) if self.search_options[:order]
|
14
|
+
if self.search_options[:search].is_a?(Array)
|
15
|
+
solr_options[:q] = self.search_options[:search].map { |query| "(#{query})" }.join(" AND ")
|
16
|
+
end
|
14
17
|
|
15
18
|
if self.search_options[:geo_center] && self.search_options[:geo_distance]
|
16
19
|
solr_options[:pt] = "#{self.search_options[:geo_center][:lat]},#{self.search_options[:geo_center][:lng]}"
|
17
20
|
solr_options[:d] = self.search_options[:geo_distance].to_f / Supernova::KM_TO_METER
|
18
|
-
solr_options[:sfield] = :location
|
21
|
+
solr_options[:sfield] = solr_field_from_field(:location)
|
19
22
|
solr_options[:fq] << "{!geofilt}"
|
20
23
|
end
|
21
24
|
if self.search_options[:select]
|
22
25
|
self.search_options[:select] << :id
|
23
|
-
solr_options[:fl] = self.search_options[:select].compact.join(",")
|
26
|
+
solr_options[:fl] = self.search_options[:select].compact.map { |field| solr_field_from_field(field) }.join(",")
|
24
27
|
end
|
25
28
|
solr_options[:fq] << "type:#{self.clazz}" if self.clazz
|
26
29
|
|
@@ -31,6 +34,39 @@ class Supernova::SolrCriteria < Supernova::Criteria
|
|
31
34
|
solr_options
|
32
35
|
end
|
33
36
|
|
37
|
+
def convert_search_order(order)
|
38
|
+
asc_or_desc = nil
|
39
|
+
field = solr_field_from_field(order)
|
40
|
+
if order.match(/^(.*?) (asc|desc)/i)
|
41
|
+
field = solr_field_from_field($1)
|
42
|
+
asc_or_desc = $2
|
43
|
+
end
|
44
|
+
[field, asc_or_desc].compact.join(" ")
|
45
|
+
end
|
46
|
+
|
47
|
+
def solr_field_from_field(field)
|
48
|
+
Supernova::SolrIndexer.solr_field_for_field_name_and_mapping(field, search_options[:attribute_mapping])
|
49
|
+
end
|
50
|
+
|
51
|
+
def fq_from_with(with)
|
52
|
+
if with.blank?
|
53
|
+
[]
|
54
|
+
else
|
55
|
+
with.map do |key_or_condition, value|
|
56
|
+
if key_or_condition.respond_to?(:solr_filter_for)
|
57
|
+
key_or_condition.key = solr_field_from_field(key_or_condition.key)
|
58
|
+
key_or_condition.solr_filter_for(value)
|
59
|
+
else
|
60
|
+
fq_filter_for_key_and_value(solr_field_from_field(key_or_condition), value)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def fq_filter_for_key_and_value(key, value)
|
67
|
+
"#{key}:#{value.is_a?(Range) ? "[#{value.first} TO #{value.last}]" : value}"
|
68
|
+
end
|
69
|
+
|
34
70
|
def build_docs(docs)
|
35
71
|
docs.map do |hash|
|
36
72
|
self.search_options[:build_doc_method] ? self.search_options[:build_doc_method].call(hash) : build_doc(hash)
|
@@ -42,8 +78,9 @@ class Supernova::SolrCriteria < Supernova::Criteria
|
|
42
78
|
end
|
43
79
|
|
44
80
|
def build_doc(hash)
|
45
|
-
return hash if hash["type"].
|
81
|
+
return hash if !hash["type"].respond_to?(:constantize)
|
46
82
|
doc = hash["type"].constantize.new
|
83
|
+
doc.instance_variable_set("@solr_doc", hash)
|
47
84
|
hash.each do |key, value|
|
48
85
|
if key == "id"
|
49
86
|
doc.id = value.to_s.split("/").last if doc.respond_to?(:id=)
|
@@ -1,14 +1,129 @@
|
|
1
1
|
require "json"
|
2
2
|
|
3
3
|
class Supernova::SolrIndexer
|
4
|
-
attr_accessor :options, :db
|
4
|
+
attr_accessor :options, :db, :ids
|
5
5
|
attr_writer :index_file_path
|
6
6
|
|
7
|
+
class << self
|
8
|
+
def field_definitions
|
9
|
+
@field_definitions ||= {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def has(key, attributes)
|
13
|
+
field_definitions[key] = attributes
|
14
|
+
end
|
15
|
+
|
16
|
+
def clazz(class_name =:only_return)
|
17
|
+
@clazz = class_name if class_name != :only_return
|
18
|
+
@clazz
|
19
|
+
end
|
20
|
+
|
21
|
+
def table_name(name = :only_return)
|
22
|
+
@table_name = name if name != :only_return
|
23
|
+
@table_name
|
24
|
+
end
|
25
|
+
|
26
|
+
def method_missing(*args)
|
27
|
+
criteria = Supernova::SolrCriteria.new(self.clazz).attribute_mapping(self.field_definitions)
|
28
|
+
if criteria.respond_to?(args.first)
|
29
|
+
criteria.send(*args)
|
30
|
+
else
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
FIELD_SUFFIX_MAPPING = {
|
37
|
+
:raw => nil,
|
38
|
+
:string => :s,
|
39
|
+
:text => :t,
|
40
|
+
:int => :i,
|
41
|
+
:integer => :i,
|
42
|
+
:sint => :si,
|
43
|
+
:float => :f,
|
44
|
+
:date => :dt,
|
45
|
+
:boolean => :b,
|
46
|
+
:location => :p
|
47
|
+
}
|
48
|
+
|
7
49
|
def initialize(options = {})
|
8
50
|
options.each do |key, value|
|
9
51
|
self.send(:"#{key}=", value) if self.respond_to?(:"#{key}=")
|
10
52
|
end
|
11
53
|
self.options = options
|
54
|
+
self.ids ||= :all
|
55
|
+
end
|
56
|
+
|
57
|
+
def index!
|
58
|
+
index_query(query_to_index) do |row|
|
59
|
+
row_to_solr(row)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def row_to_solr(row)
|
64
|
+
row
|
65
|
+
end
|
66
|
+
|
67
|
+
def table_name
|
68
|
+
self.class.table_name || (self.class.clazz && self.class.clazz.respond_to?(:table_name) ? self.class.clazz.table_name : nil)
|
69
|
+
end
|
70
|
+
|
71
|
+
def query_to_index
|
72
|
+
raise "no table_name defined" if self.table_name.nil?
|
73
|
+
query = "SELECT #{select_fields.join(", ")} FROM #{self.table_name}"
|
74
|
+
query << " WHERE id IN (#{ids.join(", ")})" if ids_given?
|
75
|
+
query
|
76
|
+
end
|
77
|
+
|
78
|
+
def default_fields
|
79
|
+
fields = ["id"]
|
80
|
+
fields << %("#{self.class.clazz}" AS type_s) if self.class.clazz
|
81
|
+
fields
|
82
|
+
end
|
83
|
+
|
84
|
+
def defined_fields
|
85
|
+
self.class.field_definitions.map do |field, options|
|
86
|
+
sql_column_from_field_and_type(field, options[:type]) if options[:virtual] != true
|
87
|
+
end.compact
|
88
|
+
end
|
89
|
+
|
90
|
+
def select_fields
|
91
|
+
default_fields + defined_fields
|
92
|
+
end
|
93
|
+
|
94
|
+
def validate_lat(lat)
|
95
|
+
float_or_nil_when_abs_bigger_than(lat, 90)
|
96
|
+
end
|
97
|
+
|
98
|
+
def validate_lng(lng)
|
99
|
+
float_or_nil_when_abs_bigger_than(lng, 180)
|
100
|
+
end
|
101
|
+
|
102
|
+
def float_or_nil_when_abs_bigger_than(value, border)
|
103
|
+
return nil if value.to_s.strip.length == 0
|
104
|
+
value_f = value.to_f
|
105
|
+
value_f.abs > border ? nil : value_f
|
106
|
+
end
|
107
|
+
|
108
|
+
def sql_column_from_field_and_type(field, type)
|
109
|
+
return sql_date_column_from_field(field) if type == :date
|
110
|
+
if suffix = self.class.suffix_from_type(type)
|
111
|
+
"#{field} AS #{field}_#{suffix}"
|
112
|
+
else
|
113
|
+
raise "no suffix for #{type} defined"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.suffix_from_type(type)
|
118
|
+
FIELD_SUFFIX_MAPPING[type.to_sym]
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.solr_field_for_field_name_and_mapping(field, mapping)
|
122
|
+
[field, mapping && mapping[field.to_sym] ? suffix_from_type(mapping[field.to_sym][:type]) : nil].compact.join("_")
|
123
|
+
end
|
124
|
+
|
125
|
+
def sql_date_column_from_field(field)
|
126
|
+
%(IF(#{field} IS NULL, NULL, CONCAT(REPLACE(#{field}, " ", "T"), "Z")) AS #{field}_dt)
|
12
127
|
end
|
13
128
|
|
14
129
|
def query_db(query)
|
@@ -23,6 +138,10 @@ class Supernova::SolrIndexer
|
|
23
138
|
finish
|
24
139
|
end
|
25
140
|
|
141
|
+
def ids_given?
|
142
|
+
self.ids.is_a?(Array)
|
143
|
+
end
|
144
|
+
|
26
145
|
def index_file_path
|
27
146
|
@index_file_path ||= File.expand_path("/tmp/index_file_#{Time.now.to_i}.json")
|
28
147
|
end
|
@@ -31,7 +31,7 @@ class Supernova::ThinkingSphinxCriteria < Supernova::Criteria
|
|
31
31
|
sphinx_options[:geo] = [self.search_options[:geo_center][:lat].to_radians, self.search_options[:geo_center][:lng].to_radians]
|
32
32
|
sphinx_options[:with]["@geodist"] = self.search_options[:geo_distance].is_a?(Range) ? self.search_options[:geo_distance] : Range.new(0.0, self.search_options[:geo_distance])
|
33
33
|
end
|
34
|
-
[self.
|
34
|
+
[(self.search_options[:search] || Array.new).join(" "), sphinx_options]
|
35
35
|
end
|
36
36
|
|
37
37
|
def to_a
|
@@ -7,12 +7,14 @@ describe "Solr" do
|
|
7
7
|
Supernova::Solr.truncate!
|
8
8
|
Offer.criteria_class = Supernova::SolrCriteria
|
9
9
|
root = Geokit::LatLng.new(47, 11)
|
10
|
-
endpoint = root.endpoint(90, 50, :units => :kms)
|
10
|
+
# endpoint = root.endpoint(90, 50, :units => :kms)
|
11
|
+
e_lat = 46.9981112912042
|
12
|
+
e_lng = 11.6587158814378
|
11
13
|
Supernova::Solr.connection.add(:id => "offers/1", :type => "Offer", :user_id => 1, :enabled => false, :text => "Hans Meyer", :popularity => 10,
|
12
14
|
:location => "#{root.lat},#{root.lng}", :type => "Offer"
|
13
15
|
)
|
14
16
|
Supernova::Solr.connection.add(:id => "offers/2", :user_id => 2, :enabled => true, :text => "Marek Mintal", :popularity => 1,
|
15
|
-
:location => "#{
|
17
|
+
:location => "#{e_lat},#{e_lng}", :type => "Offer"
|
16
18
|
)
|
17
19
|
Supernova::Solr.connection.commit
|
18
20
|
end
|
@@ -44,6 +46,25 @@ describe "Solr" do
|
|
44
46
|
new_criteria.to_a.per_page.should == 25
|
45
47
|
end
|
46
48
|
|
49
|
+
describe "plain text search" do
|
50
|
+
it "returns the correct entries for 1 term" do
|
51
|
+
new_criteria.search("Hans").to_a.map { |h| h["id"] }.should == [1]
|
52
|
+
new_criteria.search("Hans").search("Meyer").to_a.map { |h| h["id"] }.should == [1]
|
53
|
+
new_criteria.search("Marek").to_a.map { |h| h["id"] }.should == [2]
|
54
|
+
end
|
55
|
+
|
56
|
+
it "returns the correct options for a combined search" do
|
57
|
+
new_criteria.search("Hans", "Marek").to_a.map.should == []
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
it "includes the returned solr_doc" do
|
62
|
+
new_criteria.search("Hans").to_a.first.instance_variable_get("@solr_doc").should == {
|
63
|
+
"id" => "offers/1", "type" => "Offer", "user_id" => 1, "enabled" => [false], "text" => "Hans Meyer", "popularity" => 10,
|
64
|
+
"location" => "47,11", "type" => "Offer"
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
47
68
|
describe "nearby search" do
|
48
69
|
{ 49.kms => 1, 51.kms => 2 }.each do |distance, total_entries|
|
49
70
|
it "returns #{total_entries} for distance #{distance}" do
|
@@ -52,6 +73,37 @@ describe "Solr" do
|
|
52
73
|
end
|
53
74
|
end
|
54
75
|
|
76
|
+
describe "range search" do
|
77
|
+
{ Range.new(2, 3) => [2], Range.new(3, 10) => [], Range.new(1, 2) => [1, 2] }.each do |range, ids|
|
78
|
+
it "returns #{ids.inspect} for range #{range.inspect}" do
|
79
|
+
new_criteria.with(:user_id => range).map { |doc| doc["id"] }.sort.should == ids
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "not searches" do
|
85
|
+
it "finds the correct documents for not nil" do
|
86
|
+
Supernova::Solr.connection.add(:id => "offers/3", :enabled => true, :text => "Marek Mintal", :popularity => 1,
|
87
|
+
:type => "Offer"
|
88
|
+
)
|
89
|
+
Supernova::Solr.connection.commit
|
90
|
+
raise "There should be 3 docs" if new_criteria.to_a.total_entries != 3
|
91
|
+
new_criteria.with(:user_id.not => nil).to_a.map { |h| h["id"] }.should == [1, 2]
|
92
|
+
end
|
93
|
+
|
94
|
+
it "finds the correct values for not specific value" do
|
95
|
+
new_criteria.with(:user_id.not => 1).to_a.map { |h| h["id"] }.should == [2]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "gt and lt searches" do
|
100
|
+
{ :gt => [2], :gte => [1, 2], :lt => [], :lte => [1] }.each do |type, ids|
|
101
|
+
it "finds ids #{ids.inspect} for #{type}" do
|
102
|
+
new_criteria.with(:user_id.send(type) => 1).to_a.map { |row| row["id"] }.sort.should == ids
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
55
107
|
it "returns the correct objects" do
|
56
108
|
new_criteria.with(:user_id => 1).to_a.first.should be_an_instance_of(Offer)
|
57
109
|
end
|
@@ -23,7 +23,7 @@ describe "ThinkingSphinx" do
|
|
23
23
|
@offer1 = Offer.create!(:id => 1, :user_id => 1, :enabled => false, :text => "Hans Meyer", :popularity => 10, :lat => root.lat, :lng => root.lng)
|
24
24
|
@offer2 = Offer.create!(:id => 2, :user_id => 2, :enabled => true, :text => "Marek Mintal", :popularity => 1, :lat => endpoint.lat, :lng => endpoint.lng)
|
25
25
|
ts.controller.index
|
26
|
-
sleep 0.
|
26
|
+
sleep 0.2
|
27
27
|
end
|
28
28
|
|
29
29
|
it "finds the correct objects" do
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Supernova::Condition" do
|
4
|
+
it "can be initialize" do
|
5
|
+
cond = Supernova::Condition.new(:user_id, :not)
|
6
|
+
cond.key.should == :user_id
|
7
|
+
cond.type.should == :not
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "solr_filter_for" do
|
11
|
+
it "returns the correct filter for numbers" do
|
12
|
+
:user_id.not.solr_filter_for(7).should == "!user_id:7"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "returns the correct filter for numbers" do
|
16
|
+
:user_id.ne.solr_filter_for(7).should == "!user_id:7"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "returns the correct filter for not nil" do
|
20
|
+
:user_id.not.solr_filter_for(nil).should == "user_id:[* TO *]"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "returns the correct filter for gt" do
|
24
|
+
:user_id.gt.solr_filter_for(1).should == "user_id:{1 TO *}"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "returns the correct filter for gte" do
|
28
|
+
:user_id.gte.solr_filter_for(1).should == "user_id:[1 TO *]"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "returns the correct filter for lt" do
|
32
|
+
:user_id.lt.solr_filter_for(1).should == "user_id:{* TO 1}"
|
33
|
+
end
|
34
|
+
|
35
|
+
it "returns the correct filter for lt" do
|
36
|
+
:user_id.lte.solr_filter_for(1).should == "user_id:[* TO 1]"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
end
|
@@ -59,7 +59,7 @@ describe "Supernova::Criteria" do
|
|
59
59
|
|
60
60
|
describe "#search" do
|
61
61
|
it "sets the query" do
|
62
|
-
scope.search("title").
|
62
|
+
scope.search("title").search_options[:search].should == ["title"]
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
@@ -180,6 +180,21 @@ describe "Supernova::Criteria" do
|
|
180
180
|
end
|
181
181
|
end
|
182
182
|
|
183
|
+
describe "#where" do
|
184
|
+
it "delegates to with" do
|
185
|
+
ret = double("ret")
|
186
|
+
scope.should_receive(:with).with(:a => 9).and_return ret
|
187
|
+
scope.where(:a => 9).should == ret
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe "#attribute_mapping" do
|
192
|
+
it "sets the attribute_mapping option" do
|
193
|
+
mapping = { :title => { :type => :integer } }
|
194
|
+
scope.attribute_mapping(mapping).search_options[:attribute_mapping].should == mapping
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
183
198
|
describe "#merge" do
|
184
199
|
let(:criteria) { Supernova::Criteria.new.order("popularity asc").with(:a => 1).conditions(:b => 2).search("New Search") }
|
185
200
|
let(:new_crit) { Supernova::Criteria.new.order("popularity desc").with(:c => 8).conditions(:e => 9).search("Search") }
|
@@ -201,7 +216,7 @@ describe "Supernova::Criteria" do
|
|
201
216
|
end
|
202
217
|
|
203
218
|
it "merges search search" do
|
204
|
-
new_crit.merge(criteria).
|
219
|
+
new_crit.merge(criteria).search_options[:search].should == ["New Search"]
|
205
220
|
end
|
206
221
|
|
207
222
|
it "calls merge on options" do
|
@@ -19,6 +19,16 @@ describe Supernova::SolrCriteria do
|
|
19
19
|
Supernova::Solr.stub!(:connection).and_return rsolr
|
20
20
|
end
|
21
21
|
|
22
|
+
describe "#fq_from_with" do
|
23
|
+
it "returns the correct filter for with ranges" do
|
24
|
+
criteria.fq_from_with(:user_id => Range.new(10, 12)).should == ["user_id:[10 TO 12]"]
|
25
|
+
end
|
26
|
+
|
27
|
+
it "returns the correct filter for not queries" do
|
28
|
+
criteria.fq_from_with(:user_id.not => nil).should == ["user_id:[* TO *]"]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
22
32
|
describe "#to_params" do
|
23
33
|
it "returns a Hash" do
|
24
34
|
criteria.to_params.should be_an_instance_of(Hash)
|
@@ -36,10 +46,26 @@ describe Supernova::SolrCriteria do
|
|
36
46
|
criteria.order("title").to_params[:sort].should == "title"
|
37
47
|
end
|
38
48
|
|
49
|
+
it "uses a mapped field for order" do
|
50
|
+
criteria.attribute_mapping(:title => { :type => :string }).order("title").to_params[:sort].should == "title_s"
|
51
|
+
end
|
52
|
+
|
53
|
+
%w(asc desc).each do |order|
|
54
|
+
it "uses a mapped field for order even when #{order} is present" do
|
55
|
+
criteria.attribute_mapping(:title => { :type => :string }).order("title #{order}").to_params[:sort].should == "title_s #{order}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
39
60
|
it "sets search correct search query" do
|
40
|
-
criteria.search("some query").to_params[:q].should == "some query"
|
61
|
+
criteria.search("some query").to_params[:q].should == "(some query)"
|
41
62
|
end
|
42
63
|
|
64
|
+
it "joins the search terms with AND" do
|
65
|
+
criteria.search("some", "query").to_params[:q].should == "(some) AND (query)"
|
66
|
+
end
|
67
|
+
|
68
|
+
# fix me: use type_s
|
43
69
|
it "adds a filter on type when clazz set" do
|
44
70
|
Supernova::SolrCriteria.new(Offer).to_params[:fq].should == ["type:#{Offer}"]
|
45
71
|
end
|
@@ -52,11 +78,23 @@ describe Supernova::SolrCriteria do
|
|
52
78
|
criteria.select(:user_id).select(:user_id).select(:enabled).to_params[:fl].should == "user_id,enabled,id"
|
53
79
|
end
|
54
80
|
|
81
|
+
it "uses mapped fields for select" do
|
82
|
+
mapping = {
|
83
|
+
:user_id => { :type => :integer },
|
84
|
+
:enabled => { :type => :boolean }
|
85
|
+
}
|
86
|
+
criteria.attribute_mapping(mapping).select(:user_id, :enabled).to_params[:fl].should == "user_id_i,enabled_b,id"
|
87
|
+
end
|
88
|
+
|
55
89
|
it "adds all without filters" do
|
56
90
|
criteria.without(:user_id => 1).to_params[:fq].should == ["!user_id:1"]
|
57
91
|
criteria.without(:user_id => 1).without(:user_id => 1).without(:user_id => 2).to_params[:fq].sort.should == ["!user_id:1", "!user_id:2"]
|
58
92
|
end
|
59
93
|
|
94
|
+
it "uses mapped fields for without" do
|
95
|
+
criteria.attribute_mapping(:user_id => { :type => :integer }).without(:user_id => 1).to_params[:fq].should == ["!user_id_i:1"]
|
96
|
+
end
|
97
|
+
|
60
98
|
describe "with a nearby search" do
|
61
99
|
let(:nearby_criteria) { Supernova::SolrCriteria.new.near(47, 11).within(10.kms) }
|
62
100
|
|
@@ -69,7 +107,11 @@ describe Supernova::SolrCriteria do
|
|
69
107
|
end
|
70
108
|
|
71
109
|
it "sets the sfield to location" do
|
72
|
-
nearby_criteria.to_params[:sfield].should ==
|
110
|
+
nearby_criteria.to_params[:sfield].should == "location"
|
111
|
+
end
|
112
|
+
|
113
|
+
it "uses the mapped field when mapping defined" do
|
114
|
+
nearby_criteria.attribute_mapping(:location => { :type => :location }).to_params[:sfield].should == "location_p"
|
73
115
|
end
|
74
116
|
|
75
117
|
it "sets the fq field to {!geofilt}" do
|
@@ -94,6 +136,30 @@ describe Supernova::SolrCriteria do
|
|
94
136
|
criteria.paginate(:per_page => 10, :page => 2).to_params[:start].should == 10
|
95
137
|
end
|
96
138
|
end
|
139
|
+
|
140
|
+
describe "with attribute mapping" do
|
141
|
+
it "uses the mapped fields" do
|
142
|
+
criteria.attribute_mapping(:artist_name => { :type => :string }).where(:artist_name => "test").to_params[:fq].should == ["artist_name_s:test"]
|
143
|
+
end
|
144
|
+
|
145
|
+
it "uses the mapped fields for all criteria queries" do
|
146
|
+
criteria.attribute_mapping(:artist_name => { :type => :string }).where(:artist_name.ne => nil).to_params[:fq].should == ["artist_name_s:[* TO *]"]
|
147
|
+
end
|
148
|
+
|
149
|
+
it "uses the column when no mapping defined" do
|
150
|
+
criteria.where(:artist_name => "test").to_params[:fq].should == ["artist_name:test"]
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "#solr_field_from_field" do
|
156
|
+
it "returns the field when no mappings defined" do
|
157
|
+
criteria.solr_field_from_field(:artist_name).should == "artist_name"
|
158
|
+
end
|
159
|
+
|
160
|
+
it "returns the mapped field when mapping found" do
|
161
|
+
criteria.attribute_mapping(:artist_name => { :type => :string }).solr_field_from_field(:artist_name).should == "artist_name_s"
|
162
|
+
end
|
97
163
|
end
|
98
164
|
|
99
165
|
describe "#to_a" do
|
@@ -201,6 +267,17 @@ describe Supernova::SolrCriteria do
|
|
201
267
|
end
|
202
268
|
end
|
203
269
|
|
270
|
+
it "returns a Hash when type does not response to " do
|
271
|
+
type = double("type")
|
272
|
+
type.should_receive(:respond_to?).with(:constantize).and_return false
|
273
|
+
criteria.build_doc("type" => type).should be_an_instance_of(Hash)
|
274
|
+
end
|
275
|
+
|
276
|
+
it "sets the original solr_doc" do
|
277
|
+
solr_doc = { "type" => "Offer", "id" => "offers/id" }
|
278
|
+
criteria.build_doc(solr_doc).instance_variable_get("@solr_doc").should == solr_doc
|
279
|
+
end
|
280
|
+
|
204
281
|
it "should be readonly" do
|
205
282
|
criteria.build_doc(docs.first).should be_readonly
|
206
283
|
end
|
@@ -1,18 +1,27 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe Supernova::SolrIndexer do
|
4
|
-
|
4
|
+
let(:indexer_clazz) { Class.new(Supernova::SolrIndexer) }
|
5
5
|
let(:db) { double("db", :query => [to_index]) }
|
6
6
|
let(:to_index) { { :id => 1, :title => "Some Title"} }
|
7
7
|
let(:file_stub) { double("file").as_null_object }
|
8
8
|
|
9
|
-
let(:indexer)
|
9
|
+
let(:indexer) do
|
10
10
|
indexer = Supernova::SolrIndexer.new
|
11
11
|
indexer.db = db
|
12
12
|
Supernova::Solr.url = "http://solr.xx:9333/solr"
|
13
13
|
indexer.stub!(:system).and_return true
|
14
14
|
indexer
|
15
|
-
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:custom_indexer) { indexer_clazz.new }
|
18
|
+
|
19
|
+
before(:each) do
|
20
|
+
indexer_clazz.has(:title, :type => :text)
|
21
|
+
indexer_clazz.has(:artist_id, :type => :integer)
|
22
|
+
indexer_clazz.has(:description, :type => :text)
|
23
|
+
indexer_clazz.has(:created_at, :type => :date)
|
24
|
+
end
|
16
25
|
|
17
26
|
before(:each) do
|
18
27
|
File.stub!(:open).and_return file_stub
|
@@ -29,6 +38,82 @@ describe Supernova::SolrIndexer do
|
|
29
38
|
indexer = Supernova::SolrIndexer.new(:db => db)
|
30
39
|
indexer.db.should == db
|
31
40
|
end
|
41
|
+
|
42
|
+
it "can be initialized with ids" do
|
43
|
+
Supernova::SolrIndexer.new(:ids => [1, 2]).ids.should == [1, 2]
|
44
|
+
end
|
45
|
+
|
46
|
+
it "sets ids to all when nil" do
|
47
|
+
Supernova::SolrIndexer.new.ids.should == :all
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "index!" do
|
52
|
+
it "calls query_to_index" do
|
53
|
+
indexer.should_receive(:query_to_index).and_return "some query"
|
54
|
+
indexer.index!
|
55
|
+
end
|
56
|
+
|
57
|
+
it "calls index_query on query_to_index" do
|
58
|
+
query = "some query"
|
59
|
+
indexer.stub!(:query_to_index).and_return query
|
60
|
+
indexer.should_receive(:index_query).with(query)
|
61
|
+
indexer.index!
|
62
|
+
end
|
63
|
+
|
64
|
+
it "calls row_to_solr with all returned rows from sql" do
|
65
|
+
row1 = double("row1")
|
66
|
+
row2 = double("row2")
|
67
|
+
indexer.stub!(:query).and_return [row1, row2]
|
68
|
+
indexer.stub!(:query_to_index).and_return "some query"
|
69
|
+
indexer.should_receive(:row_to_solr).with(row1)
|
70
|
+
indexer.stub!(:index_query).and_yield(row1)
|
71
|
+
indexer.index!
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "validate_lat" do
|
76
|
+
{ nil => nil, 10 => 10.0, 90.1 => nil, 90 => 90, -90.1 => nil, -90 => -90 }.each do |from, to|
|
77
|
+
it "converts #{from} to #{to}" do
|
78
|
+
indexer.validate_lat(from).should == to
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "validate_lng" do
|
84
|
+
{ nil => nil, 10 => 10.0, 180.1 => nil, 180 => 180, -180.1 => nil, -180 => -180 }.each do |from, to|
|
85
|
+
it "converts #{from} to #{to}" do
|
86
|
+
indexer.validate_lng(from).should == to
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "#sql_column_from_field_and_type" do
|
92
|
+
{
|
93
|
+
[:title, :string] => "title AS title_s",
|
94
|
+
[:count, :int] => "count AS count_i",
|
95
|
+
[:test, :sint] => "test AS test_si",
|
96
|
+
[:lat, :float] => "lat AS lat_f",
|
97
|
+
[:text, :boolean] => "text AS text_b",
|
98
|
+
[:loc, :location] => "loc AS loc_p",
|
99
|
+
[:deleted_at, :date] => %(IF(deleted_at IS NULL, NULL, CONCAT(REPLACE(deleted_at, " ", "T"), "Z")) AS deleted_at_dt),
|
100
|
+
}.each do |(field, type), name|
|
101
|
+
it "maps #{field} with #{type} to #{name}" do
|
102
|
+
indexer.sql_column_from_field_and_type(field, type).should == name
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
it "raises an error when no mapping defined" do
|
107
|
+
lambda {
|
108
|
+
indexer.sql_column_from_field_and_type(:text, :rgne)
|
109
|
+
}.should raise_error
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "#row_to_solr" do
|
114
|
+
it "returns the db row by default" do
|
115
|
+
indexer.row_to_solr("id" => 1).should == { "id" => 1 }
|
116
|
+
end
|
32
117
|
end
|
33
118
|
|
34
119
|
describe "#query_db" do
|
@@ -164,4 +249,193 @@ describe Supernova::SolrIndexer do
|
|
164
249
|
indexer.do_index_file
|
165
250
|
end
|
166
251
|
end
|
252
|
+
|
253
|
+
describe "define mappings" do
|
254
|
+
let(:blank_indexer_clazz) { Class.new(Supernova::SolrIndexer) }
|
255
|
+
|
256
|
+
it "has an empty array of field_definitions by default" do
|
257
|
+
blank_indexer_clazz.field_definitions.should == {}
|
258
|
+
end
|
259
|
+
|
260
|
+
it "has adds filters to the field_definitions" do
|
261
|
+
blank_indexer_clazz.has(:artist_id, :type => :integer, :sortable => true)
|
262
|
+
blank_indexer_clazz.field_definitions.should == { :artist_id => { :type => :integer, :sortable => true } }
|
263
|
+
end
|
264
|
+
|
265
|
+
it "clazz sets indexed class" do
|
266
|
+
blank_indexer_clazz.clazz(Integer)
|
267
|
+
blank_indexer_clazz.instance_variable_get("@clazz").should == Integer
|
268
|
+
end
|
269
|
+
|
270
|
+
it "does not change but return the clazz when nil" do
|
271
|
+
blank_indexer_clazz.clazz(Integer)
|
272
|
+
blank_indexer_clazz.clazz.should == Integer
|
273
|
+
end
|
274
|
+
|
275
|
+
it "allows setting the clazz to nil" do
|
276
|
+
blank_indexer_clazz.clazz(Integer)
|
277
|
+
blank_indexer_clazz.clazz(nil)
|
278
|
+
blank_indexer_clazz.clazz.should be_nil
|
279
|
+
end
|
280
|
+
|
281
|
+
it "table_name sets the table name" do
|
282
|
+
blank_indexer_clazz.table_name(:people)
|
283
|
+
blank_indexer_clazz.instance_variable_get("@table_name").should == :people
|
284
|
+
end
|
285
|
+
|
286
|
+
it "table_name does not overwrite but return table_name when nil given" do
|
287
|
+
blank_indexer_clazz.table_name(:people)
|
288
|
+
blank_indexer_clazz.table_name.should == :people
|
289
|
+
end
|
290
|
+
|
291
|
+
it "allows setting the table_name to nil" do
|
292
|
+
blank_indexer_clazz.table_name(:people)
|
293
|
+
blank_indexer_clazz.table_name(nil).should be_nil
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
describe "#default_mappings" do
|
298
|
+
it "returns id when no class defined" do
|
299
|
+
indexer_clazz.new.default_fields.should == ["id"]
|
300
|
+
end
|
301
|
+
|
302
|
+
it "adds type when class defined" do
|
303
|
+
indexer_clazz.clazz Integer
|
304
|
+
indexer_clazz.new.default_fields.should == ["id", %("Integer" AS type_s)]
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
describe "#defined_fields" do
|
309
|
+
let(:field_definitions) { { :title => { :type => :string } } }
|
310
|
+
|
311
|
+
it "calls field_definitions" do
|
312
|
+
indexer_clazz.should_receive(:field_definitions).and_return field_definitions
|
313
|
+
custom_indexer.defined_fields
|
314
|
+
end
|
315
|
+
|
316
|
+
["title AS title_t", "artist_id AS artist_id_i", "description AS description_t",
|
317
|
+
%(IF(created_at IS NULL, NULL, CONCAT(REPLACE(created_at, " ", "T"), "Z")) AS created_at_dt)
|
318
|
+
].each do |field|
|
319
|
+
it "includes field #{field.inspect}" do
|
320
|
+
custom_indexer.defined_fields.should include(field)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
it "does not include virtual fields" do
|
325
|
+
clazz = Class.new(Supernova::SolrIndexer)
|
326
|
+
clazz.has :location, :type => :location, :virtual => true
|
327
|
+
clazz.has :title, :type => :string
|
328
|
+
clazz.new.defined_fields.should == ["title AS title_s"]
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
describe "#table_name" do
|
333
|
+
it "returns nil when no table_name defined on indexer class and no class defined" do
|
334
|
+
Class.new(Supernova::SolrIndexer).new.table_name.should be_nil
|
335
|
+
end
|
336
|
+
|
337
|
+
it "returns nil when no table_name defined on indexer class and class does not respond to table name" do
|
338
|
+
clazz = Class.new(Supernova::SolrIndexer)
|
339
|
+
clazz.clazz(Integer)
|
340
|
+
clazz.new.table_name.should be_nil
|
341
|
+
end
|
342
|
+
|
343
|
+
it "returns the table name defined in indexer class" do
|
344
|
+
clazz = Class.new(Supernova::SolrIndexer)
|
345
|
+
clazz.table_name(:some_table)
|
346
|
+
clazz.new.table_name.should == :some_table
|
347
|
+
end
|
348
|
+
|
349
|
+
it "returns the table name ob class when responding to table_name" do
|
350
|
+
model_clazz = double("clazz", :table_name => "model_table")
|
351
|
+
clazz = Class.new(Supernova::SolrIndexer)
|
352
|
+
clazz.clazz(model_clazz)
|
353
|
+
clazz.new.table_name.should == "model_table"
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
describe "#query_to_index" do
|
358
|
+
before(:each) do
|
359
|
+
@indexer_clazz = Class.new(Supernova::SolrIndexer)
|
360
|
+
@indexer_clazz.clazz Integer
|
361
|
+
@indexer_clazz.table_name "integers"
|
362
|
+
@indexer = @indexer_clazz.new
|
363
|
+
end
|
364
|
+
|
365
|
+
it "raises an error when table_name returns nil" do
|
366
|
+
@indexer_clazz.clazz(nil)
|
367
|
+
@indexer_clazz.table_name(nil)
|
368
|
+
@indexer.should_receive(:table_name).and_return nil
|
369
|
+
lambda {
|
370
|
+
@indexer.query_to_index
|
371
|
+
}.should raise_error("no table_name defined")
|
372
|
+
end
|
373
|
+
|
374
|
+
it "returns a string" do
|
375
|
+
@indexer.query_to_index.should be_an_instance_of(String)
|
376
|
+
end
|
377
|
+
|
378
|
+
it "does not include a where when ids is nil" do
|
379
|
+
@indexer.query_to_index.should_not include("WHERE")
|
380
|
+
end
|
381
|
+
|
382
|
+
it "does include a where when ids are present" do
|
383
|
+
@indexer_clazz.new(:ids => %w(1 2)).query_to_index.should include("WHERE id IN (1, 2)")
|
384
|
+
end
|
385
|
+
|
386
|
+
it "calls and includes select_fields" do
|
387
|
+
@indexer.should_receive(:select_fields).and_return %w(a c)
|
388
|
+
@indexer.query_to_index.should include("SELECT a, c FROM integers")
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
describe "#select_fields" do
|
393
|
+
it "joins default_fields with defined_fields" do
|
394
|
+
default = double("default fields")
|
395
|
+
defined = double("defined fields")
|
396
|
+
indexer.should_receive(:default_fields).and_return [default]
|
397
|
+
indexer.should_receive(:defined_fields).and_return [defined]
|
398
|
+
indexer.select_fields.should == [default, defined]
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
describe "#method_missing" do
|
403
|
+
it "returns a new supernova criteria" do
|
404
|
+
indexer_clazz.where(:a => 1).should be_an_instance_of(Supernova::SolrCriteria)
|
405
|
+
end
|
406
|
+
|
407
|
+
it "sets the correct clazz" do
|
408
|
+
indexer_clazz = Class.new(Supernova::SolrIndexer)
|
409
|
+
indexer_clazz.clazz(String)
|
410
|
+
indexer_clazz.where(:a => 1).clazz.should == String
|
411
|
+
end
|
412
|
+
|
413
|
+
it "adds the attribute_mapping" do
|
414
|
+
indexer_clazz.where(:a => 1).search_options[:attribute_mapping].should == {
|
415
|
+
:artist_id=>{:type=>:integer}, :title=>{:type=>:text}, :created_at=>{:type=>:date}, :description=>{:type=>:text}
|
416
|
+
}
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
describe "#solr_field_for_field_name_and_mapping" do
|
421
|
+
let(:mapping) do
|
422
|
+
{
|
423
|
+
:artist_name => { :type => :string },
|
424
|
+
:artist_id => { :type => :integer },
|
425
|
+
}
|
426
|
+
end
|
427
|
+
|
428
|
+
{
|
429
|
+
:artist_name => "artist_name_s", "artist_name" => "artist_name_s",
|
430
|
+
:artist_id => "artist_id_i", :popularity => "popularity"
|
431
|
+
}.each do |from, to|
|
432
|
+
it "maps #{from} to #{to}" do
|
433
|
+
Supernova::SolrIndexer.solr_field_for_field_name_and_mapping(from, mapping).should == to
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
it "returns the original field when mapping is nil" do
|
438
|
+
Supernova::SolrIndexer.solr_field_for_field_name_and_mapping(:artist, nil).should == "artist"
|
439
|
+
end
|
440
|
+
end
|
167
441
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Supernova::SymbolExtensions" do
|
4
|
+
[:not, :gt, :lt, :gte, :lte, :ne].each do |type|
|
5
|
+
it "returns the correct condition for #{type}" do
|
6
|
+
cond = :user_id.send(type)
|
7
|
+
cond.key.should == :user_id
|
8
|
+
cond.type.should == type
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it "sets the correct key" do
|
13
|
+
:other_id.not.key.should == :other_id
|
14
|
+
end
|
15
|
+
end
|
data/supernova.gemspec
CHANGED
@@ -5,16 +5,17 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{supernova}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.3.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Tobias Schwab"]
|
12
|
-
s.date = %q{2011-06-
|
12
|
+
s.date = %q{2011-06-12}
|
13
13
|
s.description = %q{Unified search scopes}
|
14
14
|
s.email = %q{tobias.schwab@dynport.de}
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE.txt",
|
17
|
-
"README.rdoc"
|
17
|
+
"README.rdoc",
|
18
|
+
"TODO"
|
18
19
|
]
|
19
20
|
s.files = [
|
20
21
|
".autotest",
|
@@ -29,11 +30,13 @@ Gem::Specification.new do |s|
|
|
29
30
|
"autotest/discover.rb",
|
30
31
|
"lib/supernova.rb",
|
31
32
|
"lib/supernova/collection.rb",
|
33
|
+
"lib/supernova/condition.rb",
|
32
34
|
"lib/supernova/criteria.rb",
|
33
35
|
"lib/supernova/numeric_extensions.rb",
|
34
36
|
"lib/supernova/solr.rb",
|
35
37
|
"lib/supernova/solr_criteria.rb",
|
36
38
|
"lib/supernova/solr_indexer.rb",
|
39
|
+
"lib/supernova/symbol_extensions.rb",
|
37
40
|
"lib/supernova/thinking_sphinx.rb",
|
38
41
|
"lib/supernova/thinking_sphinx_criteria.rb",
|
39
42
|
"solr/conf/admin-extra.html",
|
@@ -77,11 +80,13 @@ Gem::Specification.new do |s|
|
|
77
80
|
"spec/integration/solr_spec.rb",
|
78
81
|
"spec/integration/thinking_sphinx_spec.rb",
|
79
82
|
"spec/spec_helper.rb",
|
83
|
+
"spec/supernova/condition_spec.rb",
|
80
84
|
"spec/supernova/criteria_spec.rb",
|
81
85
|
"spec/supernova/numeric_extensions_spec.rb",
|
82
86
|
"spec/supernova/solr_criteria_spec.rb",
|
83
87
|
"spec/supernova/solr_indexer_spec.rb",
|
84
88
|
"spec/supernova/solr_spec.rb",
|
89
|
+
"spec/supernova/symbol_extensions_spec.rb",
|
85
90
|
"spec/supernova/thinking_sphinx_criteria_spec.rb",
|
86
91
|
"spec/supernova_spec.rb",
|
87
92
|
"supernova.gemspec"
|
metadata
CHANGED
@@ -5,9 +5,9 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 3
|
9
|
+
- 0
|
10
|
+
version: 0.3.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Tobias Schwab
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-06-
|
18
|
+
date: 2011-06-12 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -219,6 +219,7 @@ extensions: []
|
|
219
219
|
extra_rdoc_files:
|
220
220
|
- LICENSE.txt
|
221
221
|
- README.rdoc
|
222
|
+
- TODO
|
222
223
|
files:
|
223
224
|
- .autotest
|
224
225
|
- .document
|
@@ -232,11 +233,13 @@ files:
|
|
232
233
|
- autotest/discover.rb
|
233
234
|
- lib/supernova.rb
|
234
235
|
- lib/supernova/collection.rb
|
236
|
+
- lib/supernova/condition.rb
|
235
237
|
- lib/supernova/criteria.rb
|
236
238
|
- lib/supernova/numeric_extensions.rb
|
237
239
|
- lib/supernova/solr.rb
|
238
240
|
- lib/supernova/solr_criteria.rb
|
239
241
|
- lib/supernova/solr_indexer.rb
|
242
|
+
- lib/supernova/symbol_extensions.rb
|
240
243
|
- lib/supernova/thinking_sphinx.rb
|
241
244
|
- lib/supernova/thinking_sphinx_criteria.rb
|
242
245
|
- solr/conf/admin-extra.html
|
@@ -280,14 +283,17 @@ files:
|
|
280
283
|
- spec/integration/solr_spec.rb
|
281
284
|
- spec/integration/thinking_sphinx_spec.rb
|
282
285
|
- spec/spec_helper.rb
|
286
|
+
- spec/supernova/condition_spec.rb
|
283
287
|
- spec/supernova/criteria_spec.rb
|
284
288
|
- spec/supernova/numeric_extensions_spec.rb
|
285
289
|
- spec/supernova/solr_criteria_spec.rb
|
286
290
|
- spec/supernova/solr_indexer_spec.rb
|
287
291
|
- spec/supernova/solr_spec.rb
|
292
|
+
- spec/supernova/symbol_extensions_spec.rb
|
288
293
|
- spec/supernova/thinking_sphinx_criteria_spec.rb
|
289
294
|
- spec/supernova_spec.rb
|
290
295
|
- supernova.gemspec
|
296
|
+
- TODO
|
291
297
|
has_rdoc: true
|
292
298
|
homepage: http://github.com/dynport/supernova
|
293
299
|
licenses:
|