yoomee-mongosphinx 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,157 @@
1
+ = MongoSphinx
2
+
3
+ The MongoSphinx library implements an interface between MongoDBand Sphinx
4
+ supporting MongoMapper to automatically index objects in Sphinx. It tries to
5
+ act as transparent as possible: Just an additional method in MongoMapper
6
+ and some Sphinx configuration are needed to get going.
7
+
8
+
9
+ == Prerequisites
10
+
11
+ MongoSphinx needs gems MongoMapper and Riddle as well as a running Sphinx
12
+ and a MongoDB installation.
13
+
14
+ sudo gem install riddle
15
+ sudo gem install mongomapper
16
+ sudo gem install yoomee-mongosphinx
17
+
18
+ No additional configuraton is needed for interfacing with MongoDB: Setup is
19
+ done when MongoMapper is able to talk to the MongoDB server.
20
+
21
+ A proper "sphinx.conf" file and a script for retrieving index data have to
22
+ be provided for interfacing with Sphinx: Sorry, no UltraSphinx like
23
+ magic... :-) Depending on the amount of data, more than one index may be used
24
+ and indexes may be consolidated from time to time.
25
+
26
+ This is a sample configuration for a single "main" index:
27
+
28
+ searchd {
29
+ address = 0.0.0.0
30
+ port = 3312
31
+
32
+ log = ./sphinx/searchd.log
33
+ query_log = ./sphinx/query.log
34
+ pid_file = ./sphinx/searchd.pid
35
+ }
36
+
37
+ source mongoblog {
38
+ type = xmlpipe2
39
+
40
+ xmlpipe_command = "rake sphinx:genxml"
41
+ }
42
+
43
+ index mongoblog {
44
+ source = mongoblog
45
+
46
+ charset_type = utf-8
47
+ path = ./sphinx/sphinx_index_main
48
+ }
49
+
50
+ Notice the line "xmlpipe_command =". This is what the indexer runs to generate
51
+ its input. You can change this to whatever works best for you, but I set it up as
52
+ a rake task, with the following in `lib/tasks/sphinx.rake`.
53
+
54
+ See the Downloads for a more comprehensive list of rake tasks, a slight modification on those written be M E Patterson at http://blog.digimonkey.com/2010/01/mongosphinx-with-mongodb-and-mongomapper/
55
+
56
+ == Models
57
+
58
+ Use method <tt>fulltext_index</tt> to enable indexing of a model. The
59
+ default is to index all attributes but it is recommended to provide a list of
60
+ attribute keys.
61
+
62
+ A side effect of calling this method is, that MongoSphinx overrides the
63
+ default of letting MongoDB create new IDs: Sphinx only allows numeric IDs and
64
+ MongoSphinx forces new objects with the name of the class, a hyphen and an
65
+ integer as ID (e.g. <tt>Post-38497238</tt>). Again: Only these objects are
66
+ indexed due to internal restrictions of Sphinx.
67
+
68
+ Sample:
69
+
70
+ class Post
71
+ include MongoMapper::Document
72
+
73
+ key :title, String
74
+ key :body, String
75
+
76
+ fulltext_index :title, :body
77
+ end
78
+
79
+ Add options <tt>:server</tt> and <tt>:port</tt> to <tt>fulltext_index</tt> if
80
+ the Sphinx server to query is running on a different server (defaults to
81
+ "localhost" with port 3312).
82
+
83
+ Here is a full-featured sample setting additional options:
84
+
85
+ fulltext_index :title, :body, :server => 'my.other.server', :port => 3313
86
+
87
+ == Indexing
88
+
89
+ Automatically starting the reindexing of objects the moment new objects are
90
+ created can be implemented by adding a filter to the model class:
91
+
92
+ after_save :reindex
93
+ def reindex
94
+ `sudo indexer --all --rotate` # Configure sudo to allow this call...
95
+ end
96
+
97
+ This or a similar callback should be added to all models needing instant
98
+ indexing. If indexing is not that crucial or load is high, some additional
99
+ checks for the time of the last call should be added.
100
+
101
+ Keep in mind that reindexing is not incremental, and xml is generated to pass
102
+ data from mongo to sphinx. It's not a speedy operation on large datasets.
103
+
104
+
105
+ == Queries
106
+
107
+ An additional instance method <tt>by_fulltext_index</tt> is added for each
108
+ fulltext indexed model. This method takes a Sphinx query like
109
+ "foo @title bar", runs it within the context of the current class and returns
110
+ an Array of matching MongoDB documents.
111
+
112
+ Samples:
113
+
114
+ Post.by_fulltext_index('first')
115
+ => [...]
116
+
117
+ post = Post.by_fulltext_index('this is @title post').first
118
+ post.title
119
+ => "First Post"
120
+ post.class
121
+ => Post
122
+
123
+ Additional options <tt>:match_mode</tt>, <tt>:limit</tt> and
124
+ <tt>:max_matches</tt> can be provided to customize the behaviour of Riddle.
125
+ Option <tt>:raw</tt> can be set to <tt>true</tt> to do no lookup of the
126
+ document IDs but return the raw IDs instead.
127
+
128
+ Sample:
129
+
130
+ Post.by_fulltext_index('my post', :limit => 100)
131
+
132
+ == Copyright & License
133
+
134
+ Copyright (c) 2009 Burke Libbey, Ryan Neufeld
135
+
136
+ CouchSphinx Copyright (c) 2009 Holtzbrinck Digital GmbH, Jan Ulbrich
137
+
138
+ Permission is hereby granted, free of charge, to any person
139
+ obtaining a copy of this software and associated documentation
140
+ files (the "Software"), to deal in the Software without
141
+ restriction, including without limitation the rights to use,
142
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
143
+ copies of the Software, and to permit persons to whom the
144
+ Software is furnished to do so, subject to the following
145
+ conditions:
146
+
147
+ The above copyright notice and this permission notice shall be
148
+ included in all copies or substantial portions of the Software.
149
+
150
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
151
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
152
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
153
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
154
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
155
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
156
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
157
+ OTHER DEALINGS IN THE SOFTWARE.
data/lib/indexer.rb ADDED
@@ -0,0 +1,218 @@
1
+ # MongoSphinx, a full text indexing extension for MongoDB using
2
+ # Sphinx.
3
+ #
4
+ # This file contains the MongoSphinx::Indexer::XMLDocset and
5
+ # MongoSphinx::Indexer::XMLDoc classes.
6
+
7
+ # Namespace module for the MongoSphinx gem.
8
+
9
+ module MongoSphinx #:nodoc:
10
+
11
+ # Module Indexer contains classes for creating XML input documents for the
12
+ # indexer. Each Sphinx index consists of a single "sphinx:docset" with any
13
+ # number of "sphinx:document" tags.
14
+ #
15
+ # The XML source can be generated from an array of CouchRest objects or from
16
+ # an array of Hashes containing at least fields "classname" and "_id"
17
+ # as returned by MongoDB view "MongoSphinxIndex/couchrests_by_timestamp".
18
+ #
19
+ # Sample:
20
+ #
21
+ # rows = [{ 'name' => 'John', 'phone' => '199 43828',
22
+ # 'classname' => 'Address', '_id' => 'Address-234164'
23
+ # },
24
+ # { 'name' => 'Sue', 'mobile' => '828 19439',
25
+ # 'classname' => 'Address', '_id' => 'Address-422433'
26
+ # }
27
+ # ]
28
+ # puts MongoSphinx::Indexer::XMLDocset.new(rows).to_s
29
+ #
30
+ # <?xml version="1.0" encoding="utf-8"?>
31
+ # <sphinx:docset>
32
+ # <sphinx:schema>
33
+ # <sphinx:attr name="csphinx-class" type="multi"/>
34
+ # <sphinx:field name="classname"/>
35
+ # <sphinx:field name="name"/>
36
+ # <sphinx:field name="phone"/>
37
+ # <sphinx:field name="mobile"/>
38
+ # <sphinx:field name="created_at"/>
39
+ # </sphinx:schema>
40
+ # <sphinx:document id="234164">
41
+ # <csphinx-class>336,623,883,1140</csphinx-class>
42
+ # <classname>Address</classname>
43
+ # <name><![CDATA[[John]]></name>
44
+ # <phone><![CDATA[[199 422433]]></phone>
45
+ # <mobile><![CDATA[[]]></mobile>
46
+ # <created_at><![CDATA[[]]></created_at>
47
+ # </sphinx:document>
48
+ # <sphinx:document id="423423">
49
+ # <csphinx-class>336,623,883,1140</csphinx-class>
50
+ # <classname>Address</classname>
51
+ # <name><![CDATA[[Sue]]></name>
52
+ # <phone><![CDATA[[]]></phone>
53
+ # <mobile><![CDATA[[828 19439]]></mobile>
54
+ # <created_at><![CDATA[[]]></created_at>
55
+ # </sphinx:document>
56
+ # </sphinx:docset>"
57
+
58
+ module Indexer
59
+
60
+ # Class XMLDocset wraps the XML representation of a document to index. It
61
+ # contains a complete "sphinx:docset" including its schema definition.
62
+
63
+ class XMLDocset
64
+
65
+ # Objects contained in document set.
66
+
67
+ attr_reader :xml_docs
68
+
69
+ # XML generated for opening the document.
70
+
71
+ attr_reader :xml_header
72
+
73
+ # XML generated for closing the document.
74
+
75
+ attr_reader :xml_footer
76
+
77
+ # Creates a XMLDocset object from the provided data. It defines a
78
+ # superset of all fields of the classes to index objects for. The class
79
+ # names are collected from the provided objects as well.
80
+ #
81
+ # Parameters:
82
+ #
83
+ # [data] Array with objects of type CouchRest::Document or Hash to create XML for
84
+
85
+ def initialize(rows = [])
86
+ raise ArgumentError, 'Missing class names' if rows.nil?
87
+
88
+ xml = '<?xml version="1.0" encoding="utf-8"?>'
89
+
90
+ xml << '<sphinx:docset><sphinx:schema>'
91
+
92
+ @xml_docs = []
93
+ classes = []
94
+
95
+ rows.each do |row|
96
+ object = nil
97
+
98
+ if row.kind_of? MongoMapper::Document
99
+ object = row
100
+ elsif row.kind_of? Hash
101
+ row = row['value'] if row['classname'].nil?
102
+ if row and (class_name = row['classname'])
103
+ object = eval(class_name.to_s).new(row) rescue nil
104
+ end
105
+ end
106
+
107
+ if object and object.sphinx_id
108
+ classes << object.class if not classes.include? object.class
109
+ @xml_docs << XMLDoc.from_object(object)
110
+ end
111
+ end
112
+
113
+ field_names = classes.collect { |clas| clas.fulltext_keys rescue []
114
+ }.flatten.uniq
115
+
116
+ field_names.each do |key, value|
117
+ xml << "<sphinx:field name=\"#{key}\"/>"
118
+ end
119
+
120
+ xml << '<sphinx:field name="classname"/>'
121
+ xml << '<sphinx:attr name="csphinx-class" type="multi"/>'
122
+
123
+ xml << '</sphinx:schema>'
124
+
125
+ @xml_header = xml
126
+ @xml_footer = '</sphinx:docset>'
127
+ end
128
+
129
+ # Returns the encoded data as XML.
130
+
131
+ def to_xml
132
+ return to_s
133
+ end
134
+
135
+ # Returns the encoded data as XML.
136
+
137
+ def to_s
138
+ return self.xml_header + self.xml_docs.join + self.xml_footer
139
+ end
140
+ end
141
+
142
+ # Class XMLDoc wraps the XML representation of a single document to index
143
+ # and contains a complete "sphinx:document" tag.
144
+
145
+ class XMLDoc
146
+
147
+ # Returns the ID of the encoded data.
148
+
149
+ attr_reader :id
150
+
151
+ # Returns the class name of the encoded data.
152
+
153
+ attr_reader :class_name
154
+
155
+ # Returns the encoded data.
156
+
157
+ attr_reader :xml
158
+
159
+ # Creates a XMLDoc object from the provided CouchRest object.
160
+ #
161
+ # Parameters:
162
+ #
163
+ # [object] Object to index
164
+
165
+ def self.from_object(object)
166
+ raise ArgumentError, 'Missing object' if object.nil?
167
+ raise ArgumentError, 'No compatible ID' if (id = object.sphinx_id).nil?
168
+ fulltext_attrs = object.attributes.reject do |k, v|
169
+ not (object.fulltext_keys.include? k.intern)
170
+ end
171
+ return new(id, object.class.to_s, fulltext_attrs)
172
+ end
173
+
174
+ # Creates a XMLDoc object from the provided ID, class name and data.
175
+ #
176
+ # Parameters:
177
+ #
178
+ # [id] ID of the object to index
179
+ # [class_name] Name of the class
180
+ # [data] Hash with the properties to index
181
+
182
+ def initialize(id, class_name, properties)
183
+ raise ArgumentError, 'Missing id' if id.nil?
184
+ raise ArgumentError, 'Missing class_name' if class_name.nil?
185
+
186
+ xml = "<sphinx:document id=\"#{id}\">"
187
+
188
+ xml << '<csphinx-class>'
189
+ xml << MongoSphinx::MultiAttribute.encode(class_name)
190
+ xml << '</csphinx-class>'
191
+ xml << "<classname>#{class_name}</classname>"
192
+
193
+ properties.each do |key, value|
194
+ xml << "<#{key}><![CDATA[[#{value}]]></#{key}>"
195
+ end
196
+
197
+ xml << '</sphinx:document>'
198
+
199
+ @xml = xml
200
+
201
+ @id = id
202
+ @class_name = class_name
203
+ end
204
+
205
+ # Returns the encoded data as XML.
206
+
207
+ def to_xml
208
+ return to_s
209
+ end
210
+
211
+ # Returns the encoded data as XML.
212
+
213
+ def to_s
214
+ return self.xml
215
+ end
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,183 @@
1
+ # MongoSphinx, a full text indexing extension for MongoDB/MongoMapper using
2
+ # Sphinx.
3
+ #
4
+ # This file contains the MongoMapper::Mixins::Indexer module which in turn
5
+ # includes MongoMapper::Mixins::Indexer::ClassMethods.
6
+
7
+ module MongoMapper # :nodoc:
8
+ module Mixins # :nodoc:
9
+
10
+ # Mixin for MongoMapper adding indexing stuff. See class ClassMethods for
11
+ # details.
12
+
13
+ module Indexer #:nodoc:
14
+
15
+ # Bootstrap method to include patches with.
16
+ #
17
+ # Parameters:
18
+ #
19
+ # [base] Class to include class methods of module into
20
+
21
+ def self.included(base)
22
+ base.extend(ClassMethods)
23
+ end
24
+
25
+ # Patches to the MongoMapper Document module: Adds the
26
+ # "fulltext_index" method for enabling indexing and defining the fields
27
+ # to include as a domain specific extention. This method also assures
28
+ # the existence of a special design document used to generate indexes
29
+ # from.
30
+ #
31
+ # An additional save callback sets an ID like "Post-123123" (class name
32
+ # plus pure numeric ID compatible with Sphinx) for new objects).
33
+ #
34
+ # Last but not least method "by_fulltext_index" is defined allowing a
35
+
36
+ # full text search like "foo @title bar" within the context of the
37
+ # current class.
38
+ #
39
+ # Samples:
40
+ #
41
+ # class Post < MongoMapper::Document
42
+ # use_database SERVER.default_database
43
+ #
44
+ # property :title
45
+ # property :body
46
+ #
47
+ # fulltext_index :title, :body
48
+ # end
49
+ #
50
+ # Post.by_fulltext_index('first')
51
+ # => [...]
52
+ # post = Post.by_fulltext_index('this is @title post').first
53
+ # post.title
54
+ # => "First Post"
55
+ # post.class
56
+ # => Post
57
+
58
+ def save_callback()
59
+ object = self
60
+ if object.id.nil?
61
+ idsize = fulltext_opts[:idsize] || 32
62
+ limit = (1 << idsize) - 1
63
+
64
+ while true
65
+ id = rand(limit)
66
+ candidate = "#{self.class.to_s}-#{id}"
67
+
68
+ begin
69
+ object.class.find(candidate) # Resource not found exception if available
70
+ rescue MongoMapper::DocumentNotFound
71
+ object.id = candidate
72
+ break
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+
79
+
80
+ module ClassMethods
81
+
82
+ # Method for enabling fulltext indexing and for defining the fields to
83
+ # include.
84
+ #
85
+ # Parameters:
86
+ #
87
+ # [keys] Array of field keys to include plus options Hash
88
+ #
89
+ # Options:
90
+ #
91
+ # [:server] Server name (defaults to localhost)
92
+ # [:port] Server port (defaults to 3312)
93
+ # [:idsize] Number of bits for the ID to generate (defaults to 32)
94
+
95
+ def fulltext_index(*keys)
96
+ opts = keys.pop if keys.last.is_a?(Hash)
97
+ opts ||= {} # Handle some options: Future use... :-)
98
+
99
+ # Save the keys to index and the options for later use in callback.
100
+ # Helper method cattr_accessor is already bootstrapped by couchrest
101
+ # gem.
102
+
103
+ cattr_accessor :fulltext_keys
104
+ cattr_accessor :fulltext_opts
105
+
106
+ self.fulltext_keys = keys
107
+ self.fulltext_opts = opts
108
+
109
+ # Overwrite setting of new ID to do something compatible with
110
+ # Sphinx. If an ID already exists, we try to match it with our
111
+ # Schema and cowardly ignore if not.
112
+
113
+ before_save :save_callback
114
+
115
+ end
116
+
117
+ # Searches for an object of this model class (e.g. Post, Comment) and
118
+ # the requested query string. The query string may contain any query
119
+ # provided by Sphinx.
120
+ #
121
+ # Call MongoMapper::Document.by_fulltext_index() to query
122
+ # without reducing to a single class type.
123
+ #
124
+ # Parameters:
125
+ #
126
+ # [query] Query string like "foo @title bar"
127
+ # [options] Additional options to set
128
+ #
129
+ # Options:
130
+ #
131
+ # [:match_mode] Optional Riddle match mode (defaults to :extended)
132
+ # [:limit] Optional Riddle limit (Riddle default)
133
+ # [:max_matches] Optional Riddle max_matches (Riddle default)
134
+ # [:sort_by] Optional Riddle sort order (also sets sort_mode to :extended)
135
+ # [:raw] Flag to return only IDs and do not lookup objects (defaults to false)
136
+
137
+ def by_fulltext_index(query, options = {})
138
+ if self == Document
139
+ client = Riddle::Client.new
140
+ else
141
+ client = Riddle::Client.new(fulltext_opts[:server],
142
+ fulltext_opts[:port])
143
+ query = query + " @classname #{self}"
144
+ end
145
+
146
+ client.match_mode = options[:match_mode] || :extended
147
+
148
+ if (limit = options[:limit])
149
+ client.limit = limit
150
+ end
151
+
152
+ if (max_matches = options[:max_matches])
153
+ client.max_matches = matches
154
+ end
155
+
156
+ if (sort_by = options[:sort_by])
157
+ client.sort_mode = :extended
158
+ client.sort_by = sort_by
159
+ end
160
+
161
+ result = client.query(query)
162
+
163
+ #TODO
164
+ if result and result[:status] == 0 and !(matches = result[:matches]).empty?
165
+ classname = nil
166
+ ids = matches.collect do |row|
167
+ classname = MongoSphinx::MultiAttribute.decode(row[:attributes]['csphinx-class'])
168
+ (row[:doc].to_i) rescue nil
169
+ end.compact
170
+ return ids if options[:raw]
171
+ objects = []
172
+ ids.each do |id|
173
+ objects << self.find_by_sphinx_id(id)
174
+ end
175
+ return objects
176
+ else
177
+ return []
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,64 @@
1
+ # MongoSphinx, a full text indexing extension for MongoDB using
2
+ # Sphinx.
3
+ #
4
+ # This file contains the MongoMapper::Mixins::Properties module.
5
+
6
+ # Patches to the CouchRest library.
7
+
8
+ module MongoMapper # :nodoc:
9
+ module Mixins # :nodoc:
10
+
11
+ # Patches to the CouchRest Properties module: Adds the "attributes" method
12
+ # plus some fulltext relevant stuff.
13
+ #
14
+ # Samples:
15
+ #
16
+ # data = SERVER.default_database.view('CouchSphinxIndex/couchrests_by_timestamp')
17
+ # rows = data['rows']
18
+ # post = Post.new(rows.first)
19
+ #
20
+ # post.attributes
21
+ # => {:tags=>"one, two, three", :updated_at=>Tue Jun 09 14:45:00 +0200 2009,
22
+ # :author=>nil, :title=>"First Post",
23
+ # :created_at=>Tue Jun 09 14:45:00 +0200 2009,
24
+ # :body=>"This is the first post. This is the [...] first post. "}
25
+ #
26
+ # post.fulltext_attributes
27
+ # => {:title=>"First Post", :author=>nil,
28
+ # :created_at=>Tue Jun 09 14:45:00 +0200 2009
29
+ # :body=>"This is the first post. This is the [...] first post. "}
30
+ #
31
+ # post.sphinx_id
32
+ # => "921744775"
33
+ # post.id
34
+ # => "Post-921744775"
35
+
36
+ module Properties
37
+
38
+ # Returns a Hash of all attributes allowed to be indexed. As a side
39
+ # effect it sets the fulltext_keys variable if still blank or empty.
40
+
41
+ def fulltext_attributes
42
+ clas = self.class
43
+
44
+ if not clas.fulltext_keys or clas.fulltext_keys.empty?
45
+ clas.fulltext_keys = self.attributes.collect { |k,v| k.intern }
46
+ end
47
+
48
+ return self.attributes.reject do |k, v|
49
+ not (clas.fulltext_keys.include? k.intern)
50
+ end
51
+ end
52
+
53
+ # Returns the numeric part of the document ID (compatible to Sphinx).
54
+
55
+ def sphinx_id
56
+ if (match = self.id.match(/#{self.class}-([0-9]+)/))
57
+ return match[1]
58
+ else
59
+ return nil
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,63 @@
1
+ # MongoSphinx, a full text indexing extension for MongoDB using
2
+ # Sphinx.
3
+ #
4
+ # This file contains the MongoSphinx::MultiAttribute class.
5
+
6
+ # Namespace module for the MongoSphinx gem.
7
+
8
+ module MongoSphinx #:nodoc:
9
+
10
+ # Module MultiAttribute implements helpers to translate back and
11
+ # forth between Ruby Strings and an array of integers suitable for Sphinx
12
+ # attributes of type "multi".
13
+ #
14
+ # Background: Getting an ID as result for a query is OK, but for example to
15
+ # allow cast safety, we need an aditional attribute. Sphinx supports
16
+ # attributes which are returned together with the ID, but they behave a
17
+ # little different than expected: Instead we can use arrays of integers with
18
+ # ASCII character codes. These values are returned in ascending (!) order of
19
+ # value (yes, sounds funny but is reasonable from an internal view to
20
+ # Sphinx). So we mask each byte with 0x0100++ to keep the order...
21
+ #
22
+ # Sample:
23
+ #
24
+ # MongoSphinx::MultiAttribute.encode('Hello')
25
+ # => "328,613,876,1132,1391"
26
+ # MongoSphinx::MultiAttribute.decode('328,613,876,1132,1391')
27
+ # => "Hello"
28
+
29
+ module MultiAttribute
30
+
31
+ # Returns an numeric representation of a Ruby String suitable for "multi"
32
+ # attributes of Sphinx.
33
+ #
34
+ # Parameters:
35
+ #
36
+ # [str] String to translate
37
+
38
+ def self.encode(str)
39
+ offset = 0
40
+ if str.respond_to?"bytes"
41
+ bytes = str.bytes
42
+ else
43
+ bytes = []
44
+ str.each_byte{|x| bytes << x}
45
+ end
46
+ return bytes.collect { |c| (offset+= 0x0100) + c }.join(',')
47
+ end
48
+
49
+ # Returns the original MongoDB ID created from a Sphinx ID. Only works if
50
+ # the ID was created from a MongoDB ID before!
51
+ #
52
+ # Parameters:
53
+ #
54
+ # [multi] Sphinx "multi" attribute to translate back
55
+
56
+ def self.decode(multi)
57
+ offset = 0
58
+ multi = multi.split(',') if not multi.kind_of? Array
59
+
60
+ return multi.collect {|x| (x.to_i-(offset+=0x0100)).chr}.join
61
+ end
62
+ end
63
+ end
data/mongosphinx.rb ADDED
@@ -0,0 +1,40 @@
1
+ # MongoSphinx, a full text indexing extension for using
2
+ # Sphinx.
3
+ #
4
+ # This file contains the includes implementing this library. Have a look at
5
+ # the README.rdoc as a starting point.
6
+
7
+ begin
8
+ require 'rubygems'
9
+ rescue LoadError; end
10
+ require 'mongo_mapper'
11
+ require 'riddle'
12
+
13
+ module MongoSphinx
14
+ if (match = __FILE__.match(/mongosphinx-([0-9.-]*)/))
15
+ VERSION = match[1]
16
+ else
17
+ VERSION = 'unknown'
18
+ end
19
+ end
20
+
21
+ require 'lib/multi_attribute'
22
+ require 'lib/indexer'
23
+ require 'lib/mixins/indexer'
24
+ require 'lib/mixins/properties'
25
+
26
+
27
+ # Include the Indexer mixin from the original Document class of
28
+ # MongoMapper which adds a few methods and allows calling method indexed_with.
29
+
30
+ module MongoMapper # :nodoc:
31
+ module Document # :nodoc:
32
+ include MongoMapper::Mixins::Indexer
33
+ module InstanceMethods
34
+ include MongoMapper::Mixins::Properties
35
+ end
36
+ module ClassMethods
37
+ include MongoMapper::Mixins::Indexer::ClassMethods
38
+ end
39
+ end
40
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yoomee-mongosphinx
3
+ version: !ruby/object:Gem::Version
4
+ hash: 25
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 1
10
+ version: 0.1.1
11
+ platform: ruby
12
+ authors:
13
+ - Matt Atkins
14
+ - Burke Libbey
15
+ - Ryan Neufeld
16
+ - Joost Hietbrink
17
+ autorequire:
18
+ bindir: bin
19
+ cert_chain: []
20
+
21
+ date: 2010-08-13 00:00:00 +01:00
22
+ default_executable:
23
+ dependencies: []
24
+
25
+ description: MongoSphinx, a full text indexing extension for MongoDB using Sphinx, Yoomee fork from burke/master.
26
+ email:
27
+ - matt@yoomee.com
28
+ - burke@53cr.com
29
+ - ryan@53cr.com
30
+ - joost@joopp.com
31
+ executables: []
32
+
33
+ extensions: []
34
+
35
+ extra_rdoc_files:
36
+ - README.rdoc
37
+ files:
38
+ - README.rdoc
39
+ - mongosphinx.rb
40
+ - lib/multi_attribute.rb
41
+ - lib/mixins/properties.rb
42
+ - lib/mixins/indexer.rb
43
+ - lib/indexer.rb
44
+ has_rdoc: true
45
+ homepage: http://github.com/yoomee/mongosphinx
46
+ licenses: []
47
+
48
+ post_install_message:
49
+ rdoc_options:
50
+ - --exclude
51
+ - pkg
52
+ - --exclude
53
+ - tmp
54
+ - --all
55
+ - --title
56
+ - MongoSphinx
57
+ - --main
58
+ - README.rdoc
59
+ require_paths:
60
+ - .
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ hash: 3
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ hash: 3
76
+ segments:
77
+ - 0
78
+ version: "0"
79
+ requirements: []
80
+
81
+ rubyforge_project:
82
+ rubygems_version: 1.3.7
83
+ signing_key:
84
+ specification_version: 3
85
+ summary: A full text indexing extension for MongoDB using Sphinx.
86
+ test_files: []
87
+