xapit 0.1.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.
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