Skip to content
This repository
Browse code

use unique number instead of incremental index to represent field, so…

…rtable, and facet attributes - closes #51
  • Loading branch information...
commit f26d10bb0c4c6f91511814401405c8611170e493 1 parent 7e3c4c1
Ryan Bates authored April 15, 2011
7  README.rdoc
Source Rendered
@@ -33,7 +33,7 @@ To make a model searchable you must define an index through the +xapit+ method.
33 33
     end
34 34
   end
35 35
 
36  
-This indexes the model to be searched in a variety of ways (shown below). See the {Indexing}[http://wiki.github.com/ryanb/xapit/indexing] wiki page for more details.
  36
+This indexes the model to be searched in a variety of ways (shown below). See the {Indexing}[http://wiki.github.com/ryanb/xapit/indexing] wiki page for details.
37 37
 
38 38
 The index will automatically be updated when records are added or removed. You can regenerate the index manually to fill it with any existing records.
39 39
 
@@ -131,13 +131,13 @@ There are many other options you can pass into here. This is a more advanced con
131 131
 
132 132
 == Production
133 133
 
134  
-The default Xapit setup works well in development because there's only one instance of the Rails app running. However in production you will need to move Xapit into a separate server so all of the instances can communicate to it. To do this, make a Rackup file that looks like this.
  134
+The default Xapit setup works well in development because there is only one instance of the Rails app running. However in production you will need to move Xapit to a separate process so all of the instances can communicate to it. To do this, make a Rackup file that looks like this.
135 135
 
136 136
   require "rubygems"
137 137
   require "xapit"
138 138
   run Xapit.server(:database => "path/to/xapiandb")
139 139
 
140  
-Start up that rack app and point to it in the <tt>setup_xapit.rb</tt> file.
  140
+Start up that Rack app and point to it in the <tt>setup_xapit.rb</tt> file.
141 141
 
142 142
   if Rails.evn.production?
143 143
     Xapit.setup(:database => "http://localhost:9292")
@@ -145,6 +145,7 @@ Start up that rack app and point to it in the <tt>setup_xapit.rb</tt> file.
145 145
     Xapit.setup(:database => "#{Rails.root}/db/xapiandb")
146 146
   end
147 147
 
  148
+Now all Xapit commands will go through that Rack application so the database is no longer embedded into the Rails app.
148 149
 
149 150
 
150 151
 == Adapters
5  lib/xapit.rb
@@ -50,6 +50,11 @@ def self.serialize_value(value)
50 50
       value.to_s.downcase
51 51
     end
52 52
   end
  53
+
  54
+  # The Xapian value index position of an attribute
  55
+  def self.value_index(*args)
  56
+    Zlib.crc32(["xapit", *args].join) % 99999999 # Figure out the true max of a xapian value index
  57
+  end
53 58
 end
54 59
 
55 60
 require 'xapit/membership'
14  lib/xapit/index_blueprint.rb
@@ -79,20 +79,6 @@ def index_all
79 79
       end
80 80
     end
81 81
 
82  
-    # The Xapian value index position of a sortable attribute
83  
-    def position_of_sortable(sortable_attribute)
84  
-      index = sortable_attributes.map(&:to_s).index(sortable_attribute.to_s)
85  
-      raise "Unable to find indexed sortable attribute \"#{sortable_attribute}\" in #{@member_class} sortable attributes: #{sortable_attributes.inspect}" if index.nil?
86  
-      index + facets.size
87  
-    end
88  
-
89  
-    # The Xapian value index position of a field attribute
90  
-    def position_of_field(field_attribute)
91  
-      index = field_attributes.map(&:to_s).index(field_attribute.to_s)
92  
-      raise "Unable to find indexed field attribute \"#{field_attribute}\" in #{@member_class} field attributes: #{field_attributes.inspect}" if index.nil?
93  
-      index + facets.size + sortable_attributes.size
94  
-    end
95  
-
96 82
     # Add a single record to the index if it matches the xapit options.
97 83
     def create_record(member_id)
98 84
       member = @member_class.xapit_adapter.find_single(member_id, *@args)
23  lib/xapit/indexers/abstract_indexer.rb
@@ -17,9 +17,9 @@ def document_for(member)
17 17
       document.data = "#{member.class}-#{member.id}"
18 18
       index_text_attributes(member, document)
19 19
       index_terms(other_terms(member), document)
20  
-      values(member).each_with_index do |value, index|
  20
+      values(member).each do |identifier, value|
21 21
         document.values << value
22  
-        document.value_indexes << index
  22
+        document.value_indexes << Xapit.value_index(identifier)
23 23
       end
24 24
       save_facet_options_for(member)
25 25
       document
@@ -76,29 +76,32 @@ def text_terms(member) # REFACTORME some duplicaiton with simple indexer
76 76
     end
77 77
 
78 78
     def values(member)
79  
-      facet_values(member) + sortable_values(member) + field_values(member)
  79
+      facet_values(member).merge(sortable_values(member)).merge(field_values(member))
80 80
     end
81 81
 
82 82
     def sortable_values(member)
83  
-      @blueprint.sortable_attributes.map do |sortable|
  83
+      @blueprint.sortable_attributes.inject({}) do |hash, sortable|
84 84
         value = member.send(sortable)
85 85
         value = value.first if value.kind_of? Array
86  
-        Xapit.serialize_value(value)
  86
+        hash["sortable#{sortable}"] = Xapit.serialize_value(value)
  87
+        hash
87 88
       end
88 89
     end
89 90
 
90 91
     # TODO remove duplication with sortable_values
91 92
     def field_values(member)
92  
-      @blueprint.field_attributes.map do |sortable|
93  
-        value = member.send(sortable)
  93
+      @blueprint.field_attributes.inject({}) do |hash, field|
  94
+        value = member.send(field)
94 95
         value = value.first if value.kind_of? Array
95  
-        Xapit.serialize_value(value)
  96
+        hash["field#{field}"] = Xapit.serialize_value(value)
  97
+        hash
96 98
       end
97 99
     end
98 100
 
99 101
     def facet_values(member)
100  
-      @blueprint.facets.map do |facet|
101  
-        facet.identifiers_for(member).join("-")
  102
+      @blueprint.facets.inject({}) do |hash, facet|
  103
+        hash["facet#{facet.attribute}"] = facet.identifiers_for(member).join("-")
  104
+        hash
102 105
       end
103 106
     end
104 107
 
10  lib/xapit/query_parsers/abstract_query_parser.rb
@@ -40,14 +40,13 @@ def offset
40 40
     end
41 41
 
42 42
     def sort_by_values
43  
-      if @options[:order] && @member_class
44  
-        index = @member_class.xapit_index_blueprint
  43
+      if @options[:order]
45 44
         if @options[:order].kind_of? Array
46 45
           @options[:order].map do |attribute|
47  
-            index.position_of_sortable(attribute)
  46
+            Xapit.value_index(:sortable, attribute)
48 47
           end
49 48
         else
50  
-          [index.position_of_sortable(@options[:order])]
  49
+          [Xapit.value_index(:sortable, @options[:order])]
51 50
         end
52 51
       end
53 52
     end
@@ -148,8 +147,7 @@ def condition_terms_from_hash(conditions)
148 147
 
149 148
     def condition_term(name, value)
150 149
       if value.kind_of?(Range) && @member_class
151  
-        position = @member_class.xapit_index_blueprint.position_of_field(name)
152  
-        Xapian::Query.new(Xapian::Query::OP_VALUE_RANGE, position, Xapit.serialize_value(value.begin), Xapit.serialize_value(value.end))
  150
+        Xapian::Query.new(Xapian::Query::OP_VALUE_RANGE, Xapit.value_index(:field, name), Xapit.serialize_value(value.begin), Xapit.serialize_value(value.end))
153 151
       elsif value.to_s.ends_with?("*") && value.to_s.strip.length > 2
154 152
         wildcard_query(value, "X#{name}-")
155 153
       else
14  spec/xapit/index_blueprint_spec.rb
@@ -33,20 +33,6 @@
33 33
     @index.sortable_attributes.should include(:foo, :bar, :blah)
34 34
   end
35 35
 
36  
-  it "should have a sortable position offset by facets" do
37  
-    @index.facet(:foo)
38  
-    @index.facet(:test)
39  
-    @index.sortable(:bar, :blah)
40  
-    @index.position_of_sortable(:blah).should == 3
41  
-  end
42  
-
43  
-  it "should have a field position offset by facets + sortable" do
44  
-    @index.facet(:foo)
45  
-    @index.sortable(:bar, :blah)
46  
-    @index.field(:bar, :blah)
47  
-    @index.position_of_field(:blah).should == 4
48  
-  end
49  
-
50 36
   it "should index member document into database" do
51 37
     XapitMember.new
52 38
     @index.index_all
16  spec/xapit/indexers/abstract_indexer_spec.rb
@@ -34,7 +34,7 @@
34 34
     ids = Xapit::FacetBlueprint.new(XapitMember, 0, :foo).identifiers_for(member)
35 35
     @index.facet(:foo)
36 36
     @indexer.facet_terms(member).should == ids.map { |id| "F#{id}" }
37  
-    @indexer.values(member).should == [ids.join('-')]
  37
+    @indexer.values(member).should == {"facetfoo" => ids.join('-')}
38 38
     @indexer.save_facet_options_for(member)
39 39
     ids.map { |id| Xapit::FacetOption.find(id).name }.should == ["ABC", "DEF"]
40 40
   end
@@ -43,13 +43,13 @@
43 43
     member = Object.new
44 44
     stub(member).name { "Foo" }
45 45
     @index.sortable(:name)
46  
-    @indexer.values(member).should == ["foo"]
  46
+    @indexer.values(member).should == {"sortablename" => "foo"}
47 47
   end
48 48
 
49 49
   it "should add terms and values to xapian document" do
50 50
     member = Object.new
51 51
     stub(member).id { 123 }
52  
-    stub(@indexer).values.returns(%w[value list])
  52
+    stub(@indexer).values.returns({"foo" => "value", "bar" => "list"})
53 53
     stub(@indexer).other_terms { %w[term list] }
54 54
     doc = @indexer.document_for(member)
55 55
     doc.should be_kind_of(Xapit::Document)
@@ -76,21 +76,21 @@
76 76
     member = Object.new
77 77
     stub(member).age { 7.89 }
78 78
     @index.sortable(:age)
79  
-    @indexer.values(member).should == [Xapian.sortable_serialise(7.89)]
  79
+    @indexer.values(member).should == {"sortableage" => Xapian.sortable_serialise(7.89)}
80 80
   end
81 81
 
82 82
   it "should only use first value if sortable attribute is an array" do
83 83
     member = Object.new
84 84
     stub(member).age { [1, 2] }
85 85
     @index.sortable(:age)
86  
-    @indexer.values(member).should == [Xapian.sortable_serialise(1)]
  86
+    @indexer.values(member).should == {"sortableage" => Xapian.sortable_serialise(1)}
87 87
   end
88 88
 
89 89
   it "should add sortable_serialze value for numeric field" do
90 90
     member = Object.new
91 91
     stub(member).age { [1, 2] }
92 92
     @index.field(:age)
93  
-    @indexer.values(member).should == [Xapian.sortable_serialise(1)]
  93
+    @indexer.values(member).should == {"fieldage" => Xapian.sortable_serialise(1)}
94 94
   end
95 95
 
96 96
   it "should use sortable_serialze for date field" do
@@ -98,7 +98,7 @@
98 98
     member = Object.new
99 99
     stub(member).age { date }
100 100
     @index.field(:age)
101  
-    @indexer.values(member).should == [Xapian.sortable_serialise(date.to_time.to_i)]
  101
+    @indexer.values(member).should == {"fieldage" => Xapian.sortable_serialise(date.to_time.to_i)}
102 102
   end
103 103
 
104 104
   it "should use sortable_serialze for time field" do
@@ -106,6 +106,6 @@
106 106
     member = Object.new
107 107
     stub(member).age { time }
108 108
     @index.field(:age)
109  
-    @indexer.values(member).should == [Xapian.sortable_serialise(time.to_i)]
  109
+    @indexer.values(member).should == {"fieldage" => Xapian.sortable_serialise(time.to_i)}
110 110
   end
111 111
 end
11  spec/xapit/query_parsers/abstract_query_parser_spec.rb
@@ -32,9 +32,16 @@
32 32
     parser.not_condition_terms.first.xapian_query.description.should == Xapit::Query.new(%w[Xfoo-hello Xfoo-world], :or).xapian_query.description
33 33
   end
34 34
 
  35
+  it "should have a unique position for types of attributes" do
  36
+    # TODO this spec doesn't really belong here
  37
+    Xapit.value_index(:sortable, :bar).should == Xapit.value_index(:sortable, :bar)
  38
+    Xapit.value_index(:sortable, :bar).should_not == Xapit.value_index(:sortable, :baz)
  39
+    Xapit.value_index(:sortable, :bar).should_not == Xapit.value_index(:field, :bar)
  40
+  end
  41
+
35 42
   it "should allow range condition to be specified and use VALUE_RANGE xapian query." do
36 43
     XapitMember.xapit { |i| i.field :foo }
37  
-    expected = Xapian::Query.new(Xapian::Query::OP_VALUE_RANGE, 0, Xapian.sortable_serialise(2), Xapian.sortable_serialise(5))
  44
+    expected = Xapian::Query.new(Xapian::Query::OP_VALUE_RANGE, Xapit.value_index(:field, :foo), Xapian.sortable_serialise(2), Xapian.sortable_serialise(5))
38 45
     parser = Xapit::AbstractQueryParser.new(XapitMember, :conditions => { :foo => 2..5 })
39 46
     parser.condition_terms.first.description.should == expected.description
40 47
   end
@@ -42,7 +49,7 @@
42 49
   it "should allow range condition to be specified in array." do
43 50
     XapitMember.xapit { |i| i.field :foo }
44 51
     expected = Xapian::Query.new(Xapian::Query::OP_OR,
45  
-      Xapian::Query.new(Xapian::Query::OP_VALUE_RANGE, 0, Xapian.sortable_serialise(2), Xapian.sortable_serialise(5)),
  52
+      Xapian::Query.new(Xapian::Query::OP_VALUE_RANGE, Xapit.value_index(:field, :foo), Xapian.sortable_serialise(2), Xapian.sortable_serialise(5)),
46 53
       Xapian::Query.new(Xapian::Query::OP_AND, ["Xfoo-10"])
47 54
     )
48 55
     parser = Xapit::AbstractQueryParser.new(XapitMember, :conditions => { :foo => [2..5, 10] })

0 notes on commit f26d10b

Please sign in to comment.
Something went wrong with that request. Please try again.