113
ПОИСК? Роман Семирук SPHINX!

Поиск? Sphinx!

  • View
    1.014

  • Download
    4

Embed Size (px)

DESCRIPTION

Sphinx считается одним из самых быстрых и гибких поисковых движков на рынке, но не является "коробочным" решением, чем отпугивает многих разработчиков. Я расскажу как быстро поднять полнотекстовый поиск для своего проекта на базе Sphinx, почему он крут и какие существуют интеграционные решения для Python.

Citation preview

Page 1: Поиск? Sphinx!

ПОИСК? Роман Семирук

SPHINX!

Page 2: Поиск? Sphinx!

НО СНАЧАЛА...

Page 3: Поиск? Sphinx!

Очень корпоративный сайт

Page 4: Поиск? Sphinx!

Блог с кучей статей

Page 5: Поиск? Sphinx!

БД вакансий

Page 6: Поиск? Sphinx!

ПОЛНОТЕКСТОВЫЙ ПОИСКключевое слово

Page 7: Поиск? Sphinx!

Все любят сниппеты

Page 8: Поиск? Sphinx!

СНИППЕТЫключевое слово

Page 9: Поиск? Sphinx!

Кластеризация, она же группировка

Page 10: Поиск? Sphinx!

КЛАСТЕРИЗАЦИЯключевое слово

Page 11: Поиск? Sphinx!

Более лучшие сложные фильтры...

Page 12: Поиск? Sphinx!

ФИЛЬТРАЦИЯключевое слово

Page 13: Поиск? Sphinx!

Двусторонняя сортировка

Page 14: Поиск? Sphinx!

СОРТИРОВКАключевое слово

Page 15: Поиск? Sphinx!

ПОВЫШАЕМ ГРАДУС ГИКОВОСТИ

Page 16: Поиск? Sphinx!

ДОКУМЕНТЫключевое слово

Page 17: Поиск? Sphinx!

АТРИБУТЫключевое слово

Page 18: Поиск? Sphinx!

ИНДЕКСключевое слово

Page 19: Поиск? Sphinx!

ЯЗЫК ЗАПРОСОВключевое слово

Page 20: Поиск? Sphinx!

МОРФОЛОГИЧЕСКИЙАНАЛИЗ

ключевое слово

Page 21: Поиск? Sphinx!

РЕЛЕВАНТНОСТЬключевое слово

Page 22: Поиск? Sphinx!

РАНКЕР / РАНЖИРОВАНИЕключевое слово

Page 23: Поиск? Sphinx!

НО И ЭТО НЕ ВСЁ

Page 24: Поиск? Sphinx!

МАСШТАБИРОВАНИЕключевое слово

Page 25: Поиск? Sphinx!

НЕПОЛНОТЕКСТОВЫЙПОИСК

ключевое слово

Page 26: Поиск? Sphinx!

И никто даже не догадывается...

Page 27: Поиск? Sphinx!

полнотекстовый поискфильтрациясортировкагруппировкасниппетыработа с существующими документамиширокий набор атрибутовбыстрая индексациягибкий язык запросовморфологический анализаторуправление релевантностьюмасштабируемостьскоростькачествостоимость

Обычные требования к поиску

Page 28: Поиск? Sphinx!
Page 29: Поиск? Sphinx!

РЕКЛАМНАЯ ПАУЗА

Page 30: Поиск? Sphinx!

SQL PHRASE INDEXоказывается, это...

Page 31: Поиск? Sphinx!

НАБОР СПЕЦИАЛИЗИРОВАННЫХ ПРИЛОЖЕНИЙ*

* indexer, searchd, search, spelldump, indextool, wordbreaker

Page 32: Поиск? Sphinx!

ШАРАGPL2

Page 33: Поиск? Sphinx!

БЫСТРО. ОЧЕНЬ.

Page 34: Поиск? Sphinx!

ИНДЕКСАЦИЯдо 10-15 МБ/сек на ядро*

* CPU решает

Page 35: Поиск? Sphinx!

ПОИСКдо 250 запросов в секунду

на каждое ядро с 1 000 000 документов *

* зависит от размера индекса

Page 36: Поиск? Sphinx!

MYSQLPOSTGRESQLMS SQLORACLEXML

ИСТОЧНИКИ

Page 37: Поиск? Sphinx!

NATIVE. FAST.

Page 38: Поиск? Sphinx!

ВЫСОКАЯ МАСШТАБИРУЕМОСТЬ*

* доказано британскими учёными

Page 39: Поиск? Sphinx!

