xapit 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (180) hide show
  1. data/LICENSE +20 -0
  2. data/Manifest +178 -0
  3. data/README.rdoc +183 -0
  4. data/Rakefile +15 -0
  5. data/TODO +23 -0
  6. data/features/facets.feature +51 -0
  7. data/features/finding.feature +119 -0
  8. data/features/indexing.feature +41 -0
  9. data/features/step_definitions/common_steps.rb +7 -0
  10. data/features/step_definitions/xapit_steps.rb +117 -0
  11. data/features/support/env.rb +7 -0
  12. data/features/support/xapit_helpers.rb +27 -0
  13. data/init.rb +3 -0
  14. data/install.rb +9 -0
  15. data/lib/xapit.rb +39 -0
  16. data/lib/xapit/collection.rb +165 -0
  17. data/lib/xapit/config.rb +83 -0
  18. data/lib/xapit/facet.rb +59 -0
  19. data/lib/xapit/facet_blueprint.rb +59 -0
  20. data/lib/xapit/facet_option.rb +56 -0
  21. data/lib/xapit/index_blueprint.rb +117 -0
  22. data/lib/xapit/indexers/abstract_indexer.rb +101 -0
  23. data/lib/xapit/indexers/classic_indexer.rb +27 -0
  24. data/lib/xapit/indexers/simple_indexer.rb +31 -0
  25. data/lib/xapit/membership.rb +103 -0
  26. data/lib/xapit/query.rb +62 -0
  27. data/lib/xapit/query_parsers/abstract_query_parser.rb +115 -0
  28. data/lib/xapit/query_parsers/classic_query_parser.rb +19 -0
  29. data/lib/xapit/query_parsers/simple_query_parser.rb +75 -0
  30. data/spec/spec_helper.rb +15 -0
  31. data/spec/tmp/xapdb/flintlock +0 -0
  32. data/spec/tmp/xapdb/iamflint +0 -0
  33. data/spec/tmp/xapdb/postlist.DB +0 -0
  34. data/spec/tmp/xapdb/postlist.baseA +0 -0
  35. data/spec/tmp/xapdb/postlist.baseB +0 -0
  36. data/spec/tmp/xapdb/record.DB +0 -0
  37. data/spec/tmp/xapdb/record.baseA +0 -0
  38. data/spec/tmp/xapdb/record.baseB +0 -0
  39. data/spec/tmp/xapdb/spelling.DB +0 -0
  40. data/spec/tmp/xapdb/spelling.baseA +0 -0
  41. data/spec/tmp/xapdb/spelling.baseB +0 -0
  42. data/spec/tmp/xapdb/termlist.DB +0 -0
  43. data/spec/tmp/xapdb/termlist.baseA +0 -0
  44. data/spec/tmp/xapdb/termlist.baseB +0 -0
  45. data/spec/tmp/xapian_db/flintlock +0 -0
  46. data/spec/tmp/xapian_db/iamflint +0 -0
  47. data/spec/tmp/xapian_db/postlist.DB +0 -0
  48. data/spec/tmp/xapian_db/postlist.baseA +0 -0
  49. data/spec/tmp/xapian_db/record.DB +0 -0
  50. data/spec/tmp/xapian_db/record.baseA +0 -0
  51. data/spec/tmp/xapian_db/termlist.DB +0 -0
  52. data/spec/tmp/xapian_db/termlist.baseA +0 -0
  53. data/spec/tmp/xapiandab/flintlock +0 -0
  54. data/spec/tmp/xapiandab/iamflint +0 -0
  55. data/spec/tmp/xapiandab/postlist.DB +0 -0
  56. data/spec/tmp/xapiandab/postlist.baseA +0 -0
  57. data/spec/tmp/xapiandab/postlist.baseB +0 -0
  58. data/spec/tmp/xapiandab/record.DB +0 -0
  59. data/spec/tmp/xapiandab/record.baseA +0 -0
  60. data/spec/tmp/xapiandab/record.baseB +0 -0
  61. data/spec/tmp/xapiandab/spelling.DB +0 -0
  62. data/spec/tmp/xapiandab/spelling.baseA +0 -0
  63. data/spec/tmp/xapiandab/spelling.baseB +0 -0
  64. data/spec/tmp/xapiandab/termlist.DB +0 -0
  65. data/spec/tmp/xapiandab/termlist.baseA +0 -0
  66. data/spec/tmp/xapiandab/termlist.baseB +0 -0
  67. data/spec/tmp/xapiandatab/flintlock +0 -0
  68. data/spec/tmp/xapiandatab/iamflint +0 -0
  69. data/spec/tmp/xapiandatab/postlist.DB +0 -0
  70. data/spec/tmp/xapiandatab/postlist.baseA +0 -0
  71. data/spec/tmp/xapiandatab/postlist.baseB +0 -0
  72. data/spec/tmp/xapiandatab/record.DB +0 -0
  73. data/spec/tmp/xapiandatab/record.baseA +0 -0
  74. data/spec/tmp/xapiandatab/record.baseB +0 -0
  75. data/spec/tmp/xapiandatab/spelling.DB +0 -0
  76. data/spec/tmp/xapiandatab/spelling.baseA +0 -0
  77. data/spec/tmp/xapiandatab/spelling.baseB +0 -0
  78. data/spec/tmp/xapiandatab/termlist.DB +0 -0
  79. data/spec/tmp/xapiandatab/termlist.baseA +0 -0
  80. data/spec/tmp/xapiandatab/termlist.baseB +0 -0
  81. data/spec/tmp/xapiandataba/flintlock +0 -0
  82. data/spec/tmp/xapiandataba/iamflint +0 -0
  83. data/spec/tmp/xapiandataba/postlist.DB +0 -0
  84. data/spec/tmp/xapiandataba/postlist.baseA +0 -0
  85. data/spec/tmp/xapiandataba/postlist.baseB +0 -0
  86. data/spec/tmp/xapiandataba/record.DB +0 -0
  87. data/spec/tmp/xapiandataba/record.baseA +0 -0
  88. data/spec/tmp/xapiandataba/record.baseB +0 -0
  89. data/spec/tmp/xapiandataba/spelling.DB +0 -0
  90. data/spec/tmp/xapiandataba/spelling.baseA +0 -0
  91. data/spec/tmp/xapiandataba/spelling.baseB +0 -0
  92. data/spec/tmp/xapiandataba/termlist.DB +0 -0
  93. data/spec/tmp/xapiandataba/termlist.baseA +0 -0
  94. data/spec/tmp/xapiandataba/termlist.baseB +0 -0
  95. data/spec/tmp/xapiandatabas/flintlock +0 -0
  96. data/spec/tmp/xapiandatabas/iamflint +0 -0
  97. data/spec/tmp/xapiandatabas/postlist.DB +0 -0
  98. data/spec/tmp/xapiandatabas/postlist.baseA +0 -0
  99. data/spec/tmp/xapiandatabas/record.DB +0 -0
  100. data/spec/tmp/xapiandatabas/record.baseA +0 -0
  101. data/spec/tmp/xapiandatabas/termlist.DB +0 -0
  102. data/spec/tmp/xapiandatabas/termlist.baseA +0 -0
  103. data/spec/tmp/xapiandatb/flintlock +0 -0
  104. data/spec/tmp/xapiandatb/iamflint +0 -0
  105. data/spec/tmp/xapiandatb/postlist.DB +0 -0
  106. data/spec/tmp/xapiandatb/postlist.baseA +0 -0
  107. data/spec/tmp/xapiandatb/postlist.baseB +0 -0
  108. data/spec/tmp/xapiandatb/record.DB +0 -0
  109. data/spec/tmp/xapiandatb/record.baseA +0 -0
  110. data/spec/tmp/xapiandatb/record.baseB +0 -0
  111. data/spec/tmp/xapiandatb/spelling.DB +0 -0
  112. data/spec/tmp/xapiandatb/spelling.baseA +0 -0
  113. data/spec/tmp/xapiandatb/spelling.baseB +0 -0
  114. data/spec/tmp/xapiandatb/termlist.DB +0 -0
  115. data/spec/tmp/xapiandatb/termlist.baseA +0 -0
  116. data/spec/tmp/xapiandatb/termlist.baseB +0 -0
  117. data/spec/tmp/xapiandbase/flintlock +0 -0
  118. data/spec/tmp/xapiandbase/iamflint +0 -0
  119. data/spec/tmp/xapiandbase/postlist.DB +0 -0
  120. data/spec/tmp/xapiandbase/postlist.baseA +0 -0
  121. data/spec/tmp/xapiandbase/postlist.baseB +0 -0
  122. data/spec/tmp/xapiandbase/record.DB +0 -0
  123. data/spec/tmp/xapiandbase/record.baseA +0 -0
  124. data/spec/tmp/xapiandbase/record.baseB +0 -0
  125. data/spec/tmp/xapiandbase/spelling.DB +0 -0
  126. data/spec/tmp/xapiandbase/spelling.baseA +0 -0
  127. data/spec/tmp/xapiandbase/spelling.baseB +0 -0
  128. data/spec/tmp/xapiandbase/termlist.DB +0 -0
  129. data/spec/tmp/xapiandbase/termlist.baseA +0 -0
  130. data/spec/tmp/xapiandbase/termlist.baseB +0 -0
  131. data/spec/xapit/collection_spec.rb +153 -0
  132. data/spec/xapit/config_spec.rb +48 -0
  133. data/spec/xapit/facet_blueprint_spec.rb +29 -0
  134. data/spec/xapit/facet_option_spec.rb +80 -0
  135. data/spec/xapit/facet_spec.rb +73 -0
  136. data/spec/xapit/index_blueprint_spec.rb +60 -0
  137. data/spec/xapit/indexers/abstract_indexer_spec.rb +74 -0
  138. data/spec/xapit/indexers/classic_indexer_spec.rb +26 -0
  139. data/spec/xapit/indexers/simple_indexer_spec.rb +53 -0
  140. data/spec/xapit/membership_spec.rb +39 -0
  141. data/spec/xapit/query_parsers/abstract_query_parser_spec.rb +23 -0
  142. data/spec/xapit/query_parsers/classic_query_parser_spec.rb +15 -0
  143. data/spec/xapit/query_parsers/simple_query_parser_spec.rb +86 -0
  144. data/spec/xapit/query_spec.rb +41 -0
  145. data/spec/xapit_member.rb +32 -0
  146. data/tasks/spec.rb +9 -0
  147. data/tasks/xapit.rake +9 -0
  148. data/tmp/xapiandatabase/flintlock +0 -0
  149. data/tmp/xapiandatabase/iamflint +0 -0
  150. data/tmp/xapiandatabase/postlist.DB +0 -0
  151. data/tmp/xapiandatabase/postlist.baseA +0 -0
  152. data/tmp/xapiandatabase/postlist.baseB +0 -0
  153. data/tmp/xapiandatabase/record.DB +0 -0
  154. data/tmp/xapiandatabase/record.baseA +0 -0
  155. data/tmp/xapiandatabase/record.baseB +0 -0
  156. data/tmp/xapiandatabase/spelling.DB +0 -0
  157. data/tmp/xapiandatabase/spelling.baseA +0 -0
  158. data/tmp/xapiandatabase/spelling.baseB +0 -0
  159. data/tmp/xapiandatabase/termlist.DB +0 -0
  160. data/tmp/xapiandatabase/termlist.baseA +0 -0
  161. data/tmp/xapiandatabase/termlist.baseB +0 -0
  162. data/tmp/xapiandatabase/value.baseB +0 -0
  163. data/tmp/xapiandb/flintlock +0 -0
  164. data/tmp/xapiandb/iamflint +0 -0
  165. data/tmp/xapiandb/postlist.DB +0 -0
  166. data/tmp/xapiandb/postlist.baseA +0 -0
  167. data/tmp/xapiandb/postlist.baseB +0 -0
  168. data/tmp/xapiandb/record.DB +0 -0
  169. data/tmp/xapiandb/record.baseA +0 -0
  170. data/tmp/xapiandb/record.baseB +0 -0
  171. data/tmp/xapiandb/spelling.DB +0 -0
  172. data/tmp/xapiandb/spelling.baseA +0 -0
  173. data/tmp/xapiandb/spelling.baseB +0 -0
  174. data/tmp/xapiandb/termlist.DB +0 -0
  175. data/tmp/xapiandb/termlist.baseA +0 -0
  176. data/tmp/xapiandb/termlist.baseB +0 -0
  177. data/tmp/xapiandb/value.baseB +0 -0
  178. data/uninstall.rb +5 -0
  179. data/xapit.gemspec +30 -0
  180. metadata +257 -0
