Почти любому Веб-приложению необходима логика поиска данных, и зачастую это должен быть полнотекстовый поиск. Если вы используете базу данных MySQL, можно воспользоваться поиском FULLTEXT, но это не самое эффективное решение, особенно если объем данных велик. В этом случае используются сторонние поисковые движки, и один из них (и, на мой взгляд, самый эффективный из них) – это Sphinx. В данной заметке я представлю свой порт клиентской библиотеки Sphinx на Ruby и покажу, как его использовать.
Для начала, что такое Sphinx вообще? Sphinx – это полнотекстовый поисковый движок, которые предоставляет функции быстрого, эффективного и релевантного полнотекстового поиска другим приложениям. Sphinx был разработан специально для лучшей интеграции с базами данных SQL и скриптовыми языками. На сегодняшний момент встроенные источники данных поддерживают выборку либо напрямую из MySQL, либо через канал XML.
Текущий дистрибутив Sphinx включает следующие части:
- indexer: утилита для создания полнотекстовых индексов;
- search: простая (тестовая) утилита для запросов к полнотекстовым индексам из командной строки;
- searchd: демон для поиска в полнотекстовых индексах из стороннего программного обеспечения (например, Веб-скриптов);
- sphinxapi: набор библиотек API для популярных скриптовых языков для Веб (в данный момент только PHP);
Я не буду рассказывать, как установить этот движок. Если Вы впервые слышите о нем, посмотрите официальную документацию (но если Вы хотите получить эту информацию от меня, всегда можно попросить в комментариях, и я расскажу об установке в одной из последующих заметок). Вместо этого, представлю свой порт клиентской библиотеки Sphinx на Ruby и покажу, как ее использовать (обратите внимание, что Вам необходим Sphinx 0.9.7-RC2).
Для начала скачайте плагин с RubyForge, или с этого сайта:
Скачать Sphinx-0.2.0.zip
Это плагин Ruby on Rails, потому распакуйте его в каталог <app>/vendor/plugins (библиотека может использоваться и вне контекста Rails-приложения). Теперь Вы можете написать что-то вроде этого в Вашем коде:
1 2 3 4 5 6 7 8 9 10 11 | sphinx = Sphinx.new sphinx.set_match_mode(Sphinx::SPH_MATCH_ANY) result = sphinx.query('term1 term2') # Получить соответствующие объекты модели ids = result[:matches].map { |id, value| id }.join(',') posts = Post.find :all, :conditions => "id IN (#{ids})" # Получить выдержки docs = posts.map { |post| post.body } excerpts = sphinx.build_excerpts(docs, 'index', 'term1 term2') |
Довольно просто, не правда ли? Существует несколько опций, которые Вы можете использовать для получения более релевантных результатов поиска:
- set_limits(offset, limit) – индекс первого документа и количество документов для выборки.
- set_match_mode(mode) – режим поиска (может быть SPH_MATCH_ALL – поиск по всем словам, SPH_MATCH_ANY – поиск по любому из слов, SPH_MATCH_PHRASE – поиск по точной фразе, SPH_MATCH_BOOLEAN – поиск по логическому выражению).
- set_sort_mode(mode) – режим сортировки (can be SPH_SORT_RELEVANCE – сортировать по релевантности документа по убыванию, затем по дате, SPH_SORT_ATTR_DESC – сортировать по дате документа по убыванию, затем по релевантности по убыванию, SPH_SORT_ATTR_ASC – сортировать документы по дате по возрастанию, затем по релевантности по убыванию, SPH_SORT_TIME_SEGMENTS – сортировать по сегментам времени (час/день/неделя/что-то еще) по убыванию, затем по релевантности по убыванию).
Другие опции можно найти в документации API.
Если Вас заинтересовала эта библиотека, если Вы нашли ошибки или знаете, как ее можно улучшить – пожалуйста, отпишитесь в комментариях.
Обновление: К сожалению, нет скомпилированной версии последнего Sphinx 0.9.7-rc2 для Windows. Я собрал его, и добавил в архив рабочий файл конфигурации. Вы можете забрать сборку здесь.