CRAIGSLIST.ORG250 миллионов запросов в деньнесколько миллиардов документовкластер из 15 инстансов Sphinx

Page 40: Поиск? Sphinx!

LINUXBSDOS XWINDOWS

ПЛАТФОРМЫ

Page 41: Поиск? Sphinx!

АРХИТЕКТУРЫ x86x86_64SPARC64ARM

Page 42: Поиск? Sphinx!

API*

* PHP, Perl, Java, Python, Ruby и некоторые другие

Page 43: Поиск? Sphinx!

SQL-подобный язык запросовSphinxQL

Page 44: Поиск? Sphinx!

ПОЛИГЛОТрусский

françaisespañol

italiano

deutsch

svensk

suomalainen

english

čeština

العربية

Page 45: Поиск? Sphinx!

ГИБКИЕ НАСТРОЙКИтачка на прокачку

Page 46: Поиск? Sphinx!

РАНКЕРЫранжирование, релевантность и всё такое

Page 47: Поиск? Sphinx!

до 256 полей для индексации на один индексдо 32-х атрибутов различных типов

* хватит на все случаи жизни

Page 48: Поиск? Sphinx!

АТРИБУТЫ unsigned integersfloating point valuesboolstringsunix timestampMVA – multi-value attributesJSON (new)

Page 49: Поиск? Sphinx!

СНИППЕТЫ

Page 50: Поиск? Sphinx!

RTFM, please

Page 51: Поиск? Sphinx!

WORKFLOW

Page 52: Поиск? Sphinx!

searchd & indexerэто...

Page 53: Поиск? Sphinx!

SQLdatabase

index_file.spa index_file.sph index_file.spk index_file.sppindex_file.spdindex_file.spiindex_file.spm index_file.sps

searchdindexer

Page 54: Поиск? Sphinx!

searchd

index_file.spa index_file.sph index_file.spk index_file.sppindex_file.spdindex_file.spiindex_file.spm index_file.sps

indexerAPP

cron

SQLdatabase

Page 55: Поиск? Sphinx!

Disk или RT?

* мир перевернулся, индексы бывают разные

Page 56: Поиск? Sphinx!

DISK RT

Page 57: Поиск? Sphinx!

DISK RTpull push

Page 58: Поиск? Sphinx!

DISK RTpullэффективная структура

pushфрагментация

Page 59: Поиск? Sphinx!

DISK RTpullэффективная структурамонолит

pushфрагментацияобновление на лету

Page 60: Поиск? Sphinx!

DISK RTpullэффективная структурамонолитдельта-индексы

pushфрагментацияобновление на летучастые дампы

Page 61: Поиск? Sphinx!

DISK RTpullэффективная структурамонолитдельта-индексы

pushфрагментацияобновление на летучастые дампы

Page 62: Поиск? Sphinx!

ПРАКТИКУМ

Page 63: Поиск? Sphinx!

$ apt-get install sphinx

$ brew install sphinx

Ubuntu

OS X

$ ./configure --with-pgsql$ make$ make install

DIY

Page 64: Поиск? Sphinx!

sphinx.conf

Page 65: Поиск? Sphinx!

sphinx.conf | connection

source common{ type = pgsql sql_host = localhost sql_user = sphinx sql_pass = sphinx sql_db = megaportal}

Page 66: Поиск? Sphinx!

sphinx.conf | source description

source company: common{ sql_query =\ SELECT company.id, \ company.name, \ company.date_created \ FROM company

sql_field_string = name sql_attr_timestamp = date_created}

Page 67: Поиск? Sphinx!

sphinx.conf | source description

source company: common{ sql_query =\ SELECT company.id, \ company.name, \ company.date_created \ FROM company

sql_field_string = name sql_attr_timestamp = date_created}

выборка

атрибуты

Page 68: Поиск? Sphinx!

sphinx.conf | index description

index common{ type = plain morphology = stem_en, stem_ru min_word_len = 2 charset_type = utf-8 html_strip = 1 html_remove_elements = script html_index_attrs = img=alt,title; a=title;

min_stemming_len = 3 min_infix_len = 3 enable_star = 1 ...}

Page 69: Поиск? Sphinx!

sphinx.conf | index description

index common{ type = plain morphology = stem_en, stem_ru min_word_len = 2 charset_type = utf-8 html_strip = 1 html_remove_elements = script html_index_attrs = img=alt,title; a=title;

min_stemming_len = 3 min_infix_len = 3 enable_star = 1 ...}

«джентельменский набор»

десятки других опций

