xapit 0.2.7 → 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/{CHANGELOG → CHANGELOG.rdoc} +7 -2
- data/Gemfile +19 -0
- data/LICENSE +4 -4
- data/README.rdoc +61 -108
- data/Rakefile +11 -10
- data/features/facets.feature +93 -82
- data/features/finding.feature +196 -138
- data/features/indexing.feature +35 -37
- data/features/remote_server.feature +10 -0
- data/features/step_definitions/xapit_steps.rb +53 -25
- data/features/suggestions.feature +20 -14
- data/features/support/env.rb +13 -6
- data/features/support/xapit_helpers.rb +8 -9
- data/lib/generators/xapit/install_generator.rb +14 -0
- data/lib/generators/xapit/templates/xapit.ru +6 -0
- data/lib/generators/xapit/templates/xapit.yml +11 -0
- data/lib/xapit.rb +106 -64
- data/lib/xapit/client/collection.rb +150 -0
- data/lib/xapit/client/facet.rb +11 -0
- data/lib/xapit/client/facet_option.rb +29 -0
- data/lib/xapit/client/index_builder.rb +67 -0
- data/lib/xapit/client/membership.rb +46 -0
- data/lib/xapit/client/model_adapters/abstract_model_adapter.rb +30 -0
- data/lib/xapit/client/model_adapters/active_record_adapter.rb +27 -0
- data/lib/xapit/client/model_adapters/default_model_adapter.rb +7 -0
- data/lib/xapit/client/railtie.rb +18 -0
- data/lib/xapit/client/remote_database.rb +21 -0
- data/lib/xapit/client/tasks.rb +18 -0
- data/lib/xapit/server/app.rb +27 -0
- data/lib/xapit/server/database.rb +47 -0
- data/lib/xapit/server/indexer.rb +138 -0
- data/lib/xapit/server/query.rb +240 -0
- data/spec/fixtures/blankdb/flintlock +0 -0
- data/spec/fixtures/blankdb/iamchert +1 -0
- data/spec/fixtures/blankdb/postlist.DB +0 -0
- data/spec/fixtures/blankdb/postlist.baseA +0 -0
- data/spec/fixtures/blankdb/record.DB +0 -0
- data/spec/fixtures/blankdb/record.baseA +0 -0
- data/spec/fixtures/blankdb/termlist.DB +0 -0
- data/spec/fixtures/blankdb/termlist.baseA +0 -0
- data/spec/fixtures/xapit.ru +13 -0
- data/spec/fixtures/xapit.yml +4 -0
- data/spec/spec_helper.rb +8 -9
- data/spec/support/spec_macros.rb +6 -0
- data/spec/{xapit_member.rb → support/xapit_member.rb} +14 -16
- data/spec/xapit/client/collection_spec.rb +63 -0
- data/spec/xapit/client/facet_option_spec.rb +26 -0
- data/spec/xapit/client/facet_spec.rb +13 -0
- data/spec/xapit/client/index_builder_spec.rb +66 -0
- data/spec/xapit/client/membership_spec.rb +43 -0
- data/spec/xapit/client/model_adapters/active_record_adapter_spec.rb +62 -0
- data/spec/xapit/client/model_adapters/default_model_adapter_spec.rb +7 -0
- data/spec/xapit/client/remote_database_spec.rb +19 -0
- data/spec/xapit/server/app_spec.rb +22 -0
- data/spec/xapit/server/database_spec.rb +37 -0
- data/spec/xapit/server/indexer_spec.rb +82 -0
- data/spec/xapit/server/query_spec.rb +43 -0
- data/spec/xapit/xapit_spec.rb +28 -0
- metadata +124 -93
- data/Manifest +0 -60
- data/features/sorting.feature +0 -29
- data/init.rb +0 -1
- data/install.rb +0 -8
- data/lib/xapit/adapters/abstract_adapter.rb +0 -47
- data/lib/xapit/adapters/active_record_adapter.rb +0 -20
- data/lib/xapit/adapters/data_mapper_adapter.rb +0 -10
- data/lib/xapit/collection.rb +0 -187
- data/lib/xapit/config.rb +0 -84
- data/lib/xapit/facet.rb +0 -67
- data/lib/xapit/facet_blueprint.rb +0 -59
- data/lib/xapit/facet_option.rb +0 -56
- data/lib/xapit/index_blueprint.rb +0 -147
- data/lib/xapit/indexers/abstract_indexer.rb +0 -116
- data/lib/xapit/indexers/classic_indexer.rb +0 -29
- data/lib/xapit/indexers/simple_indexer.rb +0 -38
- data/lib/xapit/membership.rb +0 -137
- data/lib/xapit/query.rb +0 -89
- data/lib/xapit/query_parsers/abstract_query_parser.rb +0 -174
- data/lib/xapit/query_parsers/classic_query_parser.rb +0 -29
- data/lib/xapit/query_parsers/simple_query_parser.rb +0 -75
- data/lib/xapit/rake_tasks.rb +0 -13
- data/rails_generators/xapit/USAGE +0 -13
- data/rails_generators/xapit/templates/setup_xapit.rb +0 -1
- data/rails_generators/xapit/templates/xapit.rake +0 -4
- data/rails_generators/xapit/xapit_generator.rb +0 -20
- data/spec/xapit/adapters/active_record_adapter_spec.rb +0 -31
- data/spec/xapit/adapters/data_mapper_adapter_spec.rb +0 -10
- data/spec/xapit/collection_spec.rb +0 -176
- data/spec/xapit/config_spec.rb +0 -62
- data/spec/xapit/facet_blueprint_spec.rb +0 -29
- data/spec/xapit/facet_option_spec.rb +0 -80
- data/spec/xapit/facet_spec.rb +0 -73
- data/spec/xapit/index_blueprint_spec.rb +0 -112
- data/spec/xapit/indexers/abstract_indexer_spec.rb +0 -111
- data/spec/xapit/indexers/classic_indexer_spec.rb +0 -35
- data/spec/xapit/indexers/simple_indexer_spec.rb +0 -69
- data/spec/xapit/membership_spec.rb +0 -55
- data/spec/xapit/query_parsers/abstract_query_parser_spec.rb +0 -60
- data/spec/xapit/query_parsers/classic_query_parser_spec.rb +0 -20
- data/spec/xapit/query_parsers/simple_query_parser_spec.rb +0 -86
- data/spec/xapit/query_spec.rb +0 -60
- data/tasks/spec.rb +0 -9
- data/tasks/xapit.rake +0 -1
- data/uninstall.rb +0 -5
- data/xapit.gemspec +0 -30
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
module Xapit
|
|
2
|
+
module Server
|
|
3
|
+
class Query
|
|
4
|
+
def initialize(clauses)
|
|
5
|
+
@clauses = clauses
|
|
6
|
+
@xapian_query = nil
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def matches
|
|
10
|
+
enquire = Xapian::Enquire.new(Xapit.database.xapian_database)
|
|
11
|
+
enquire.query = xapian_query
|
|
12
|
+
enquire.set_sort_by_key_then_relevance(sorter, false) if sorter
|
|
13
|
+
enquire.mset((page.to_i-1)*per_page.to_i, per_page.to_i).matches
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def records
|
|
17
|
+
matches.map do |match|
|
|
18
|
+
class_name, id = match.document.data.split('-')
|
|
19
|
+
{:class => class_name, :id => id, :relevance => match.percent}
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def spelling_suggestion
|
|
24
|
+
text = @clauses.map { |clause| clause[:search] }.compact.join(" ")
|
|
25
|
+
if [text, *text.scan(/\w+/)].all? { |term| term_suggestion(term).nil? }
|
|
26
|
+
nil
|
|
27
|
+
else
|
|
28
|
+
return term_suggestion(text) unless term_suggestion(text).to_s.empty?
|
|
29
|
+
text.downcase.gsub(/\w+/) do |term|
|
|
30
|
+
term_suggestion(term) || term
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def facets
|
|
36
|
+
facets = {}
|
|
37
|
+
enquire = Xapian::Enquire.new(Xapit.database.xapian_database)
|
|
38
|
+
enquire.query = xapian_query
|
|
39
|
+
spies = facet_spies
|
|
40
|
+
spies.values.each do |spy|
|
|
41
|
+
enquire.add_matchspy(spy)
|
|
42
|
+
end
|
|
43
|
+
enquire.mset(0, 200)
|
|
44
|
+
spies.each do |attribute, spy|
|
|
45
|
+
values = {}
|
|
46
|
+
spy.values.map do |spy_value|
|
|
47
|
+
spy_value.term.split("\3").each do |term| # used to support multiple facet values
|
|
48
|
+
values[term] ||= 0
|
|
49
|
+
values[term] += spy_value.termfreq.to_i
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
facets[attribute] = values.map { |value, count| {:value => value, :count => count} }
|
|
53
|
+
end
|
|
54
|
+
facets
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def applied_facet_options
|
|
58
|
+
facet_options = []
|
|
59
|
+
@clauses.each do |clause|
|
|
60
|
+
if clause[:with_facets]
|
|
61
|
+
clause[:with_facets].each do |identifier|
|
|
62
|
+
facet_options << facet_option(identifier)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
facet_options
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def facet_option(identifier)
|
|
70
|
+
match = self.class.new([{:in_classes => ["FacetOption"]}, {:where => {:id => identifier}}]).matches.first
|
|
71
|
+
if match.nil?
|
|
72
|
+
raise "Unable to find facet option for #{identifier}."
|
|
73
|
+
else
|
|
74
|
+
name, value = match.document.data.split('|||')
|
|
75
|
+
{:id => identifier, :name => name, :value => value}
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def total
|
|
80
|
+
enquire = Xapian::Enquire.new(Xapit.database.xapian_database)
|
|
81
|
+
enquire.query = xapian_query
|
|
82
|
+
enquire.mset(0, Xapit.database.xapian_database.doccount).matches_estimated
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def data
|
|
86
|
+
{:records => records, :facets => facets, :applied_facet_options => applied_facet_options, :total => total}
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
def page
|
|
92
|
+
@clauses.map { |clause| clause[:page] }.compact.last || 1
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def per_page
|
|
96
|
+
@clauses.map { |clause| clause[:per_page] }.compact.last || Xapit::Client::Collection::DEFAULT_PER_PAGE
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def term_suggestion(term)
|
|
100
|
+
suggestion = Xapit.database.xapian_database.get_spelling_suggestion(term.downcase)
|
|
101
|
+
suggestion.to_s.empty? ? nil : suggestion
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def sorter
|
|
105
|
+
if @clauses.any? { |c| c[:order] }
|
|
106
|
+
sorter = Xapian::MultiValueKeyMaker.new
|
|
107
|
+
@clauses.each do |clause|
|
|
108
|
+
if clause[:order]
|
|
109
|
+
attribute, direction = clause[:order]
|
|
110
|
+
sorter.add_value(Xapit.value_index(:sortable, attribute), direction.to_sym == :desc)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
sorter
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def facet_spies
|
|
118
|
+
spies = {}
|
|
119
|
+
@clauses.each do |clause|
|
|
120
|
+
if clause[:include_facets]
|
|
121
|
+
clause[:include_facets].each do |facet|
|
|
122
|
+
spies[facet] = Xapian::ValueCountMatchSpy.new(Xapit.value_index(:facet, facet))
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
spies
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def xapian_query
|
|
130
|
+
build_xapian_query if @query.nil?
|
|
131
|
+
@xapian_query
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def build_xapian_query
|
|
135
|
+
@xapian_query = Xapian::Query.new("")
|
|
136
|
+
@clauses.each do |clause|
|
|
137
|
+
clause.each do |type, options|
|
|
138
|
+
apply_clause(type, options)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def apply_clause(type, value)
|
|
144
|
+
case type
|
|
145
|
+
when :search
|
|
146
|
+
merge(:and, search_query(value))
|
|
147
|
+
when :where
|
|
148
|
+
merge(:and, where_query(value))
|
|
149
|
+
when :or_where
|
|
150
|
+
merge(:or, where_query(value))
|
|
151
|
+
when :not_where
|
|
152
|
+
merge(:not, where_query(value))
|
|
153
|
+
when :in_classes
|
|
154
|
+
merge(:and, value.map { |c| "C#{c}" })
|
|
155
|
+
when :not_in_classes
|
|
156
|
+
merge(:not, value.map { |c| "C#{c}" })
|
|
157
|
+
when :similar_to
|
|
158
|
+
similar_to(value)
|
|
159
|
+
when :with_facets
|
|
160
|
+
merge(:and, facet_terms(value))
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def similar_to(data)
|
|
165
|
+
indexer = Indexer.new(data)
|
|
166
|
+
terms = (indexer.text_terms + indexer.field_terms).map { |a| a.first }
|
|
167
|
+
merge(:and, Xapian::Query.new(xapian_operator(:or), terms))
|
|
168
|
+
merge(:not, ["Q#{data[:class]}-#{data[:id]}"])
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def where_query(conditions)
|
|
172
|
+
queries = []
|
|
173
|
+
terms = []
|
|
174
|
+
conditions.each do |name, value|
|
|
175
|
+
if value.kind_of?(Hash) && value[:from] && value[:to]
|
|
176
|
+
queries << Xapian::Query.new(xapian_operator(:range), Xapit.value_index(:field, name), Xapit.serialize_value(value[:from]), Xapit.serialize_value(value[:to]))
|
|
177
|
+
else
|
|
178
|
+
terms << "X#{name}-#{value.to_s.downcase}"
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
queries << Xapian::Query.new(xapian_operator(:and), terms) unless terms.empty?
|
|
182
|
+
queries.inject(queries.shift) do |merged_query, query|
|
|
183
|
+
Xapian::Query.new(xapian_operator(:and), merged_query, query)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def where_terms(conditions)
|
|
188
|
+
conditions.map do |name, value|
|
|
189
|
+
"X#{name}-#{value.to_s.downcase}"
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def facet_terms(facets)
|
|
194
|
+
facets.map { |facet| "F#{facet}" }
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def search_query(text)
|
|
198
|
+
clean_text = text.gsub(/\b([a-z])\*/i, "\\1").gsub(/[^\w\*\s:]/u, "")
|
|
199
|
+
xapian_parser.parse_query(clean_text, Xapian::QueryParser::FLAG_WILDCARD | Xapian::QueryParser::FLAG_BOOLEAN) # Xapian::QueryParser::FLAG_LOVEHATE
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def merge(operator, query)
|
|
203
|
+
query = Xapian::Query.new(xapian_operator(:and), query) unless query.kind_of? Xapian::Query
|
|
204
|
+
@xapian_query = Xapian::Query.new(xapian_operator(operator), @xapian_query, query)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def xapian_operator(operator)
|
|
208
|
+
case operator
|
|
209
|
+
when :and then Xapian::Query::OP_AND
|
|
210
|
+
when :or then Xapian::Query::OP_OR
|
|
211
|
+
when :not then Xapian::Query::OP_AND_NOT
|
|
212
|
+
when :range then Xapian::Query::OP_VALUE_RANGE
|
|
213
|
+
else raise "Unknown Xapian operator #{operator}"
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def xapian_parser
|
|
218
|
+
@xapian_parser ||= build_xapian_parser
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def build_xapian_parser
|
|
222
|
+
parser = Xapian::QueryParser.new
|
|
223
|
+
parser.database = Xapit.database.xapian_database
|
|
224
|
+
if Xapit.config[:stemming]
|
|
225
|
+
parser.stemmer = Xapian::Stem.new(Xapit.config[:stemming])
|
|
226
|
+
parser.stemming_strategy = Xapian::QueryParser::STEM_SOME
|
|
227
|
+
end
|
|
228
|
+
parser.default_op = xapian_operator(:and)
|
|
229
|
+
@clauses.each do |clause|
|
|
230
|
+
if clause[:search]
|
|
231
|
+
clause[:search].scan(/([a-z0-9_]+)\:/i).each do
|
|
232
|
+
parser.add_prefix($1, "X#{$1}-")
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
parser
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
IAmChert�����pn�N3��gf�]
|
|
File without changes
|
|
Binary file
|
|
File without changes
|
|
Binary file
|
|
File without changes
|
|
Binary file
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require "rubygems"
|
|
2
|
+
|
|
3
|
+
# Add lib directory so we can include xapit, this isn't necessary when a gem is available
|
|
4
|
+
root = File.expand_path('../../..', __FILE__)
|
|
5
|
+
$:.unshift "#{root}/lib"
|
|
6
|
+
require "xapit"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
FileUtils.rm_rf("#{root}/tmp/testdb") # quick hack to start with a fresh database every time
|
|
10
|
+
Xapit.config[:database_path] = "#{root}/tmp/testdb"
|
|
11
|
+
Xapit.config[:template_path] = "#{root}/spec/fixtures/blankdb"
|
|
12
|
+
|
|
13
|
+
run Xapit::Server::App.new
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
require 'rubygems'
|
|
2
|
-
require '
|
|
3
|
-
require
|
|
4
|
-
require 'fileutils'
|
|
5
|
-
require File.dirname(__FILE__) + '/../lib/xapit'
|
|
6
|
-
require File.dirname(__FILE__) + '/xapit_member'
|
|
2
|
+
require 'bundler/setup'
|
|
3
|
+
Bundler.require(:default)
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
require "support/spec_macros"
|
|
6
|
+
require "support/xapit_member"
|
|
7
|
+
|
|
8
|
+
RSpec.configure do |config|
|
|
9
|
+
config.include SpecMacros
|
|
10
10
|
config.before(:each) do
|
|
11
|
-
Xapit.
|
|
12
|
-
Xapit.remove_database
|
|
11
|
+
Xapit.reset_config
|
|
13
12
|
XapitMember.delete_all
|
|
14
13
|
end
|
|
15
14
|
end
|
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
class XapitMember
|
|
2
|
-
include Xapit::Membership
|
|
3
|
-
|
|
2
|
+
include Xapit::Client::Membership
|
|
3
|
+
xapit { } # loads Xapit member methods
|
|
4
|
+
|
|
4
5
|
attr_reader :id
|
|
5
|
-
|
|
6
|
-
# Make it look like this inherits from ActiveRecord::Base
|
|
7
|
-
# so it will use the ActiveRecord adapter.
|
|
8
|
-
def self.ancestors
|
|
9
|
-
["ActiveRecord::Base"]
|
|
10
|
-
end
|
|
11
|
-
|
|
6
|
+
|
|
12
7
|
def self.find_each(&block)
|
|
13
8
|
@@records.each(&block) if @@records
|
|
14
9
|
end
|
|
15
|
-
|
|
10
|
+
|
|
16
11
|
def self.delete_all
|
|
17
12
|
@@records = []
|
|
18
13
|
end
|
|
19
|
-
|
|
14
|
+
|
|
20
15
|
def self.find(ids)
|
|
21
16
|
if ids.kind_of? Array
|
|
22
17
|
# change the order to mimic database where we can't predict the order
|
|
@@ -25,18 +20,21 @@ class XapitMember
|
|
|
25
20
|
@@records.detect { |r| r.id == ids.to_i }
|
|
26
21
|
end
|
|
27
22
|
end
|
|
28
|
-
|
|
23
|
+
|
|
29
24
|
def self.find_by_id(id)
|
|
30
25
|
find(id)
|
|
31
26
|
end
|
|
32
|
-
|
|
27
|
+
|
|
33
28
|
def initialize(attributes = {})
|
|
34
29
|
@@records ||= []
|
|
35
30
|
@id = @@records.size + 1
|
|
36
|
-
@attributes =
|
|
31
|
+
@attributes = {}
|
|
32
|
+
attributes.each do |key, value|
|
|
33
|
+
@attributes[key.to_sym] = value
|
|
34
|
+
end
|
|
37
35
|
@@records << self
|
|
38
36
|
end
|
|
39
|
-
|
|
37
|
+
|
|
40
38
|
def method_missing(name, *args)
|
|
41
39
|
if @attributes.has_key? name
|
|
42
40
|
@attributes[name]
|
|
@@ -44,7 +42,7 @@ class XapitMember
|
|
|
44
42
|
super
|
|
45
43
|
end
|
|
46
44
|
end
|
|
47
|
-
|
|
45
|
+
|
|
48
46
|
def update_attribute(name, value)
|
|
49
47
|
@attributes[name] = value
|
|
50
48
|
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe Xapit::Client::Collection do
|
|
4
|
+
it "builds up clauses with in_classes, search, where, order calls" do
|
|
5
|
+
collection1 = Xapit::Client::Collection.new([:initial])
|
|
6
|
+
collection2 = collection1.in_classes(String).search("hello").where(:foo => "bar").order(:bar)
|
|
7
|
+
collection1.clauses.should eq([:initial])
|
|
8
|
+
collection2.clauses.should eq([:initial, {:in_classes => [String]}, {:search => "hello"}, {:where => {:foo => "bar"}}, {:order => [:bar, :asc]}])
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "returns same collection when searching nil or empty string" do
|
|
12
|
+
collection1 = Xapit::Client::Collection.new
|
|
13
|
+
collection1.search("").should eq(collection1)
|
|
14
|
+
collection1.search(nil).should eq(collection1)
|
|
15
|
+
collection1.search.should eq(collection1)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "returns indexed records and delegates array methods to it" do
|
|
19
|
+
load_xapit_database
|
|
20
|
+
member = XapitMember.new
|
|
21
|
+
member.class.xapit_index_builder.add_document(member)
|
|
22
|
+
collection = Xapit::Client::Collection.new
|
|
23
|
+
collection.records.should eq([member])
|
|
24
|
+
collection.should respond_to(:flatten)
|
|
25
|
+
collection.flatten.should eq([member])
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "splits up matching facets into an array" do
|
|
29
|
+
collection = Xapit::Client::Collection.new.with_facets("foo-bar")
|
|
30
|
+
collection.clauses.should eq([{:with_facets => %w[foo bar]}])
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "splits range into from/to hash" do
|
|
34
|
+
collection = Xapit::Client::Collection.new.where(:priority => 3..5)
|
|
35
|
+
collection.clauses.should eq([{:where => {:priority => {:from => 3, :to => 5}}}])
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "does not raise an exception when passing nil to with_facets" do
|
|
39
|
+
lambda {
|
|
40
|
+
Xapit::Client::Collection.new.with_facets(nil).should be_kind_of(Xapit::Client::Collection)
|
|
41
|
+
}.should_not raise_exception
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "defaults to 20 per page and page 1" do
|
|
45
|
+
Xapit::Client::Collection.new.limit_value.should eq(20)
|
|
46
|
+
Xapit::Client::Collection.new.current_page.should eq(1)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "supports kaminari pagination" do
|
|
50
|
+
collection = Xapit::Client::Collection.new.page("2").per("10")
|
|
51
|
+
collection.stub(:total_entries) { 29 }
|
|
52
|
+
collection.current_page.should eq(2)
|
|
53
|
+
collection.num_pages.should eq(3)
|
|
54
|
+
collection.limit_value.should eq(10)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "supports will_paginate pagination" do
|
|
58
|
+
collection = Xapit::Client::Collection.new.page("2").per("10")
|
|
59
|
+
collection.stub(:total_entries) { 29 }
|
|
60
|
+
collection.current_page.should eq(2)
|
|
61
|
+
collection.total_pages.should eq(3)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe Xapit::Client::FacetOption do
|
|
4
|
+
it "has an identifier using attribute and value" do
|
|
5
|
+
option = Xapit::Client::FacetOption.new("greeting", :value => "Hello")
|
|
6
|
+
option.identifier.should eq(Xapit.facet_identifier("greeting", "Hello"))
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it "has a name and count matching passed options" do
|
|
10
|
+
option = Xapit::Client::FacetOption.new("greeting", :value => "Hello", :count => "3")
|
|
11
|
+
option.name.should eq("Hello")
|
|
12
|
+
option.count.should eq(3)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "combines previous identifiers with current one on to_param" do
|
|
16
|
+
id = Xapit.facet_identifier("greeting", "Hello")
|
|
17
|
+
option = Xapit::Client::FacetOption.new("greeting", {:value => "Hello"}, %w[abc 123])
|
|
18
|
+
option.to_param.should == "abc-123-#{id}"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "removes current identifier from previous identifiers if it exists" do
|
|
22
|
+
id = Xapit.facet_identifier("greeting", "Hello")
|
|
23
|
+
option = Xapit::Client::FacetOption.new("greeting", {:value => "Hello"}, %w[abc 123] + [id])
|
|
24
|
+
option.to_param.should == "abc-123"
|
|
25
|
+
end
|
|
26
|
+
end
|