supernova 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|