Page 70: Поиск? Sphinx!

sphinx.conf | index description

index company: common{ source = company path = /path/to/index_files/megaportal}

Page 71: Поиск? Sphinx!

sphinx.conf | indexer tuning

indexer{ mem_limit = 512M}

Page 72: Поиск? Sphinx!

sphinx.conf | indexer tuning

searchd{ listen = 9306:mysql41 log = /path/to/logs/searchd.log query_log = /path/to/logs/query.log pid_file = /path/to/pid/searchd.pid}

Page 73: Поиск? Sphinx!

sphinx.conf | fatality

source common{ type = pgsql sql_host = localhost sql_user = sphinx sql_pass = sphinx sql_db = megaportal}

source company: common{ sql_query =\ SELECT company.id, \ company.name, \ company.date_created \ FROM company

sql_field_string = name sql_attr_timestamp = date_created}

index common{ type = plain morphology = stem_en, stem_ru min_word_len = 2 charset_type = utf-8 html_strip = 1 html_remove_elements = script html_index_attrs = img=alt,title; a=title;

min_stemming_len = 3 min_infix_len = 3 enable_star = 1}

index company: common{ source = company path = /path/to/index_files/megaportal}

indexer{ mem_limit = 512M}

searchd{ listen = 9306:mysql41 log = /path/to/logs/searchd.log query_log = /path/to/logs/query.log pid_file = /path/to/pid/searchd.pid}

описаниедокументов

описаниеиндексов

indexer + searchd

Page 74: Поиск? Sphinx!

$ indexer -c /path/to/sphinx.conf --all

Page 75: Поиск? Sphinx!

