Использование поискового движка Sphinx в Ruby on Rails

Posted by Dmytro Shteflyuk on under MySQL, Ruby & Rails · English (44,743 views)

Почти любому Веб-приложению необходима логика поиска данных, и зачастую это должен быть полнотекстовый поиск. Если вы используете базу данных 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. Я собрал его, и добавил в архив рабочий файл конфигурации. Вы можете забрать сборку здесь.

37 Responses to this entry

Subscribe to comments with RSS

said on Сентябрь 10, 2007 at 05:12 · Permalink
1
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
Index: vendor/plugins/sphinx/lib/client.rb
===================================================================
--- 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)
tolya @
said on Сентябрь 11, 2008 at 12:54 · Permalink

Привет, Всем!

У меня появился вопрос по Sphinx, помогите пожалуйста найти решение.

У меня есть следующая структура в конфигурационном файле:

sphinx.conf:

1
2
3
4
5
6
7
8
source sphinx_users_main
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

1
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
Sphinx 0.9.8-release (r1371)
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

1
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
Sphinx 0.9.8-release (r1371)
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

Подскажите мне пожалуйста, как это можно организовать?

Спасибо

said on Сентябрь 11, 2008 at 14:51 · Permalink

Второй параметр метода Query – название индекса, по которому искать:

1
sphinx.Query('test', 'spaces_main');
tolya @
said on Сентябрь 12, 2008 at 14:27 · Permalink

Спасибо большое за ответ.

Подскажите пожалуйста, как я могу в Sphinx изменить шаблон, по которому мне возвращается результат запроса?
Например в результате запроса: sphinx.Query(‘test’)
я хотел бы, чтоб в результате я мог бы получить кроме всего прочего: test16, test_12, hello@test.com.

Спасибо

Anatoliy @
said on Сентябрь 24, 2008 at 17:23 · Permalink

Привет, всем!!!

Подскажите пожалуйста, как в sphinx реализовать такой же поиск, какой бы например был бы при ‘…LIKE %name%…’

Спасибо

More comments: 1 2

Comments are closed

Comments for this entry are closed for a while. If you have anything to say – use a contact form. Thank you for your patience.