Здравствуй, читатель!
Некоторое время назад мне была поставлена задача внедрения MediaWiki в корпоративной сети.
И главной проблемой этого внедрения стал поиск информации, содержащейся в вики.
В этой статье я хотел бы рассказать о том, как подружить поиск Sphinx с MediaWiki.
Причина по которой я хотел бы это написать — отсутствие русскоязычной документации и более-менее приличного руководства или описания, которое помогало бы моим коллегам быстро и просто начать использовать этот прекрасный поисковый механизм.
Возможно, я просто не умею пользоваться гуглом…
Зачем это нужно
Цель данного внедрения в нашей организации — перенос корпоративной базы знаний в более удобный формат представления и исправления\добавления.
К слову сказать — компания наша реализует проекты по автоматизации документооборота в комплексе.
Заказчики крупные, решения сложные и порой нестандартные.
И статьи в вики предполагается иметь не только о проектах, но и о технических решениях, фичах и т.п., инновационных методах и технологиях.
А также в планах использовать ее как источник информации для новых сотрудников- им предстоит изучить достаточно приличный объем информации и удобство доступа к ней в настоящий момент оставляет желать лучшего.
Как было сказано выше — за основу был взят популярный движок MediaWiki. А ключевой проблемой, которую я предсказывал еще в самом начале, стала проблема поиска информации.
Всем известно — стандартный поиск совсем плох. И закономерным стал вопрос — как исправить это недоразумение.
Подготовка
Итак, все развернуто на Windows Server 2012 R2 64bit, естественно поднят IIS:
Самые последние версии на момент установки. Расширение SphinxSearch на скрине уже подключено. Как это сделать я напишу чуть ниже.
Необходимо скачать сам поисковый движок с официального сайта. Я выбрал 2.1.9-release (July 2014).
Также необходимо скачать расширение для MediaWiki.
Его я брал на GIT WikiMedia
Версия 0.9.0 была актуальной.
Установка и настройка поискового движка Sphinx
После скачивания движка я распаковал его в C:\inetpub\wwwroot\mw\sphinx).
Следующий шаг — подготовка конфига. В качестве основы я взял файл sphinx.conf.in
У меня получился вот такой рабочий, который я и привожу тут с комментариями.
# data source definition for the main index
source src_wiki_main
{
type = mysql
# data source
sql_host= 127.0.0.1 # localhost не работает в силу специфики Win7+ ветки
sql_user= mwuser
sql_pass=
sql_db=
sql_port= 3306# optional, default is 3306
# pre-query, executed before the main fetch query. Дабы понималась кодировка в базе
sql_query_pre= SET NAMES utf8
# main document fetch query - change the table names if you are using a prefix
# Этот и последующий запросы предоставлены самим разработчиком расширения.
sql_query= SELECT page_id, page_title, page_namespace, page_is_redirect, old_id, old_text FROM page, revision, text WHERE rev_id=page_latest AND old_id=rev_text_id
# attribute columns
sql_attr_uint= page_namespace
sql_attr_uint= page_is_redirect
sql_attr_uint= old_id
# collect all category ids for category filtering
sql_attr_multi = uint category from query; SELECT cl_from, page_id AS category FROM categorylinks, page WHERE page_title=cl_to AND page_namespace=14
# used by command-line search utility to display document information
sql_query_info= SELECT page_title, page_namespace FROM page WHERE page_id=$id
}
# data source definition for the incremental index
source src_wiki_incremental : src_wiki_main
{
# adjust this query based on the time you run the full index
# in this case, full index runs at 7 AM UTC
sql_query= SELECT page_id, page_title, page_namespace, page_is_redirect, old_id, old_text FROM page, revision, text WHERE rev_id=page_latest AND old_id=rev_text_id AND page_touched>=DATE_FORMAT(CURDATE(), '%Y%m%d070000')
# Тип поиска должен быть plain
type = plain
}
# main index definition
index wiki_main
{
type = plain
# which document source to index
source= src_wiki_main
# this is path and index file name without extension
# you may need to change this path or create this folder
path= C:/inetpub/wwwroot/mw/sphinx/data/wiki_main
# docinfo (ie. per-document attribute values) storage strategy
docinfo= extern
# morphology
morphology= stem_en, stem_ru
# stopwords file
#stopwords= /var/data/sphinx/stopwords.txt
# minimum word length
min_word_len= 1
# allow wildcard (*) searches
min_infix_len = 1
enable_star = 1
# charset encoding type
charset_type= utf-8
# charset definition and case folding rules "table"
# Это позволяет включать поиск по русскоязычным источникам. По умолчанию он не работает без этой магии.
charset_table= 0..9, A..Z->a..z, a..z, \
U+C0->a, U+C1->a, U+C2->a, U+C3->a, U+C4->a, U+C5->a, U+C6->a, \
U+C7->c,U+E7->c, U+C8->e, U+C9->e, U+CA->e, U+CB->e, U+CC->i, \
U+CD->i, U+CE->i, U+CF->i, U+D0->d, U+D1->n, U+D2->o, U+D3->o, \
U+D4->o, U+D5->o, U+D6->o, U+D8->o, U+D9->u, U+DA->u, U+DB->u, \
U+DC->u, U+DD->y, U+DE->t, U+DF->s, \
U+E0->a, U+E1->a, U+E2->a, U+E3->a, U+E4->a, U+E5->a, U+E6->a, \
U+E7->c,U+E7->c, U+E8->e, U+E9->e, U+EA->e, U+EB->e, U+EC->i, \
U+ED->i, U+EE->i, U+EF->i, U+F0->d, U+F1->n, U+F2->o, U+F3->o, \
U+F4->o, U+F5->o, U+F6->o, U+F8->o, U+F9->u, U+FA->u, U+FB->u, \
U+FC->u, U+FD->y, U+FE->t, U+FF->s, U+410..U+42F->U+430..U+44F, \
U+430..U+44F, U+0400->U+0435, U+0401->U+0435, U+0402->U+0452, \
U+0452, U+0403->U+0433, U+0404->U+0454, U+0454, U+0405->U+0455, \
U+0455, U+0406->U+0456, U+0407->U+0456, U+0457->U+0456, U+0456, \
U+0408..U+040B->U+0458..U+045B, U+0458..U+045B, U+040C->U+043A, \
U+040D->U+0438, U+040E->U+0443, U+040F->U+045F, U+045F, \
U+0450->U+0435, U+0451->U+0435, U+0453->U+0433, U+045C->U+043A, \
U+045D->U+0438, U+045E->U+0443, U+0460->U+0461, U+0461, U+0462->U+0463, \
U+0463, U+0464->U+0465, U+0465, U+0466->U+0467, U+0467, U+0468->U+0469, \
U+0469, U+046A->U+046B, U+046B, U+046C->U+046D, U+046D, U+046E->U+046F, \
U+046F, U+0470->U+0471, U+0471, U+0472->U+0473, U+0473, U+0474->U+0475, \
U+0476->U+0475, U+0477->U+0475, U+0475, U+0478->U+0479, U+0479, \
U+047A->U+047B, U+047B, U+047C->U+047D, U+047D, U+047E->U+047F, U+047F, \
U+0480->U+0481, U+0481, U+048A->U+0438, U+048B->U+0438, U+048C->U+044C, \
U+048D->U+044C, U+048E->U+0440, U+048F->U+0440, U+0490->U+0433, \
U+0491->U+0433, U+0490->U+0433, U+0491->U+0433, U+0492->U+0433, \
U+0493->U+0433, U+0494->U+0433, U+0495->U+0433, U+0496->U+0436, \
U+0497->U+0436, U+0498->U+0437, U+0499->U+0437, U+049A->U+043A, \
U+049B->U+043A, U+049C->U+043A, U+049D->U+043A, U+049E->U+043A, \
U+049F->U+043A, U+04A0->U+043A, U+04A1->U+043A, U+04A2->U+043D, \
U+04A3->U+043D, U+04A4->U+043D, U+04A5->U+043D, U+04A6->U+043F, \
U+04A7->U+043F, U+04A8->U+04A9, U+04A9, U+04AA->U+0441, U+04AB->U+0441, \
U+04AC->U+0442, U+04AD->U+0442, U+04AE->U+0443, U+04AF->U+0443, U+04B0->U+0443, \
U+04B1->U+0443, U+04B2->U+0445, U+04B3->U+0445, U+04B4->U+04B5, U+04B5, \
U+04B6->U+0447, U+04B7->U+0447, U+04B8->U+0447, U+04B9->U+0447, U+04BA->U+04BB, \
U+04BB, U+04BC->U+04BD, U+04BE->U+04BD, U+04BF->U+04BD, U+04BD, U+04C0->U+04CF, \
U+04CF, U+04C1->U+0436, U+04C2->U+0436, U+04C3->U+043A, U+04C4->U+043A, \
U+04C5->U+043B, U+04C6->U+043B, U+04C7->U+043D, U+04C8->U+043D, U+04C9->U+043D, \
U+04CA->U+043D, U+04CB->U+0447, U+04CC->U+0447, U+04CD->U+043C, U+04CE->U+043C, \
U+04D0->U+0430, U+04D1->U+0430, U+04D2->U+0430, U+04D3->U+0430, U+04D4->U+00E6, \
U+04D5->U+00E6, U+04D6->U+0435, U+04D7->U+0435, U+04D8->U+04D9, U+04DA->U+04D9, \
U+04DB->U+04D9, U+04D9, U+04DC->U+0436, U+04DD->U+0436, U+04DE->U+0437, \
U+04DF->U+0437, U+04E0->U+04E1, U+04E1, U+04E2->U+0438, U+04E3->U+0438, \
U+04E4->U+0438, U+04E5->U+0438, U+04E6->U+043E, U+04E7->U+043E, U+04E8->U+043E, \
U+04E9->U+043E, U+04EA->U+043E, U+04EB->U+043E, U+04EC->U+044D, U+04ED->U+044D, \
U+04EE->U+0443, U+04EF->U+0443, U+04F0->U+0443, U+04F1->U+0443, U+04F2->U+0443, \
U+04F3->U+0443, U+04F4->U+0447, U+04F5->U+0447, U+04F6->U+0433, U+04F7->U+0433, \
U+04F8->U+044B, U+04F9->U+044B, U+04FA->U+0433, U+04FB->U+0433, U+04FC->U+0445, \
U+04FD->U+0445, U+04FE->U+0445, U+04FF->U+0445, U+0410..U+0418->U+0430..U+0438, \
U+0419->U+0438, U+0430..U+0438, U+041A..U+042F->U+043A..U+044F, U+043A..U+044F,
}
# incremental index definition
index wiki_incremental : wiki_main
{
type = plain
path= C:/inetpub/wwwroot/mw/sphinx/data/wiki_incremental
}
# indexer settings
indexer
{
# memory limit (default is 32M)
mem_limit= 64M
}
# searchd settings
searchd
{
# IP address and port on which search daemon will bind and accept
listen= 127.0.0.1:9312
# searchd run info is logged here - create or change the folder
log= C:/inetpub/wwwroot/mw/sphinx/log/searchd.log
# all the search queries are logged here
query_log= C:/inetpub/wwwroot/mw/sphinx/log/query.log
# client read timeout, seconds
read_timeout= 5
# maximum amount of children to fork
max_children= 30
# a file which will contain searchd process ID
pid_file= C:/inetpub/wwwroot/mw/sphinx/log/searchd.pid
# maximum amount of matches this daemon would ever retrieve
# from each index and serve to client
max_matches= 1000
workers = threads
}
# --eof--
На этом конфигурирование сфинкса закончено.
Установка службы поиска
Теперь установим нашу службу.
Для этого в командной строке пишем
C:/inetpub/wwwroot/mw/sphinx/bin/searchd --install --config C:/inetpub/wwwroot/mw/sphinx/bin/sphinx.conf --servicename SphinxSearch
Все должно пойти без ошибок и служба долна установиться и стать видимой через Администрирование — Службы под именем SphinxSearch.
Пока ее запускать не стоит т.к. данные еще не проиндексированы и при запуске службы получим ошибку.
Стоит отметить, что слэши используются именно такие /, а не такие \. В противном случае появится ошибка доступа к файлам логов и PID файлам процессов поискового движка.
Также обращаю внимание, что conf файл лежит в папке с бинарниками (bin), дабы при запусках через консоль не писать пути к конфигу.
Но при установке службы лучше написать по какому пути лежит конфиг.
Теперь в командной строке переходим в папку с бинарниками (bin) и пишем
indexer --all
Получаем результат вроде этого:
Sphinx 2.1.9-release (r4761)
Copyright (c) 2001-2014, Andrew Aksyonoff
Copyright (c) 2008-2014, Sphinx Technologies Inc (http://sphinxsearch.com)
using config file './sphinx.conf'...
indexing index 'wiki_main'...
collected 159 docs, 0.5 MB
collected 0 attr values
sorted 0.0 Mvalues, 100.0% done
sorted 1.6 Mhits, 100.0% done
total 159 docs, 494176 bytes
total 0.596 sec, 827807 bytes/sec, 266.34 docs/sec
indexing index 'wiki_incremental'...
collected 159 docs, 0.5 MB
collected 0 attr values
sorted 0.0 Mvalues, 100.0% done
sorted 1.6 Mhits, 100.0% done
total 159 docs, 494176 bytes
total 0.584 sec, 844808 bytes/sec, 271.81 docs/sec
total 4 reads, 0.005 sec, 2107.7 kb/call avg, 1.4 msec/call avg
total 38 writes, 0.022 sec, 479.7 kb/call avg, 0.5 msec/call avg
Все, индекс создан.
Проверка работы поискового движка
Как выяснилось выше — индекс создался. В командной строке мы все еще в папке с бинарниками. Теперь запускаем нашу службу SphinxSearch и в командной строке пишем что нибудь вроде:
search wiki
У меня получился вот такой результат:
Sphinx 2.1.9-release (r4761)
Copyright (c) 2001-2014, Andrew Aksyonoff
Copyright (c) 2008-2014, Sphinx Technologies Inc (http://sphinxsearch.com)
using config file './sphinx.conf'...
index 'wiki_main': query 'wiki ': returned 13 matches of 13 total in 0.004 sec
displaying matches:
1. document=76, weight=1719, page_namespace=0, page_is_redirect=0, old_id=929, c
ategory=()
page_title=???????_CompanyNameWiki
page_namespace=0
2. document=77, weight=1670, page_namespace=0, page_is_redirect=0, old_id=1136,
category=()
page_title=FAQ_CompanyNameWiki
page_namespace=0
3. document=79, weight=1670, page_namespace=0, page_is_redirect=0, old_id=864, c
ategory=()
page_title=CompanyNameWiki:_?????
page_namespace=0
4. document=81, weight=1670, page_namespace=12, page_is_redirect=0, old_id=939,
category=()
page_title=C???????_?????_??????
page_namespace=12
5. document=128, weight=1670, page_namespace=0, page_is_redirect=0, old_id=1075,
category=()
page_title=?????
page_namespace=0
6. document=1, weight=1648, page_namespace=0, page_is_redirect=0, old_id=1091, c
ategory=()
page_title=?????????_????????
page_namespace=0
7. document=4, weight=1648, page_namespace=0, page_is_redirect=0, old_id=10, cat
egory=()
page_title=?????????_????????
page_namespace=0
8. document=5, weight=1648, page_namespace=0, page_is_redirect=0, old_id=181, ca
tegory=()
page_title=?????????_?????????_????_(???????_??????)
page_namespace=0
9. document=2, weight=1608, page_namespace=8, page_is_redirect=0, old_id=1135, c
ategory=()
page_title=Sidebar
page_namespace=8
10. document=12, weight=1608, page_namespace=0, page_is_redirect=0, old_id=719,
category=()
page_title=?????????_CRM
page_namespace=0
11. document=71, weight=1608, page_namespace=0, page_is_redirect=0, old_id=701,
category=()
page_title=??????_???????
page_namespace=0
12. document=80, weight=1608, page_namespace=12, page_is_redirect=0, old_id=862,
category=()
page_title=?????????_CompanyNameWiki
page_namespace=12
13. document=129, weight=1608, page_namespace=0, page_is_redirect=0, old_id=1085
, category=()
page_title=????
page_namespace=0
words:
1. 'wiki': 13 documents, 37 hits
index 'wiki_incremental': query 'wiki ': returned 13 matches of 13 total in 0.00
0 sec
displaying matches:
1. document=76, weight=1719, page_namespace=0, page_is_redirect=0, old_id=929, c
ategory=()
page_title=???????_CompanyNameWiki
page_namespace=0
2. document=77, weight=1670, page_namespace=0, page_is_redirect=0, old_id=1136,
category=()
page_title=FAQ_CompanyNameWiki
page_namespace=0
3. document=79, weight=1670, page_namespace=0, page_is_redirect=0, old_id=864, c
ategory=()
page_title=CompanyNameWiki:_?????
page_namespace=0
4. document=81, weight=1670, page_namespace=12, page_is_redirect=0, old_id=939,
category=()
page_title=C???????_?????_??????
page_namespace=12
5. document=128, weight=1670, page_namespace=0, page_is_redirect=0, old_id=1075,
category=()
page_title=?????
page_namespace=0
6. document=1, weight=1648, page_namespace=0, page_is_redirect=0, old_id=1091, c
ategory=()
page_title=?????????_????????
page_namespace=0
7. document=4, weight=1648, page_namespace=0, page_is_redirect=0, old_id=10, cat
egory=()
page_title=?????????_????????
page_namespace=0
8. document=5, weight=1648, page_namespace=0, page_is_redirect=0, old_id=181, ca
tegory=()
page_title=?????????_?????????_????_(???????_??????)
page_namespace=0
9. document=2, weight=1608, page_namespace=8, page_is_redirect=0, old_id=1135, c
ategory=()
page_title=Sidebar
page_namespace=8
10. document=12, weight=1608, page_namespace=0, page_is_redirect=0, old_id=719,
category=()
page_title=?????????_CRM
page_namespace=0
11. document=71, weight=1608, page_namespace=0, page_is_redirect=0, old_id=701,
category=()
page_title=??????_???????
page_namespace=0
12. document=80, weight=1608, page_namespace=12, page_is_redirect=0, old_id=862,
category=()
page_title=?????????_CompanyNameWiki
page_namespace=12
13. document=129, weight=1608, page_namespace=0, page_is_redirect=0, old_id=1085
, category=()
page_title=????
page_namespace=0
words:
1. 'wiki': 13 documents, 37 hits
В связи с тем, что имеется разница в кодировках — получили "?????", а не русские буквы. НО главное- присутствует выдача. Значит поиск работает!
Вот собственно и все, мы установили sphinx, проиндексировали нашу базу данных и имеем работающий поисковый движок!
Автоматизация обновления индекса
Для полной работы поиска необходимо также обеспечить регулярное обновление индекса — ведь статьи добавляются и необходимо обеспечить их доступность в поисковой выдаче в том числе.
Для этого в планировщике заданий создадим задание с регулярностью запуска (у меня 5 минут) bat файла со следующим содержанием:
c:\inetpub\wwwroot\mw\sphinx\bin\indexer --all --config c:\inetpub\wwwroot\mw\sphinx\bin\sphinx.conf --rotate
Я сделал запуск задания от имени локального администратора. Предварительно необходимо ЯВНО приписать права на всю папку sphinx.
Подключение поиска Sphinx в Mediawiki
Теперь необходимо подключить поисковый движок к Mediawiki. Иначе последний ну никак не знает что искать надо не встроенным механизмом, а при помощи сфинкса.
Идем в файл LocalSettings.php (Лежит в папке с медиавики) и там добавляем:
#Sphinx search
$wgSearchType = 'SphinxMWSearch';
require_once "$IP/extensions/SphinxSearch/SphinxSearch.php";
$wgSphinxSearch_host = "127.0.0.1";
$wgSphinxSearch_port = 9312;
$wgSphinxSearch_matches = 50;
$wgEnableSphinxPrefixSearch = true;
$wgFooterIcons['poweredby']['sphinxsearch'] = array(
'src' => "$wgScriptPath/extensions/SphinxSearch/skins/images/Powered_by_sphinx.png",
'url' => 'http://www.mediawiki.org/wiki/Extension:SphinxSearch',
'alt' => 'Search Powered by Sphinx',
);
Создаем в папке extensions новую папку с именем SphinxSearch.
важное замечание оставил vedmaka:
Добавлю: после установки sphinx нужно пойти на http://sphinxsearch.com/downloads/archive/, скачать оттуда исходники соответствующей версии и из них закинуть файл sphinxapi.php в директорию с SphinxSearch расширением.
Сохраняем. Перезапускаем сайт через менеджер IIS. Проверяем поиск руками через веб-страницу Mediawiki. Все должно работать.
Выдача при наборе текста в поисковой строке.
И сама поисковая выдача.
Заключение
В итоге мы получили более качественный поиск по материалам в нашей вики.
В выдаче по умолчанию сортировка принимает значение SPH_SORT_RELEVANCE.
При желании ее можно изменить путем явного указания в файле
LocalSettings.php
через параметр$wgSphinxSearch_sortby
Подробнее о различных вариантах сортировки выдачи можно почитать в этом разделе документации.
В данной статье я использовал не только личные наработки, но и информацию, собранную в процессе реализации работы с данным механизмом поиска.
Я не рассматривал возможные ошибки, которые могут возникать в процессе т.к. посчитал правильным поделиться работающей конфигурацией, а также последовательностью действий, которые в итоге приводят к работе решения в целом. А ошибок этих было море- начиная от нехватки прав на файлы, «не тех» слешей и заканчивая неработоспособностью конфигурации Sphinx, поставляемой в комплекте с расширением.