Sphinx 2.0.6-release (r3473)Copyright (c) 2001-2012, Andrew AksyonoffCopyright (c) 2008-2012, Sphinx Technologies Inc(http://sphinxsearch.com)

using config file '/path/to/sphinx.conf'...indexing index 'common'...ERROR: index 'common': no valid sources configured; skipping.indexing index 'company'...collected 1066244 docs, 28.7 MBsorted 2.3 Mhits, 100.0% donetotal 1066244 docs, 28735925 bytestotal 11.452 sec, 2509034 bytes/sec, 93097.50 docs/sectotal 60 reads, 0.019 sec, 495.0 kb/call avg, 0.3 msec/call avgtotal 127 writes, 0.053 sec, 471.7 kb/call avg, 0.4 msec/call avgrotating indices: successfully sent SIGHUP to searchd (pid=56606).

Page 76: Поиск? Sphinx!

$ searchd -c /path/to/sphinx.conf

Page 77: Поиск? Sphinx!

SphinxQLAPI

Page 78: Поиск? Sphinx!

$ mysql -h 0 -P 9306Welcome to the MySQL monitor. Commands end with ; or \g.Your MySQL connection id is 1Server version: 2.0.6-release (r3473)

Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or itsaffiliates. Other names may be trademarks of their respectiveowners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

Page 79: Поиск? Sphinx!

mysql> select * from company where match('тнк');+--------+--------+--------------+----------+| id | status | date_created | owner_id |+--------+--------+--------------+----------+| 5015 | 6 | 2008 | 5019 || 25502 | 3 | 2009 | 25507 || 39771 | 6 | 2009 | 39776 || 152307 | 1 | 2010 | 152380 || 183905 | 3 | 2010 | 184097 || 194302 | 6 | 2010 | 194517 || 218982 | 1 | 2011 | 219439 || 235881 | 3 | 2011 | 236408 || 287319 | 3 | 2011 | 288131 || 338476 | 3 | 2011 | 339574 || 340073 | 6 | 2011 | 341177 || 471410 | 2 | 2012 | 473498 || 513023 | 0 | 2012 | 515276 || 768093 | 1 | 2012 | 770983 || 823359 | 6 | 2012 | 826706 || 915374 | 3 | 2013 | 919765 |+--------+--------+--------------+----------+16 rows in set (0.00 sec)

Page 80: Поиск? Sphinx!

mysql> select * from company where match('тнк') and date_created between 2010 and 2012;+--------+--------+--------------+----------+| id | status | date_created | owner_id |+--------+--------+--------------+----------+| 152307 | 1 | 2010 | 152380 || 183905 | 3 | 2010 | 184097 || 194302 | 6 | 2010 | 194517 || 218982 | 1 | 2011 | 219439 || 235881 | 3 | 2011 | 236408 || 287319 | 3 | 2011 | 288131 || 338476 | 3 | 2011 | 339574 || 340073 | 6 | 2011 | 341177 || 471410 | 2 | 2012 | 473498 || 513023 | 0 | 2012 | 515276 || 768093 | 1 | 2012 | 770983 || 823359 | 6 | 2012 | 826706 |+--------+--------+--------------+----------+12 rows in set (0.01 sec)

Page 81: Поиск? Sphinx!

mysql> SHOW META;+---------------+--------+| Variable_name | Value |+---------------+--------+| total | 6 || total_found | 6 || time | 0.000 || keyword[0] | тнк || docs[0] | 16 || hits[0] | 16 |+---------------+--------+6 rows in set (0.00 sec)

mysql> DESC company;+--------------+-----------+| Field | Type |+--------------+-----------+| id | integer || name | field || status | uint || date_created | timestamp || owner_id | uint |+--------------+-----------+5 rows in set (0.00 sec)

Page 82: Поиск? Sphinx!

REAL LIFE

Page 83: Поиск? Sphinx!

«Порционный» запрос

source company: common{ sql_query_range =\ SELECT MIN(id), MAX(id) from company sql_range_step = 10000

sql_query =\ SELECT company.id, \ company.name, \ company.date_created \ FROM company \ WHERE company.id BETWEEN $start AND $end

sql_field_string = name sql_attr_timestamp = date_created}

Page 84: Поиск? Sphinx!

Расширяем «охват» индекса

source company: common{ ... sql_query =\ SELECT company.id, \ company.name, \ company.date_created, \ "user".email as owner_email \ FROM company \ LEFT JOIN "user" \ ON company.owner_id = "user".id \ WHERE company.id BETWEEN $start AND $end

sql_field_string = name sql_field_string = owner_email sql_attr_timestamp = date_created}

Page 85: Поиск? Sphinx!

One-to-many? M2M? MVA!

source company: common{ ... sql_field_string = name sql_field_string = owner_email sql_attr_timestamp = date_created

sql_attr_multi =\ uint products from ranged-query; \ SELECT company_id, id FROM product \ WHERE id >= $start AND id <= $end; \ SELECT MIN(id), MAX(id) FROM product}

Page 86: Поиск? Sphinx!

mysql> select name, products from company where match('тнк') and products in (109503, 1123362); +------------------------------+----------+| name | products |+------------------------------+----------+| ТНК-Транс | 109503 || ООО «ТНК-Транс» | 1123362 |+------------------------------+----------+2 rows in set (0.00 sec)

Page 87: Поиск? Sphinx!

Δ-индексы

* такие же как обычные, только маленькие и другие

Page 88: Поиск? Sphinx!

Процесс индексации

пред-запросы пост-запросы

перестройка индекса

пост-обработка

выборка

1 2 3 54

* открытие-закрытие соединений не указаны

Page 89: Поиск? Sphinx!

source company: common{ sql_query_pre = SET @maxts:=(SELECT NOW())

sql_query = SELECT company.id, company.name FROM company \ WHERE company.created_at < @maxts ...

sql_query_post =\ REPLACE INTO search_deltacounters \ VALUES (@id, 'company_tmp', @maxts)

sql_query_post_index =\ DELETE FROM search_deltacounters \ WHERE tablename='company'

sql_query_post_index =\ UPDATE search_deltacounters \ SET tablename='company' WHERE tablename='company_tmp'}

Модифицируем основной индекс

Page 90: Поиск? Sphinx!

source company_delta: company{ sql_query_pre =\ SET @maxts=(SELECT maxts FROM search_deltacounters \ WHERE tablename='company')

sql_query = SELECT company.id, company.name FROM company \ WHERE company.created_at >= @maxts ...

sql_query_post = sql_query_post_index =}

Создаём дельту

index company_delta: common{ source = company_delta path = /path/to/indexes/megaportal}

Page 91: Поиск? Sphinx!

$ indexer -c /path/to/sphinx.conf company_delta --rotate

Page 92: Поиск? Sphinx!

mysql> select * from company, company_delta where match('тнк');+--------+--------+--------------+----------+| id | status | date_created | owner_id |+--------+--------+--------------+----------+| 5015 | 6 | 2008 | 5019 || 25502 | 3 | 2009 | 25507 || 39771 | 6 | 2009 | 39776 || 152307 | 1 | 2010 | 152380 || 183905 | 3 | 2010 | 184097 || 194302 | 6 | 2010 | 194517 || 218982 | 1 | 2011 | 219439 || 235881 | 3 | 2011 | 236408 || 287319 | 3 | 2011 | 288131 || 338476 | 3 | 2011 | 339574 || 340073 | 6 | 2011 | 341177 || 471410 | 2 | 2012 | 473498 || 513023 | 0 | 2012 | 515276 || 768093 | 1 | 2012 | 770983 || 823359 | 6 | 2012 | 826706 || 915374 | 3 | 2013 | 919765 |+--------+--------+--------------+----------+16 rows in set (0.00 sec)

Page 93: Поиск? Sphinx!

А питон?..

Page 94: Поиск? Sphinx!

:(

Page 96: Поиск? Sphinx!

НЕ ORM

https://github.com/semirook/sphinxit

Page 97: Поиск? Sphinx!

Лёгкийконструктор SphinxQL-запросов

https://github.com/semirook/sphinxit

Page 98: Поиск? Sphinx!

https://github.com/semirook/sphinxit

Oursql & MySQLdb

Page 99: Поиск? Sphinx!

Framework-free

https://github.com/semirook/sphinxit

Page 100: Поиск? Sphinx!

https://github.com/semirook/sphinxit

Python 2 & 3спасибо, six

Page 101: Поиск? Sphinx!

https://github.com/semirook/sphinxit

Тесты :)спасибо, тесты

Page 102: Поиск? Sphinx!

$ pip install sphinxit

Page 103: Поиск? Sphinx!

sphinxit | config

class SearchConfig(object): DEBUG = True WITH_META = True WITH_STATUS = True SEARCHD_CONNECTION = { 'host': '127.0.0.1', 'port': 9306, }

Page 104: Поиск? Sphinx!

sphinxit | usage

from sphinxit.core.processor import Search, Snippet

company_search = ( Search( indexes=['company'], config=SearchConfig ) .match('ТНК'))

SELECT * FROM company WHERE MATCH('ТНК')

Page 105: Поиск? Sphinx!

sphinxit | usage

search = Search(['company'], config=SearchConfig)search = ( search .match('ТНК') .select('id', 'name') .options( ranker='proximity', max_matches=100, ) .order_by('name', 'desc'))

SELECT id, name FROM company WHERE MATCH('ТНК')ORDER BY name DESC OPTION max_matches=100, ranker=proximity

Page 106: Поиск? Sphinx!

sphinxit | usage

search = Search(['company'], config=SearchConfig) search = ( search .match('ТНК') .filter(date_created__lte=datetime.date.today()))results = search.ask()

SELECT * FROM companyWHERE MATCH('ТНК') AND date_created<=1370552400

Page 107: Поиск? Sphinx!

sphinxit | result

{ u'result': [ { 'date_created': 2008L, 'owner_email': u'[email protected]', 'products': u'2,5', 'id': 5015L, 'name': u'\u0422\u041d\u041a', }, { 'date_created': 2009L, 'owner_email': u'[email protected]', 'products': u'2,5', 'id': 25502L, 'name': u'\u0422\u041d\u041a \u0418\u043d\u0442\u0435\u0440\u043', } ], ... u'meta': { u'total': u'16', u'total_found': u'16', u'docs[0]': u'16', u'time': u'0.000', u'hits[0]': u'16', u'keyword[0]': u'\u0442\u043d\u043a' }}

Page 108: Поиск? Sphinx!

sphinxit | update syntax

search = Search(['company'], config=SearchConfig)search = ( search .match('ТНК') .update(products=(5,2)) .filter(id__gt=1))

UPDATE company SET products=(5,2) WHERE MATCH('ТНК') AND id>1

Page 109: Поиск? Sphinx!

sphinxit | snippets syntax

snippets = ( Snippet(index='company', config=SearchConfig) .for_query("Me amore") .from_data("amore", "amore mia"))

CALL SNIPPETS ( ('amore', 'me amore'), 'company', 'Me amore');

{ u'result': [ {'snippet': u'<b>amore</b>'}, {'snippet': u'<b>amore</b> mia'} ]}

Page 110: Поиск? Sphinx!

sphinxit | snippets syntax

snippets = ( Snippet(index='company', config=SearchConfig) .for_query("Me amore") .from_data("amore mia") .options( before_match='<strong>', after_match='</strong>', ))

CALL SNIPPETS ( 'amore mia', 'company', 'Me amore', '<strong>' AS before_match, '</strong>' AS after_match)

Page 111: Поиск? Sphinx!

Pull requests, pleasehttps://github.com/semirook/sphinxit

Page 112: Поиск? Sphinx!

http://sphinxsearch.com/docs/

So, you want to know more?..

http://sphinxsearch.com/forum/