@@ -0,0 +1,73 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Xapit::Facet do
4
+ describe "with database" do
5
+ before(:each) do
6
+ XapitMember.xapit do |index|
7
+ index.facet :visible
8
+ end
9
+ end
10
+
11
+ describe "indexed" do
12
+ before(:each) do
13
+ @visible1 = XapitMember.new(:visible => true)
14
+ @visible2 = XapitMember.new(:visible => true)
15
+ @invisible = XapitMember.new(:visible => false)
16
+ Xapit.index_all
17
+ end
18
+
19
+ describe "facet from empty search" do
20
+ before(:each) do
21
+ @facet = XapitMember.search("").facets.first
22
+ end
23
+
24
+ it "should have the name of 'Visible'" do
25
+ @facet.name.should == 'Visible'
26
+ end
27
+
28
+ it "should have true and false options" do
29
+ @facet.options.map(&:name).sort.should == %w[false true]
30
+ end
31
+
32
+ it "should have record count" do
33
+ @facet.options.detect { |o| o.name == 'true' }.count.should == 2
34
+ @facet.options.detect { |o| o.name == 'false' }.count.should == 1
35
+ end
36
+
37
+ it "should have identifier for options" do
38
+ blueprint = Xapit::FacetBlueprint.new(XapitMember, 0, :visible)
39
+ @facet.options.detect { |o| o.name == 'true' }.identifier.should == blueprint.identifiers_for(@visible1).first
40
+ @facet.options.detect { |o| o.name == 'false' }.identifier.should == blueprint.identifiers_for(@invisible).first
41
+ end
42
+
43
+ it "should have matching identifiers" do
44
+ blueprint = Xapit::FacetBlueprint.new(XapitMember, 0, :visible)
45
+ hash = { blueprint.identifiers_for(@visible1).first => 2, blueprint.identifiers_for(@invisible).first => 1 }
46
+ @facet.matching_identifiers.should == hash
47
+ end
48
+
49
+ it "should not include matching identifiers that are current" do
50
+ blueprint = Xapit::FacetBlueprint.new(XapitMember, 0, :visible)
51
+ @facet.existing_facet_identifiers = blueprint.identifiers_for(@visible1)
52
+ @facet.matching_identifiers.should == { blueprint.identifiers_for(@invisible).first => 1 }
53
+ end
54
+
55
+ it "should return identifier on to_param" do
56
+ blueprint = Xapit::FacetBlueprint.new(XapitMember, 0, :visible)
57
+ @facet.options.detect { |o| o.name == 'true' }.to_param.should == blueprint.identifiers_for(@visible1).first
58
+ end
59
+
60
+ it "should sort options in alphabetical order" do
61
+ @facet.options.first.name.should == 'false'
62
+ @facet.options.last.name.should == 'true'
63
+ end
64
+ end
65
+
66
+ it "should not list facets if only one option is found" do
67
+ blueprint = Xapit::FacetBlueprint.new(XapitMember, 0, :visible)
68
+ facets = XapitMember.search("", :facets => blueprint.identifiers_for(@visible1)).facets
69
+ facets.should be_empty
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,60 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Xapit::IndexBlueprint do
4
+ before(:each) do
5
+ @index = Xapit::IndexBlueprint.new(XapitMember)
6
+ end
7
+
8
+ it "should remember text attributes" do
9
+ @index.text(:foo)
10
+ @index.text(:bar, :blah)
11
+ @index.text(:custom) { |t| t*t }
12
+ @index.text_attributes.keys.should include(:foo, :bar, :blah, :custom)
13
+ @index.text_attributes[:foo][:proc].should be_nil
14
+ @index.text_attributes[:custom][:proc].should be_kind_of(Proc)
15
+ end
16
+
17
+ it "should remember field attributes" do
18
+ @index.field(:foo)
19
+ @index.field(:bar, :blah)
20
+ @index.field_attributes.should include(:foo, :bar, :blah)
21
+ end
22
+
23
+ it "should remember facets" do
24
+ @index.facet(:foo)
25
+ @index.facet(:bar, "Baz")
26
+ @index.facets.map(&:name).should == ["Foo", "Baz"]
27
+ end
28
+
29
+ it "should remember sortable attributes" do
30
+ @index.sortable(:foo)
31
+ @index.sortable(:bar, :blah)
32
+ @index.sortable_attributes.should include(:foo, :bar, :blah)
33
+ end
34
+
35
+ it "should have a sortable position offset by facets" do
36
+ @index.facet(:foo)
37
+ @index.facet(:test)
38
+ @index.sortable(:bar, :blah)
39
+ @index.sortable_position_for(:blah).should == 3
40
+ end
41
+
42
+ it "should index member document into database" do
43
+ XapitMember.new
44
+ @index.index_all
45
+ Xapit::Config.writable_database.doccount.should >= 1
46
+ Xapit::Config.writable_database.flush
47
+ end
48
+
49
+ it "should remember all blueprints and index each of them" do
50
+ stub(Xapit::Config.writable_database).add_document
51
+ mock(@index).index_all
52
+ Xapit::IndexBlueprint.index_all
53
+ end
54
+
55
+ it "should pass in extra arguments to each method" do
56
+ index = Xapit::IndexBlueprint.new(Object, :foo, :bar => :blah)
57
+ mock(Object).find_each(:foo, :bar => :blah)
58
+ index.index_all
59
+ end
60
+ end
@@ -0,0 +1,74 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe Xapit::AbstractIndexer do
4
+ before(:each) do
5
+ XapitMember.xapit { } # to ensure methods are included
6
+ @index = Xapit::IndexBlueprint.new(XapitMember)
7
+ @indexer = Xapit::SimpleIndexer.new(@index)
8
+ end
9
+
10
+ it "should map field to term with 'X' prefix" do
11
+ member = Object.new
12
+ stub(member).category { "Water" }
13
+ @index.field(:category)
14
+ @indexer.field_terms(member).should == %w[Xcategory-water]
15
+ end
16
+
17
+ it "should add terms separately when array is returned" do
18
+ member = Object.new
19
+ stub(member).category { ["Water", "Liquid"] }
20
+ @index.field(:category)
21
+ @indexer.field_terms(member).should == %w[Xcategory-water Xcategory-liquid]
22
+ end
23
+
24
+ it "should have base terms with class name and id" do
25
+ member = Object.new
26
+ stub(member).id { 123 }
27
+ @indexer.base_terms(member).should == %w[CObject QObject-123]
28
+ end
29
+
30
+ it "should add terms, values and options for facets" do
31
+ Xapit::Config.writable_database # force xapit to use a writable database at the beginning
32
+ stub(XapitMember).xapit_index_blueprint { @index }
33
+ member = XapitMember.new(:foo => ["ABC", "DEF"])
34
+ ids = Xapit::FacetBlueprint.new(XapitMember, 0, :foo).identifiers_for(member)
35
+ @index.facet(:foo)
36
+ @indexer.facet_terms(member).should == ids.map { |id| "F#{id}" }
37
+ @indexer.values(member).should == [ids.join('-')]
38
+ @indexer.save_facet_options_for(member)
39
+ ids.map { |id| Xapit::FacetOption.find(id).name }.should == ["ABC", "DEF"]
40
+ end
41
+
42
+ it "should add values for sortable fields" do
43
+ member = Object.new
44
+ stub(member).name { "Foo" }
45
+ @index.sortable(:name)
46
+ @indexer.values(member).should == ["foo"]
47
+ end
48
+
49
+ it "should add terms and values to xapian document" do
50
+ member = Object.new
51
+ stub(member).id { 123 }
52
+ stub(@indexer).values.returns(%w[value list])
53
+ stub(@indexer).other_terms { %w[term list] }
54
+ doc = @indexer.document_for(member)
55
+ doc.should be_kind_of(Xapian::Document)
56
+ doc.data.should == "Object-123"
57
+ doc.values.map(&:value).sort.should == %w[value list].sort
58
+ doc.terms.map(&:term).sort.should == %w[term list].sort
59
+ end
60
+
61
+ it "should convert time to integer before saving as field term" do
62
+ member = Object.new
63
+ stub(member).created_at { Time.now }
64
+ @index.field(:created_at)
65
+ @indexer.field_terms(member).should == ["Xcreated_at-#{member.created_at.to_i}"]
66
+ end
67
+
68
+ it "should convert date to time then integer before saving as field term" do
69
+ member = Object.new
70
+ stub(member).created_on { Date.today }
71
+ @index.field(:created_on)
72
+ @indexer.field_terms(member).should == ["Xcreated_on-#{member.created_on.to_time.to_i}"]
73
+ end
74
+ end
@@ -0,0 +1,26 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe Xapit::ClassicIndexer do
4
+ before(:each) do
5
+ @index = Xapit::IndexBlueprint.new(XapitMember)
6
+ @indexer = Xapit::ClassicIndexer.new(@index)
7
+ end
8
+
9
+ it "should add text terms to document when indexing attributes" do
10
+ member = Object.new
11
+ stub(member).name { "jumping high" }
12
+ @index.text(:name)
13
+ document = Xapian::Document.new
14
+ @indexer.index_text_attributes(member, document)
15
+ document.terms.map(&:term).sort.should == %w[Zjump Zhigh jumping high].sort
16
+ end
17
+
18
+ it "should use given block to generate text terms" do
19
+ member = Object.new
20
+ stub(member).name { "foobar" }
21
+ @index.text(:name) { |t| [t.length] }
22
+ document = Xapian::Document.new
23
+ @indexer.index_text_attributes(member, document)
24
+ document.terms.map(&:term).sort.should == %w[6].sort
25
+ end
26
+ end
@@ -0,0 +1,53 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe Xapit::SimpleIndexer do
4
+ before(:each) do
5
+ @index = Xapit::IndexBlueprint.new(XapitMember)
6
+ @indexer = Xapit::SimpleIndexer.new(@index)
7
+ end
8
+
9
+ it "should return terms for text attributes" do
10
+ member = Object.new
11
+ stub(member).description { "This is a test" }
12
+ @index.text(:description)
13
+ @indexer.terms_for_attribute_without_stemming(member, :description, {}).should == %w[this is a test]
14
+ end
15
+
16
+ it "should return text term with stemming added" do
17
+ member = Object.new
18
+ stub(member).description { "jumping high" }
19
+ @index.text(:description)
20
+ @indexer.terms_for_attribute(member, :description, {}).should == %w[jumping Zjump high Zhigh]
21
+ end
22
+
23
+ it "should convert attribute to string when converting text to terms" do
24
+ member = Object.new
25
+ stub(member).num { 123 }
26
+ @index.text(:num)
27
+ @indexer.terms_for_attribute_without_stemming(member, :num, {}).should == %w[123]
28
+ end
29
+
30
+ it "should add text terms to document when indexing attributes" do
31
+ @index.text(:description)
32
+ stub(@indexer).terms_for_attribute { %w[term list] }
33
+ document = Xapian::Document.new
34
+ @indexer.index_text_attributes(nil, document)
35
+ document.terms.map(&:term).sort.should == %w[term list].sort
36
+ end
37
+
38
+ it "should use given block to generate text terms" do
39
+ member = Object.new
40
+ stub(member).name { "foobar" }
41
+ proc = lambda { |t| [t.length] }
42
+ @indexer.terms_for_attribute_without_stemming(member, :name, { :proc => proc }).should == ["6"]
43
+ end
44
+
45
+ it "should increment term frequency by weight option" do
46
+ member = Object.new
47
+ stub(member).description { "This is a test" }
48
+ @index.text(:description, :weight => 10)
49
+ document = Xapian::Document.new
50
+ @indexer.index_text_attributes(member, document)
51
+ document.terms.first.wdf.should == 10
52
+ end
53
+ end
@@ -0,0 +1,39 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ class OtherMember
4
+ include Xapit::Membership
5
+ end
6
+
7
+ describe XapitMember do
8
+ it "should have xapit method" do
9
+ OtherMember.should respond_to(:xapit)
10
+ end
11
+
12
+ it "should not respond to xapit_index_blueprint if xapit isn't called" do
13
+ OtherMember.should_not respond_to(:xapit_index_blueprint)
14
+ OtherMember.should_not respond_to(:search)
15
+ OtherMember.new.should_not respond_to(:search_similar)
16
+ OtherMember.new.should_not respond_to(:xapit_relevance)
17
+ end
18
+
19
+ describe "with description indexed" do
20
+ before(:each) do
21
+ XapitMember.xapit do |index|
22
+ index.text :description
23
+ end
24
+ end
25
+
26
+ it "should have xapit index blueprint" do
27
+ XapitMember.xapit_index_blueprint.should be_kind_of(Xapit::IndexBlueprint)
28
+ end
29
+ it "should return collection from search" do
30
+ XapitMember.search("foo").class.should == Xapit::Collection
31
+ end
32
+
33
+ it "should store xapit_relevance" do
34
+ member = XapitMember.new
35
+ member.xapit_relevance = 123
36
+ member.xapit_relevance.should == 123
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,23 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe Xapit::AbstractQueryParser do
4
+ before(:each) do
5
+ end
6
+
7
+ it "parse conditions hash into terms" do
8
+ parser = Xapit::AbstractQueryParser.new(:conditions => { :foo => 'bar', 'hello' => :world })
9
+ parser.condition_terms.sort.should == ["Xfoo-bar", "Xhello-world"].sort
10
+ end
11
+
12
+ it "convert time into integer before placing in condition term" do
13
+ time = Time.now
14
+ parser = Xapit::AbstractQueryParser.new(:conditions => { :time => time })
15
+ parser.condition_terms.should == ["Xtime-#{time.to_i}"]
16
+ end
17
+
18
+ it "convert date into time then integer before placing in condition term" do
19
+ date = Date.today
20
+ parser = Xapit::AbstractQueryParser.new(:conditions => { :date => date })
21
+ parser.condition_terms.should == ["Xdate-#{date.to_time.to_i}"]
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe Xapit::ClassicQueryParser do
4
+ before(:each) do
5
+ @parser = Xapit::ClassicQueryParser.new(nil, nil)
6
+ end
7
+
8
+ it "should have an initial xapian parser with stemming and default operator support" do
9
+ expected = Xapian::QueryParser.new
10
+ expected.stemmer = Xapian::Stem.new("english")
11
+ expected.stemming_strategy = Xapian::QueryParser::STEM_SOME
12
+ expected.default_op = Xapian::Query::OP_AND
13
+ @parser.xapian_query_from_text("foo bar").description.should == expected.parse_query("foo bar").description
14
+ end
15
+ end
@@ -0,0 +1,86 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe Xapit::SimpleQueryParser do
4
+ describe "with stemming" do
5
+ it "should include stemmed variation for single word" do
6
+ Xapit::SimpleQueryParser.new(nil, "jumping").parsed.should == "Zjump"
7
+ end
8
+
9
+ it "should include stemmed variations for multiple words" do
10
+ Xapit::SimpleQueryParser.new(nil, "jumping high").parsed.should == [:and, "Zjump", "Zhigh"]
11
+ end
12
+
13
+ it "should add stemmed variation for 'not' option" do
14
+ Xapit::SimpleQueryParser.new(nil, "jumping not high").parsed.should == [:and, "Zjump", [:not, "Zhigh"]]
15
+ end
16
+ end
17
+
18
+ describe "without stemming" do
19
+ before(:each) do
20
+ Xapit::Config.options[:stemming] = false
21
+ end
22
+
23
+ it "should parse nothing for simple string" do
24
+ Xapit::SimpleQueryParser.new(nil, "foobar").parsed.should == "foobar"
25
+ end
26
+
27
+ it "should parse empty string as blank string" do
28
+ Xapit::SimpleQueryParser.new(nil, "").parsed.should == ""
29
+ Xapit::SimpleQueryParser.new(nil, " \t ").parsed.should == ""
30
+ end
31
+
32
+ it "should parse white space as AND" do
33
+ Xapit::SimpleQueryParser.new(nil, "foo bar").parsed.should == [:and, "foo", "bar"]
34
+ Xapit::SimpleQueryParser.new(nil, "\t foo \t bar ").parsed.should == [:and, "foo", "bar"]
35
+ end
36
+
37
+ it "should parse simple 'or' query" do
38
+ Xapit::SimpleQueryParser.new(nil, "foo or bar").parsed.should == [:or, "foo", "bar"]
39
+ Xapit::SimpleQueryParser.new(nil, " foo or\t bar \t ").parsed.should == [:or, "foo", "bar"]
40
+ Xapit::SimpleQueryParser.new(nil, "foo OR bar").parsed.should == [:or, "foo", "bar"]
41
+ end
42
+
43
+ it "should parse 'and' within 'or' giving 'or' presedence" do
44
+ Xapit::SimpleQueryParser.new(nil, "foo or bar blah").parsed.should == [:or, "foo", [:and, "bar", "blah"]]
45
+ Xapit::SimpleQueryParser.new(nil, "foo bar or blah").parsed.should == [:or, [:and, "foo", "bar"], "blah"]
46
+ end
47
+
48
+ it "should parse simple 'not' query" do
49
+ Xapit::SimpleQueryParser.new(nil, "foo not bar").parsed.should == [:and, "foo", [:not, "bar"]]
50
+ Xapit::SimpleQueryParser.new(nil, "foo NOT bar blah").parsed.should == [:and, "foo", [:not, "bar"], "blah"]
51
+ end
52
+
53
+ it "should convert simple query to xapian query" do
54
+ Xapit::SimpleQueryParser.new(nil, "foo bar").xapian_query.description.should == Xapian::Query.new(Xapian::Query::OP_AND, "foo", "bar").description
55
+ Xapit::SimpleQueryParser.new(nil, "foo OR bar").xapian_query.description.should == Xapian::Query.new(Xapian::Query::OP_OR, "foo", "bar").description
56
+ end
57
+
58
+ it "should convert deep query to xapian query" do
59
+ query = Xapian::Query.new(Xapian::Query::OP_OR,
60
+ Xapian::Query.new(Xapian::Query::OP_OR, ["foo"]),
61
+ Xapian::Query.new(Xapian::Query::OP_AND, ["bar", "blah"])
62
+ )
63
+ Xapit::SimpleQueryParser.new(nil, "foo or bar blah").xapian_query.description.should == query.description
64
+ end
65
+
66
+ it "should convert multi-deep query to xapian query" do
67
+ query = Xapian::Query.new(Xapian::Query::OP_OR,
68
+ Xapian::Query.new(Xapian::Query::OP_AND, ["foo", "bar"]),
69
+ Xapian::Query.new(Xapian::Query::OP_AND, ["test", "blah"])
70
+ )
71
+ Xapit::SimpleQueryParser.new(nil, "foo bar or test blah").xapian_query.description.should == query.description
72
+ end
73
+
74
+ it "should convert single word query to xapian query" do
75
+ Xapit::SimpleQueryParser.new(nil, "foo").xapian_query.description.should == Xapian::Query.new(Xapian::Query::OP_AND, ["foo"]).description
76
+ end
77
+
78
+ it "should convert negative query to xapian query" do
79
+ query = Xapian::Query.new(Xapian::Query::OP_AND_NOT,
80
+ Xapian::Query.new(Xapian::Query::OP_AND, ["foo"]),
81
+ Xapian::Query.new(Xapian::Query::OP_AND, ["bar"])
82
+ )
83
+ Xapit::SimpleQueryParser.new(nil, "foo not bar").xapian_query.description.should == query.description
84
+ end
85
+ end
86
+ end