[...] ActiveSearch Spinx indexed_search_engine SearchGenerator 一整個generator 另外,也可以做成Ajax real-time [...]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
===================================================================
--- vendor/plugins/sphinx/lib/client.rb (revision 5885)
+++ vendor/plugins/sphinx/lib/client.rb (working copy)
@@ -391,18 +391,20 @@
count = response[p, 4].unpack('N*').first; p += 4
# read matches
- result['matches'] = {}
+ result['matches'] = []
while count > 0 and p < max
count -= 1
doc, weight = response[p, 8].unpack('N*N*'); p += 8
- result['matches'][doc] ||= {}
- result['matches'][doc]['weight'] = weight
+ doc_data = {}
+ doc_data['weight'] = weight
attrs_names_in_order.each do |attr|
val = response[p, 4].unpack('N*').first; p += 4
- result['matches'][doc]['attrs'] ||= {}
- result['matches'][doc]['attrs'][attr] = val
+ doc_data['attrs'] ||= {}
+ doc_data['attrs'][attr] = val
end
+
+ result['matches'] << [doc, doc_data]
end
result['total'], result['total_found'], msecs, words = response[p, 16].unpack('N*N*N*N*'); p += 16
result['time'] = '%.3f' % (msecs / 1000.0)
[...] to embed into Ruboss (Heroku?) – Create Flex MXML templates for a Blog, Comments, Search (with Sphinx) Forum, Profile, Wiki, PhotoGallery, and Podcast Viewer. Bookmark It [...]
Привет, Всем!
У меня появился вопрос по Sphinx, помогите пожалуйста найти решение.
У меня есть следующая структура в конфигурационном файле:
sphinx.conf:
2
3
4
5
6
7
8
source sphinx_users_delta : sphinx_users_main
source sphinx_spaces_main
source sphinx_spaces_delta : sphinx_spaces_main
index users_main
index users_delta : users_main
index spaces_main
index spaces_delta : spaces_main
Такая структура была придумана мной для того, чтоб можно было при поиске получать ID по отдельной таблицы(указав по какому индексу с конфигурационного файла производить поиск).
Все, вроде как, корректно работает:
search -a test
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
Copyright (c) 2001-2008, Andrew Aksyonoff
using config file '/usr/local/etc/sphinx.conf'...
index 'users_main': query 'test ': returned 14 matches of 14 total in 0.000 sec
displaying matches:
1. document=3592, weight=2
2. document=4178, weight=2
3. document=4179, weight=2
4. document=4181, weight=2
5. document=6192, weight=2
6. document=2807, weight=1
7. document=3593, weight=1
8. document=4717, weight=1
9. document=4740, weight=1
10. document=6090, weight=1
11. document=6196, weight=1
12. document=6218, weight=1
13. document=6219, weight=1
14. document=6220, weight=1
words:
1. 'test': 14 documents, 19 hits
index 'users_delta': query 'test ': returned 0 matches of 0 total in 0.000 sec
words:
1. 'test': 0 documents, 0 hits
index 'spaces_main': query 'test ': returned 17 matches of 17 total in 0.000 sec
displaying matches:
1. document=937, weight=1
2. document=940, weight=1
3. document=942, weight=1
4. document=943, weight=1
5. document=944, weight=1
6. document=945, weight=1
7. document=964, weight=1
8. document=983, weight=1
9. document=984, weight=1
10. document=985, weight=1
11. document=986, weight=1
12. document=987, weight=1
13. document=988, weight=1
14. document=989, weight=1
15. document=990, weight=1
16. document=991, weight=1
17. document=992, weight=1
words:
1. 'test': 17 documents, 17 hits
index 'spaces_delta': query 'test ': returned 0 matches of 0 total in 0.000 sec
words:
1. 'test': 0 documents, 0 hits
Но вот не могу понять, как с помощью Sphinx организовать поиск по указанному мной индексу, как например я это делаю с консоли:
search -i spaces_main -a test
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Copyright (c) 2001-2008, Andrew Aksyonoff
using config file '/usr/local/etc/sphinx.conf'...
index 'spaces_main': query 'test ': returned 17 matches of 17 total in 0.000 sec
displaying matches:
1. document=937, weight=1
2. document=940, weight=1
3. document=942, weight=1
4. document=943, weight=1
5. document=944, weight=1
6. document=945, weight=1
7. document=964, weight=1
8. document=983, weight=1
9. document=984, weight=1
10. document=985, weight=1
11. document=986, weight=1
12. document=987, weight=1
13. document=988, weight=1
14. document=989, weight=1
15. document=990, weight=1
16. document=991, weight=1
17. document=992, weight=1
words:
1. 'test': 17 documents, 17 hits
Подскажите мне пожалуйста, как это можно организовать?
Спасибо
Второй параметр метода Query – название индекса, по которому искать:
Спасибо большое за ответ.
Подскажите пожалуйста, как я могу в Sphinx изменить шаблон, по которому мне возвращается результат запроса?
Например в результате запроса: sphinx.Query(‘test’)
я хотел бы, чтоб в результате я мог бы получить кроме всего прочего: test16, test_12, hello@test.com.
Спасибо
Привет, всем!!!
Подскажите пожалуйста, как в sphinx реализовать такой же поиск, какой бы например был бы при ‘…LIKE %name%…’
Спасибо