ambitious-sphinx 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.
- data/LICENSE +18 -0
- data/Manifest +16 -0
- data/README +82 -0
- data/ambitious-sphinx.gemspec +60 -0
- data/config/ultrasphinx/default.base +77 -0
- data/config/ultrasphinx/development.conf +62 -0
- data/lib/ambition/adapters/ambitious_sphinx.rb +15 -0
- data/lib/ambition/adapters/ambitious_sphinx/base.rb +33 -0
- data/lib/ambition/adapters/ambitious_sphinx/query.rb +52 -0
- data/lib/ambition/adapters/ambitious_sphinx/select.rb +108 -0
- data/lib/ambition/adapters/ambitious_sphinx/slice.rb +13 -0
- data/lib/ambition/adapters/ambitious_sphinx/sort.rb +38 -0
- data/test/helper.rb +9 -0
- data/test/select_test.rb +80 -0
- data/test/slice_test.rb +12 -0
- data/test/sort_test.rb +26 -0
- metadata +87 -0
data/LICENSE
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Copyright (c) 2007 Josh Nichols and Dan Croak
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
7
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
8
|
+
subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
15
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
16
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
ambitious-sphinx.gemspec
|
2
|
+
config/ultrasphinx/default.base
|
3
|
+
config/ultrasphinx/development.conf
|
4
|
+
lib/ambition/adapters/ambitious_sphinx/base.rb
|
5
|
+
lib/ambition/adapters/ambitious_sphinx/query.rb
|
6
|
+
lib/ambition/adapters/ambitious_sphinx/select.rb
|
7
|
+
lib/ambition/adapters/ambitious_sphinx/slice.rb
|
8
|
+
lib/ambition/adapters/ambitious_sphinx/sort.rb
|
9
|
+
lib/ambition/adapters/ambitious_sphinx.rb
|
10
|
+
LICENSE
|
11
|
+
Manifest
|
12
|
+
README
|
13
|
+
test/helper.rb
|
14
|
+
test/select_test.rb
|
15
|
+
test/slice_test.rb
|
16
|
+
test/sort_test.rb
|
data/README
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
= An Ambitious Sphinx Adapter
|
2
|
+
|
3
|
+
I don't know about you, but I like me some sexy full-text searching.
|
4
|
+
|
5
|
+
== The basics
|
6
|
+
|
7
|
+
Want to find all meals that mention bacon?
|
8
|
+
|
9
|
+
Meal.select {'bacon'}
|
10
|
+
|
11
|
+
What about bacon bits and sour cream?
|
12
|
+
|
13
|
+
Meal.select {'bacon bits' && 'sour cream'}
|
14
|
+
|
15
|
+
Maybe with bacon in the name, or cheese in the recipe?
|
16
|
+
|
17
|
+
Meal.select {|m| m.name =~ 'bacon' || m.recipe =~ 'cheese'}
|
18
|
+
|
19
|
+
Cheese in the name, but not grilled?
|
20
|
+
|
21
|
+
Meal.select {|m| m.name =~ 'bacon' && m.name !~ 'grilled'}
|
22
|
+
|
23
|
+
== Pagination
|
24
|
+
|
25
|
+
You're going to want to use pagination. Ultrasphinx, the underlying library, only supports it, as in, you can't just get all the objects matching your query. You _have_ to use paging.
|
26
|
+
|
27
|
+
It's pretty simple:
|
28
|
+
|
29
|
+
Meal.select {'bacon'}.page(2)
|
30
|
+
Meal.select {'bacon'}.page(3)
|
31
|
+
|
32
|
+
== Big honking disclaimer
|
33
|
+
|
34
|
+
We're still learning a lot about how sphinx and ambition work, so things are likely to change a lot, and features are likely to be missing.
|
35
|
+
|
36
|
+
== Getting Started
|
37
|
+
|
38
|
+
|
39
|
+
=== Installing
|
40
|
+
|
41
|
+
sudo gem install ambitious-sphinx
|
42
|
+
|
43
|
+
=== Add it to your app
|
44
|
+
|
45
|
+
Require our files somewhere, like at the end of config/environment.rb, maybe create config/initializers/sphinx.rb
|
46
|
+
|
47
|
+
require 'ultrasphinx'
|
48
|
+
require 'ambition/adapters/ambitious_sphinx'
|
49
|
+
|
50
|
+
=== Sphinx and Ultrasphinx
|
51
|
+
|
52
|
+
You will also need to go through the motions of setting up ultrasphinx.
|
53
|
+
|
54
|
+
This includes:
|
55
|
+
|
56
|
+
* Configuring/installing sphinx
|
57
|
+
* Modifying your model to indicate what's to be indexed
|
58
|
+
* Bootstrapping ultrasphinx
|
59
|
+
|
60
|
+
All this is discussed in detail in ultrasphinx's README
|
61
|
+
|
62
|
+
== Playing with the code base
|
63
|
+
|
64
|
+
In addition to the other dependencies, you'll need to:
|
65
|
+
|
66
|
+
gem install echoe redgreen mocha test-spec
|
67
|
+
|
68
|
+
Run the tests with:
|
69
|
+
|
70
|
+
rake test
|
71
|
+
|
72
|
+
|
73
|
+
== More information on Sphinx:
|
74
|
+
|
75
|
+
-> http://www.sphinxsearch.com/
|
76
|
+
-> http://blog.evanweaver.com/articles/2007/07/09/ultrasphinx-searching-the-world-in-231-seconds/
|
77
|
+
-> http://blog.evanweaver.com/files/doc/fauna/ultrasphinx/files/README.html
|
78
|
+
|
79
|
+
== More information on Ambition:
|
80
|
+
|
81
|
+
-> http://ambition.rubyforge.org
|
82
|
+
-> http://groups.google.com/group/ambition-rb/
|
@@ -0,0 +1,60 @@
|
|
1
|
+
|
2
|
+
# Gem::Specification for Ambitious-sphinx-0.1.0
|
3
|
+
# Originally generated by Echoe
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = %q{ambitious-sphinx}
|
7
|
+
s.version = "0.1.0"
|
8
|
+
|
9
|
+
s.specification_version = 2 if s.respond_to? :specification_version=
|
10
|
+
|
11
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
+
s.authors = ["Josh Nichols"]
|
13
|
+
s.date = %q{2008-02-29}
|
14
|
+
s.description = %q{An ambitious adapter for sphinx}
|
15
|
+
s.email = %q{josh@technicalpickles.com}
|
16
|
+
s.files = ["ambitious-sphinx.gemspec", "config/ultrasphinx/default.base", "config/ultrasphinx/development.conf", "lib/ambition/adapters/ambitious_sphinx/base.rb", "lib/ambition/adapters/ambitious_sphinx/query.rb", "lib/ambition/adapters/ambitious_sphinx/select.rb", "lib/ambition/adapters/ambitious_sphinx/slice.rb", "lib/ambition/adapters/ambitious_sphinx/sort.rb", "lib/ambition/adapters/ambitious_sphinx.rb", "LICENSE", "Manifest", "README", "test/helper.rb", "test/select_test.rb", "test/slice_test.rb", "test/sort_test.rb"]
|
17
|
+
s.has_rdoc = true
|
18
|
+
s.homepage = %q{http://ambitioussphinx.rubyforge.org/}
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
s.rubyforge_project = %q{ambitioussphinx}
|
21
|
+
s.rubygems_version = %q{1.0.0}
|
22
|
+
s.summary = %q{An ambitious adapter for sphinx}
|
23
|
+
s.test_files = ["test/select_test.rb", "test/slice_test.rb", "test/sort_test.rb"]
|
24
|
+
|
25
|
+
s.add_dependency(%q<ultrasphinx>, [">= 1.7"])
|
26
|
+
s.add_dependency(%q<ambition>, [">= 0.5.0"])
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
# # Original Rakefile source (requires the Echoe gem):
|
31
|
+
#
|
32
|
+
# require 'rake'
|
33
|
+
#
|
34
|
+
# begin
|
35
|
+
# require 'rubygems'
|
36
|
+
# gem 'echoe', '>=2.7'
|
37
|
+
# ENV['RUBY_FLAGS'] = ""
|
38
|
+
# require 'echoe'
|
39
|
+
#
|
40
|
+
# Echoe.new('ambitious-sphinx') do |p|
|
41
|
+
# p.dependencies << 'ultrasphinx >=1.7'
|
42
|
+
# p.summary = 'An ambitious adapter for sphinx'
|
43
|
+
# p.author = 'Josh Nichols'
|
44
|
+
# p.email = 'josh@technicalpickles.com'
|
45
|
+
#
|
46
|
+
# p.project = 'ambitioussphinx'
|
47
|
+
# p.url = 'http://ambitioussphinx.rubyforge.org/'
|
48
|
+
# p.test_pattern = 'test/*_test.rb'
|
49
|
+
# p.version = '0.1.0'
|
50
|
+
# p.dependencies << 'ambition >=0.5.0'
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# rescue LoadError
|
54
|
+
# puts "Not doing any of the Echoe gemmy stuff, because you don't have the specified gem versions"
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# desc 'Install as a gem'
|
58
|
+
# task :install_gem do
|
59
|
+
# puts `rake manifest package && gem install pkg/ambitious-sphinx-#{Version}.gem`
|
60
|
+
# end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Sphinx/Ultrasphinx user-configurable options.
|
4
|
+
#
|
5
|
+
# Copy this file to RAILS_ROOT/config/ultrasphinx. You can use individual
|
6
|
+
# namespaces if you want (e.g. development.base, production.base,
|
7
|
+
# test.base).
|
8
|
+
#
|
9
|
+
# This file should not be handed directly to Sphinx. Use the rake task
|
10
|
+
#
|
11
|
+
# rake ultrasphinx::configure
|
12
|
+
#
|
13
|
+
# to generate a parallel default.conf file. This is the file that Sphinx itself will
|
14
|
+
# use. The Ultrasphinx rake tasks automatically pass the correct file to
|
15
|
+
# to Sphinx.
|
16
|
+
#
|
17
|
+
# It is safe to edit .base files by hand. It is not safe to edit the generated
|
18
|
+
# .conf files. Do not symlink the .conf file to the .base file! I don't know why
|
19
|
+
# people think they need to do that. It's wrong.
|
20
|
+
#
|
21
|
+
|
22
|
+
indexer
|
23
|
+
{
|
24
|
+
# Indexer running options
|
25
|
+
mem_limit = 256M
|
26
|
+
}
|
27
|
+
|
28
|
+
searchd
|
29
|
+
{
|
30
|
+
# Daemon options
|
31
|
+
# What interface the search daemon should listen on and where to store its logs
|
32
|
+
address = 0.0.0.0
|
33
|
+
port = 3312
|
34
|
+
seamless_rotate = 1
|
35
|
+
log = /tmp/sphinx/log/searchd.log
|
36
|
+
query_log = /tmp/sphinx/log/query.log
|
37
|
+
read_timeout = 5
|
38
|
+
max_children = 300
|
39
|
+
pid_file = /tmp/sphinx/log/searchd.pid
|
40
|
+
max_matches = 100000
|
41
|
+
}
|
42
|
+
|
43
|
+
client
|
44
|
+
{
|
45
|
+
# Client options
|
46
|
+
# Name of the Aspell dictionary (two letters max)
|
47
|
+
dictionary_name = ap
|
48
|
+
# How your application connects to the search daemon (not necessarily the same as above)
|
49
|
+
server_host = localhost
|
50
|
+
server_port = 3312
|
51
|
+
}
|
52
|
+
|
53
|
+
source
|
54
|
+
{
|
55
|
+
# Individual SQL source options
|
56
|
+
sql_range_step = 5000
|
57
|
+
strip_html = 0
|
58
|
+
index_html_attrs =
|
59
|
+
sql_query_post =
|
60
|
+
}
|
61
|
+
|
62
|
+
index
|
63
|
+
{
|
64
|
+
# Index building options
|
65
|
+
path = db/sphinx
|
66
|
+
docinfo = extern # just leave this alone
|
67
|
+
morphology = stem_en
|
68
|
+
stopwords = # /path/to/stopwords.txt
|
69
|
+
min_word_len = 1
|
70
|
+
|
71
|
+
# Enable these if you need wildcard searching. They will slow down indexing significantly.
|
72
|
+
# min_infix_len = 1
|
73
|
+
# enable_star = 1
|
74
|
+
|
75
|
+
charset_type = utf-8 # or sbcs (Single Byte Character Set)
|
76
|
+
charset_table = 0..9, A..Z->a..z, -, _, ., &, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F,U+C5->U+E5, U+E5, U+C4->U+E4, U+E4, U+D6->U+F6, U+F6, U+16B, U+0c1->a, U+0c4->a, U+0c9->e, U+0cd->i, U+0d3->o, U+0d4->o, U+0da->u, U+0dd->y, U+0e1->a, U+0e4->a, U+0e9->e, U+0ed->i, U+0f3->o, U+0f4->o, U+0fa->u, U+0fd->y, U+104->U+105, U+105, U+106->U+107, U+10c->c, U+10d->c, U+10e->d, U+10f->d, U+116->U+117, U+117, U+118->U+119, U+11a->e, U+11b->e, U+12E->U+12F, U+12F, U+139->l, U+13a->l, U+13d->l, U+13e->l, U+141->U+142, U+142, U+143->U+144, U+144,U+147->n, U+148->n, U+154->r, U+155->r, U+158->r, U+159->r, U+15A->U+15B, U+15B, U+160->s, U+160->U+161, U+161->s, U+164->t, U+165->t, U+16A->U+16B, U+16B, U+16e->u, U+16f->u, U+172->U+173, U+173, U+179->U+17A, U+17A, U+17B->U+17C, U+17C, U+17d->z, U+17e->z,
|
77
|
+
}
|
@@ -0,0 +1,62 @@
|
|
1
|
+
|
2
|
+
# Auto-generated at Tue Jan 15 21:29:08 -0500 2008.
|
3
|
+
# Hand modifications will be overwritten.
|
4
|
+
# /Users/nichoj/Projects/ambitious_sphinx/config/ultrasphinx/default.base
|
5
|
+
indexer {
|
6
|
+
mem_limit = 256M
|
7
|
+
}
|
8
|
+
searchd {
|
9
|
+
read_timeout = 5
|
10
|
+
max_children = 300
|
11
|
+
log = /tmp/sphinx/log/searchd.log
|
12
|
+
port = 3312
|
13
|
+
max_matches = 100000
|
14
|
+
query_log = /tmp/sphinx/log/query.log
|
15
|
+
seamless_rotate = 1
|
16
|
+
pid_file = /tmp/sphinx/log/searchd.pid
|
17
|
+
address = 0.0.0.0
|
18
|
+
}
|
19
|
+
|
20
|
+
# Source configuration
|
21
|
+
|
22
|
+
source tweets
|
23
|
+
{
|
24
|
+
strip_html = 0
|
25
|
+
sql_range_step = 5000
|
26
|
+
index_html_attrs =
|
27
|
+
sql_query_post =
|
28
|
+
|
29
|
+
type = mysql
|
30
|
+
sql_query_pre = SET SESSION group_concat_max_len = 65535
|
31
|
+
sql_query_pre = SET NAMES utf8
|
32
|
+
|
33
|
+
sql_db = ambitious_sphinx_development
|
34
|
+
sql_host = localhost
|
35
|
+
sql_pass =
|
36
|
+
sql_sock = /tmp/mysql.sock
|
37
|
+
sql_user = root
|
38
|
+
sql_query_range = SELECT MIN(id) , MAX(id) FROM tweets
|
39
|
+
sql_query = SELECT (tweets.id * 1 + 0) AS id, tweets.body AS body, 'Tweet' AS class, 0 AS class_id, UNIX_TIMESTAMP(tweets.created_at) AS created_at, tweets.user_name AS user_name FROM tweets WHERE tweets.id >= $start AND tweets.id <= $end GROUP BY tweets.id
|
40
|
+
|
41
|
+
sql_group_column = class_id
|
42
|
+
sql_date_column = created_at
|
43
|
+
sql_query_info = SELECT * FROM tweets WHERE tweets.id = (($id - 0) / 1)
|
44
|
+
}
|
45
|
+
|
46
|
+
|
47
|
+
# Index configuration
|
48
|
+
|
49
|
+
index complete
|
50
|
+
{
|
51
|
+
source = tweets
|
52
|
+
charset_type = utf-8
|
53
|
+
charset_table = 0..9, A..Z->a..z, -, _, ., &, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F,U+C5->U+E5, U+E5, U+C4->U+E4, U+E4, U+D6->U+F6, U+F6, U+16B, U+0c1->a, U+0c4->a, U+0c9->e, U+0cd->i, U+0d3->o, U+0d4->o, U+0da->u, U+0dd->y, U+0e1->a, U+0e4->a, U+0e9->e, U+0ed->i, U+0f3->o, U+0f4->o, U+0fa->u, U+0fd->y, U+104->U+105, U+105, U+106->U+107, U+10c->c, U+10d->c, U+10e->d, U+10f->d, U+116->U+117, U+117, U+118->U+119, U+11a->e, U+11b->e, U+12E->U+12F, U+12F, U+139->l, U+13a->l, U+13d->l, U+13e->l, U+141->U+142, U+142, U+143->U+144, U+144,U+147->n, U+148->n, U+154->r, U+155->r, U+158->r, U+159->r, U+15A->U+15B, U+15B, U+160->s, U+160->U+161, U+161->s, U+164->t, U+165->t, U+16A->U+16B, U+16B, U+16e->u, U+16f->u, U+172->U+173, U+173, U+179->U+17A, U+17A, U+17B->U+17C, U+17C, U+17d->z, U+17e->z,
|
54
|
+
min_word_len = 1
|
55
|
+
# enable_star = 1
|
56
|
+
stopwords =
|
57
|
+
path = db/sphinx//sphinx_index_complete
|
58
|
+
docinfo = extern
|
59
|
+
morphology = stem_en
|
60
|
+
# min_infix_len = 1
|
61
|
+
}
|
62
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'ambition'
|
2
|
+
|
3
|
+
# stub out rails stuff enough so that ultrasphinx will be happy
|
4
|
+
RAILS_ROOT = './' unless defined? RAILS_ROOT
|
5
|
+
RAILS_ENV = 'development' unless defined? RAILS_ENV
|
6
|
+
|
7
|
+
require 'active_record'
|
8
|
+
require 'ultrasphinx'
|
9
|
+
|
10
|
+
%w(base page query select sort slice).each do |f|
|
11
|
+
require "ambition/adapters/ambitious_sphinx/#{f}"
|
12
|
+
end
|
13
|
+
|
14
|
+
ActiveRecord::Base.extend Ambition::API
|
15
|
+
ActiveRecord::Base.ambition_adapter = Ambition::Adapters::AmbitiousSphinx
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Ambition # :nodoc:
|
2
|
+
module Adapters # :nodoc:
|
3
|
+
module AmbitiousSphinx # :nodoc:
|
4
|
+
# Helper for the other
|
5
|
+
class Base
|
6
|
+
# Does the string have an Ultrasphinx field?
|
7
|
+
def has_field? str
|
8
|
+
str =~ /:/
|
9
|
+
end
|
10
|
+
|
11
|
+
# Does the string have any Ultrasphinx operators?
|
12
|
+
def has_operator? str
|
13
|
+
str =~ /(AND|OR|NOT)/
|
14
|
+
end
|
15
|
+
|
16
|
+
# Should this string be quotified? It needs to happen if the string doesn't
|
17
|
+
# have an operator or a field.
|
18
|
+
def needs_quoting? str
|
19
|
+
not (has_operator?(str) or has_field?(str))
|
20
|
+
end
|
21
|
+
|
22
|
+
# Quote the string if it needs it.
|
23
|
+
def quotify str
|
24
|
+
if needs_quoting?(str)
|
25
|
+
%Q("#{str}")
|
26
|
+
else
|
27
|
+
str
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Ambition
|
2
|
+
module Adapters
|
3
|
+
module AmbitiousSphinx
|
4
|
+
# Responsible for taking the clauses that Ambition has generated, and ultimately doing a
|
5
|
+
# Ultrasphinx::Search based on them
|
6
|
+
class Query < Base
|
7
|
+
def kick
|
8
|
+
Ultrasphinx::Search.new(to_hash).results
|
9
|
+
end
|
10
|
+
|
11
|
+
# Some magic to add pagination. This gets called if you were to do:
|
12
|
+
#
|
13
|
+
# Meal.select {'bacon'}.page(5)
|
14
|
+
#
|
15
|
+
# When +page+ is invoked, it's actually being invoked on +Ambition::Context+.
|
16
|
+
# It has +method_missing+? voodoo which will try to pass the method onto
|
17
|
+
# the +Query+. That's this class.
|
18
|
+
def page(number)
|
19
|
+
stash[:page] = number
|
20
|
+
context
|
21
|
+
end
|
22
|
+
|
23
|
+
# Not entirely sure when this is used, so unimplemented so far.
|
24
|
+
def size
|
25
|
+
raise "Not implemented yet."
|
26
|
+
end
|
27
|
+
|
28
|
+
# Builds a hash of options for Ultrasphinx::Search based on the clauses Ambition has generated.
|
29
|
+
def to_hash
|
30
|
+
hash = {}
|
31
|
+
|
32
|
+
unless (query = clauses[:select]).blank?
|
33
|
+
query_s = query.join(' ').squeeze(' ').strip
|
34
|
+
hash[:query] = quotify(query_s)
|
35
|
+
end
|
36
|
+
|
37
|
+
unless (page = stash[:page]).blank?
|
38
|
+
hash[:page] = page
|
39
|
+
end
|
40
|
+
|
41
|
+
hash
|
42
|
+
end
|
43
|
+
|
44
|
+
# Prints out the query that would be executed.
|
45
|
+
def to_s
|
46
|
+
hash = to_hash
|
47
|
+
hash[:query]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Ambition
|
2
|
+
module Adapters
|
3
|
+
module AmbitiousSphinx
|
4
|
+
# Select is responsible for taking pure Ruby, and mangling it until it resembles
|
5
|
+
# the syntax that Ultrasphinx[http://blog.evanweaver.com/files/doc/fauna/ultrasphinx/files/README.html] uses.
|
6
|
+
class Select < Base
|
7
|
+
# Handles method calls, like
|
8
|
+
#
|
9
|
+
# u.name
|
10
|
+
def call(method)
|
11
|
+
"#{method.to_s}:"
|
12
|
+
end
|
13
|
+
|
14
|
+
# Should we be supporting chained calls like:
|
15
|
+
#
|
16
|
+
# u.name.downcase
|
17
|
+
#
|
18
|
+
# ?
|
19
|
+
#
|
20
|
+
# I don't think Sphinx would be able to handle this.
|
21
|
+
def chained_call(*methods)
|
22
|
+
raise "Not implemented yet."
|
23
|
+
end
|
24
|
+
|
25
|
+
# Handles generating an Ultrasphinx query for code like:
|
26
|
+
#
|
27
|
+
# 'foo' && 'bar'
|
28
|
+
def both(left, right)
|
29
|
+
"#{quotify left} AND #{quotify right}"
|
30
|
+
end
|
31
|
+
|
32
|
+
# Handles generating an Ultrasphinx query for code like:
|
33
|
+
#
|
34
|
+
# 'foo' || 'bar'
|
35
|
+
def either(left, right)
|
36
|
+
"#{quotify left} OR #{quotify right}"
|
37
|
+
end
|
38
|
+
|
39
|
+
# Sphinx doesn't support equality.
|
40
|
+
def ==(left, right)
|
41
|
+
raise "Not applicable to sphinx."
|
42
|
+
end
|
43
|
+
|
44
|
+
# Sphinx doesn't support inequality.
|
45
|
+
def not_equal(left, right)
|
46
|
+
raise "Not applicable to sphinx."
|
47
|
+
end
|
48
|
+
|
49
|
+
# Handles generating an Ultrasphinx query for code like:
|
50
|
+
#
|
51
|
+
# u.name =~ 'bob'
|
52
|
+
#
|
53
|
+
# Some cavaets:
|
54
|
+
# * left hand side _must_ be a field, like u.name
|
55
|
+
# * right hand side _must not_ be a regular expression. Pattern matching is generally not
|
56
|
+
# supported by full text search engines
|
57
|
+
def =~(left, right)
|
58
|
+
raise if right.is_a? Regexp
|
59
|
+
"#{left}#{quotify right}"
|
60
|
+
end
|
61
|
+
|
62
|
+
# Handles generating an Ultrasphinx query for code like:
|
63
|
+
#
|
64
|
+
# u.name !~ 'bob'
|
65
|
+
#
|
66
|
+
# Some cavaets:
|
67
|
+
# * left hand side _must_ be a field, like u.name
|
68
|
+
# * right hand side _must not_ be a regular expression. Pattern matching is generally not
|
69
|
+
# supported by full text search engines
|
70
|
+
def not_regexp(left, right)
|
71
|
+
# could be DRYer, but this is more readable than: "NOT #{self.=~(left,right)}"
|
72
|
+
raise if right.is_a? Regexp
|
73
|
+
"NOT #{left}#{quotify right}"
|
74
|
+
end
|
75
|
+
|
76
|
+
# Not supported by Sphinx. If you need this kind of comparison, you probably should be
|
77
|
+
# using ambitious-activerecord.
|
78
|
+
def <(left, right)
|
79
|
+
raise "Not applicable to sphinx."
|
80
|
+
end
|
81
|
+
|
82
|
+
# Not supported by Sphinx. If you need this kind of comparison, you probably should be
|
83
|
+
# using ambitious-activerecord.
|
84
|
+
def >(left, right)
|
85
|
+
raise "Not applicable to sphinx."
|
86
|
+
end
|
87
|
+
|
88
|
+
# Not supported by Sphinx. If you need this kind of comparison, you probably should be
|
89
|
+
# using ambitious-activerecord.
|
90
|
+
def >=(left, right)
|
91
|
+
raise "Not applicable to sphinx."
|
92
|
+
end
|
93
|
+
|
94
|
+
# Not supported by Sphinx. If you need this kind of comparison, you probably should be
|
95
|
+
# using ambitious-activerecord.
|
96
|
+
def <=(left, right)
|
97
|
+
raise "Not applicable to sphinx."
|
98
|
+
end
|
99
|
+
|
100
|
+
# Not supported by Sphinx. If you need this kind of comparison, you probably should be
|
101
|
+
# using ambitious-activerecord.
|
102
|
+
def include?(left, right)
|
103
|
+
raise "Not applicable to sphinx."
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Ambition
|
2
|
+
module Adapters
|
3
|
+
module AmbitiousSphinx
|
4
|
+
# Slice would normally handle slicing, but we don't support it yet.
|
5
|
+
class Slice < Base
|
6
|
+
# Not implemented
|
7
|
+
def slice(start, length)
|
8
|
+
raise "Not implemented."
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Ambition
|
2
|
+
module Adapters
|
3
|
+
module AmbitiousSphinx
|
4
|
+
# +Sort+ would normally handle sorting, but we don't support it yet.
|
5
|
+
class Sort < Base
|
6
|
+
# Not implemented
|
7
|
+
def sort_by(method)
|
8
|
+
raise "Not implemented."
|
9
|
+
end
|
10
|
+
|
11
|
+
# Not implemented
|
12
|
+
def reverse_sort_by(method)
|
13
|
+
raise "Not implemented."
|
14
|
+
end
|
15
|
+
|
16
|
+
# Not implemented
|
17
|
+
def chained_sort_by(receiver, method)
|
18
|
+
raise "Not implemented."
|
19
|
+
end
|
20
|
+
|
21
|
+
# Not implemented
|
22
|
+
def chained_reverse_sort_by(receiver, method)
|
23
|
+
raise "Not implemented."
|
24
|
+
end
|
25
|
+
|
26
|
+
# Not implemented
|
27
|
+
def to_proc(symbol)
|
28
|
+
raise "Not implemented."
|
29
|
+
end
|
30
|
+
|
31
|
+
# Not implemented
|
32
|
+
def rand
|
33
|
+
raise "Not implemented."
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/test/helper.rb
ADDED
data/test/select_test.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
context 'AmbitiousSphinx Adapter :: Select' do
|
4
|
+
|
5
|
+
specify 'Ruby attributes become Sphinx fields prefixed with @' do
|
6
|
+
query = User.select {|m| m.name}.to_hash[:query]
|
7
|
+
query.should == "name:"
|
8
|
+
end
|
9
|
+
|
10
|
+
specify 'Ruby string becomes Sphinx string' do
|
11
|
+
query = User.select {'jon'}.to_hash[:query]
|
12
|
+
query.should == %Q("jon")
|
13
|
+
end
|
14
|
+
|
15
|
+
specify 'Ruby string becomes Sphinx phrase search' do
|
16
|
+
query = User.select {'jon doe'}.to_hash[:query]
|
17
|
+
query.should == "\"jon doe\""
|
18
|
+
end
|
19
|
+
|
20
|
+
specify 'Ruby == should not be supported' do
|
21
|
+
should.raise do User.select {|m| m.name == 'jon'} end
|
22
|
+
end
|
23
|
+
|
24
|
+
specify 'Ruby != becomes Sphinx NOT operator' do
|
25
|
+
should.raise do User.select {|m| m.name != 'jon'} end
|
26
|
+
end
|
27
|
+
|
28
|
+
specify 'Ruby && becomes Sphinx AND operator' do
|
29
|
+
query = User.select {'jon' && 'blarg'}.to_hash[:query]
|
30
|
+
query.should == %Q("jon" AND "blarg")
|
31
|
+
end
|
32
|
+
|
33
|
+
specify 'Ruby || becomes Sphinx OR operator' do
|
34
|
+
query = User.select { 'jon' || 'chris'}.to_hash[:query]
|
35
|
+
query.should == %Q("jon" OR "chris")
|
36
|
+
end
|
37
|
+
|
38
|
+
specify 'Ruby =~ with string' do
|
39
|
+
query = User.select {|m| m.name =~ 'chris'}.to_hash[:query]
|
40
|
+
query.should == %Q(name:"chris")
|
41
|
+
end
|
42
|
+
|
43
|
+
specify 'Ruby =~ with string' do
|
44
|
+
query = User.select {|m| m.name =~ 'chris' && m.name =~ 'jon'}.to_hash[:query]
|
45
|
+
query.should == %Q(name:"chris" AND name:"jon")
|
46
|
+
end
|
47
|
+
|
48
|
+
specify 'Ruby =~ with Regexp' do
|
49
|
+
should.raise do User.select {|m| m.name =~ /chris/} end
|
50
|
+
end
|
51
|
+
|
52
|
+
specify 'Ruby !~ with string' do
|
53
|
+
query = User.select {|m| m.name =~ 'chris' && m.name !~ 'jon'}.to_hash[:query]
|
54
|
+
query.should == %Q(name:"chris" AND NOT name:"jon")
|
55
|
+
end
|
56
|
+
|
57
|
+
specify 'Ruby !~ with Regexp is not supported' do
|
58
|
+
should.raise do User.select {|m| m.name !~ /chris/} end
|
59
|
+
end
|
60
|
+
|
61
|
+
specify 'Ruby > should not be supported' do
|
62
|
+
should.raise do User.select {|m| m.age > 21} end
|
63
|
+
end
|
64
|
+
|
65
|
+
specify 'Ruby >= should not be supported' do
|
66
|
+
should.raise do User.select {|m| m.age >= 21} end
|
67
|
+
end
|
68
|
+
|
69
|
+
specify 'Ruby < should not be supported' do
|
70
|
+
should.raise do User.select {|m| m.age < 21} end
|
71
|
+
end
|
72
|
+
|
73
|
+
specify 'Ruby <= should not be supported' do
|
74
|
+
should.raise do User.select {|m| m.age <= 21} end
|
75
|
+
end
|
76
|
+
|
77
|
+
xspecify 'inspect' do
|
78
|
+
User.select { |u| u.name }.inspect.should.match %r(call #to_s or #to_hash)
|
79
|
+
end
|
80
|
+
end
|
data/test/slice_test.rb
ADDED
data/test/sort_test.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
context "AmbitiousSphinx Adapter :: Sort" do
|
4
|
+
setup do
|
5
|
+
@klass = User
|
6
|
+
@block = @klass.select { |m| m.name =~ 'jon' }
|
7
|
+
end
|
8
|
+
|
9
|
+
specify "order" do
|
10
|
+
should.raise do
|
11
|
+
@block.sort_by {|m| m.name}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
specify "reverse order with -" do
|
16
|
+
should.raise do
|
17
|
+
@block.sort_by { |m| -m.age }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
specify "random order" do
|
22
|
+
should.raise do
|
23
|
+
@block.sort_by { rand }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ambitious-sphinx
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Josh Nichols
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-02-29 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: ultrasphinx
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: "1.7"
|
23
|
+
version:
|
24
|
+
- !ruby/object:Gem::Dependency
|
25
|
+
name: ambition
|
26
|
+
version_requirement:
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: 0.5.0
|
32
|
+
version:
|
33
|
+
description: An ambitious adapter for sphinx
|
34
|
+
email: josh@technicalpickles.com
|
35
|
+
executables: []
|
36
|
+
|
37
|
+
extensions: []
|
38
|
+
|
39
|
+
extra_rdoc_files: []
|
40
|
+
|
41
|
+
files:
|
42
|
+
- ambitious-sphinx.gemspec
|
43
|
+
- config/ultrasphinx/default.base
|
44
|
+
- config/ultrasphinx/development.conf
|
45
|
+
- lib/ambition/adapters/ambitious_sphinx/base.rb
|
46
|
+
- lib/ambition/adapters/ambitious_sphinx/query.rb
|
47
|
+
- lib/ambition/adapters/ambitious_sphinx/select.rb
|
48
|
+
- lib/ambition/adapters/ambitious_sphinx/slice.rb
|
49
|
+
- lib/ambition/adapters/ambitious_sphinx/sort.rb
|
50
|
+
- lib/ambition/adapters/ambitious_sphinx.rb
|
51
|
+
- LICENSE
|
52
|
+
- Manifest
|
53
|
+
- README
|
54
|
+
- test/helper.rb
|
55
|
+
- test/select_test.rb
|
56
|
+
- test/slice_test.rb
|
57
|
+
- test/sort_test.rb
|
58
|
+
has_rdoc: true
|
59
|
+
homepage: http://ambitioussphinx.rubyforge.org/
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: "0"
|
70
|
+
version:
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: "0"
|
76
|
+
version:
|
77
|
+
requirements: []
|
78
|
+
|
79
|
+
rubyforge_project: ambitioussphinx
|
80
|
+
rubygems_version: 1.0.0
|
81
|
+
signing_key:
|
82
|
+
specification_version: 2
|
83
|
+
summary: An ambitious adapter for sphinx
|
84
|
+
test_files:
|
85
|
+
- test/select_test.rb
|
86
|
+
- test/slice_test.rb
|
87
|
+
- test/sort_test.rb
|