yoomee-mongosphinx 0.1.1

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/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
+