Оптимизация индексирования Shpinx для LiveStreet, исправляем древнюю проблему с закрытыми блогами
Суть проблемы — LS ищет и находит сфинксом заметки и комментарии из всех блогов, независимо от того имеете вы к ним доступ или нет.
Чинится это просто:
Расписываю по шагам (LiveStreet 1.0.2, Sphinx 2.0.6 ).
I. Вносим некоторые изменения в конфигурацию сфинкса. Берём конфигурацию от сюда livestreet.ru/blog/dev_documentation/13482.html (к слову, конфигурация там существенно лучше стандартной, настоятельно рекомендую) и правим в ней entityprefixTopicsSource и entityprefixCommentsSource. Они должны стать такими:
II. Создаем Mapper для модуля Sphinx /classes/modules/sphinx/mapper/Sphinx.mapper.class.php следующего содержания:
Замечание (26.09.2013): В mapper добавлена поддержка случая, когда пользователь является создателем, но не подписчиком закрытого блога.
III. Мелкие изменения функции PrepareResults в /classes/actions/ActionSearch.class.php
204 строка была такой:
Добавялем новую строку, сразу после строки 222 — первый параметр в вызове функции Sphinx_FindContent:
IV. Изменения класса /classes/modules/sphinx/Sphinx.class.php
Самое начало класса должно стать таким (декларация и инициализация мэппера):
V. Итого:
Господин ort , может воткнёте это на GitHub? Вместе с улучшенной конфигурацией Sphinx? А то нытьё про плохой стандартный поиск слегка достало.
Чинится это просто:
- вносим в поисковый индекс id закрытого блога к которому относится топик или комментарий, либо 0 если этот блог не закрытый.
- передаём в поиск id текущего пользователя
- находим список закрытых блогов в которые имеет доступ текущий пользователь.
- используем этот список расширенный нулём (что добавит все не закрытые блоги в результаты поиска для любого пользователя) со стандартной возможностью фильтрации Сфинкса
Расписываю по шагам (LiveStreet 1.0.2, Sphinx 2.0.6 ).
I. Вносим некоторые изменения в конфигурацию сфинкса. Берём конфигурацию от сюда livestreet.ru/blog/dev_documentation/13482.html (к слову, конфигурация там существенно лучше стандартной, настоятельно рекомендую) и правим в ней entityprefixTopicsSource и entityprefixCommentsSource. Они должны стать такими:
source entityprefixTopicsSource : entityprefixSource { sql_query = \ SELECT t.topic_id, t.topic_title, UNIX_TIMESTAMP(t.topic_date_add) as topic_date_add, \ tc.topic_text, t.topic_publish, b.blog_title, u.user_login, \ IF( b.blog_type = 'close', b.blog_id,0 ) as blog_id \ FROM x50_topic t, x50_topic_content tc, x50_blog b, x50_user u \ WHERE t.topic_id=tc.topic_id AND t.topic_publish=1 \ AND b.blog_id=t.blog_id AND t.user_id=u.user_id \ AND t.topic_id>=$start AND t.topic_id<=$end sql_joined_field = tags from query; select topic_id, topic_tag_text \ from x50_topic_tag order by topic_id ASC sql_query_range = SELECT MIN(topic_id),MAX(topic_id) FROM x50_topic sql_range_step = 1000 sql_attr_uint = blog_id sql_attr_bool = topic_publish sql_attr_timestamp = topic_date_add sql_attr_multi = uint tag from query; SELECT topic_id, topic_tag_id FROM x50_topic_tag } source entityprefixCommentsSource : entityprefixSource { sql_query = \ SELECT m.comment_id, m.comment_text, UNIX_TIMESTAMP(m.comment_date) as comment_date, \ m.comment_delete, u.user_login, \ IF( b.blog_type = 'close', b.blog_id, 0 ) as blog_id \ FROM x50_comment m, x50_user u, x50_topic t, x50_blog b \ WHERE m.target_type='topic' AND m.comment_delete=0 AND m.comment_publish=1 AND m.user_id=u.user_id \ AND m.target_id = t.topic_id AND b.blog_id = t.blog_id \ AND m.comment_id>=$start AND m.comment_id<=$end sql_range_step = 5000 sql_attr_uint = blog_id sql_query_range = SELECT MIN(comment_id),MAX(comment_id) FROM x50_comment sql_attr_bool = comment_delete sql_attr_timestamp = comment_date }
II. Создаем Mapper для модуля Sphinx /classes/modules/sphinx/mapper/Sphinx.mapper.class.php следующего содержания:
<?php class ModuleSphinx_MapperSphinx extends Mapper { public function GetUsersCloseBlogs($sUserId) { $sql = "SELECT b.blog_id FROM ".Config::Get('db.table.blog')." as b, ".Config::Get('db.table.blog_user')." as bu WHERE bu.user_id = ?d AND bu.blog_id = b.blog_id AND b.blog_type='close' AND bu.user_role = 1 UNION DISTINCT SELECT blog_id FROM ".Config::Get('db.table.blog')." WHERE blog_type='close' AND user_owner_id = ?d"; $aBlogIDs=array(); if ($aRows=$this->oDb->select($sql,$sUserId,$sUserId)) { foreach ($aRows as $aBlogID) { $aBlogIDs[]=$aBlogID['blog_id']; } } return $aBlogIDs; } } ?>
Замечание (26.09.2013): В mapper добавлена поддержка случая, когда пользователь является создателем, но не подписчиком закрытого блога.
III. Мелкие изменения функции PrepareResults в /classes/actions/ActionSearch.class.php
204 строка была такой:
$aRes['aCounts'][$sType] = intval($this->Sphinx_GetNumResultsByType($aReq['q'], $sType, $aExtra));Стала такой:
$aRes['aCounts'][$sType] = intval($this->Sphinx_GetNumResultsByType($this->User_GetUserCurrent()?$this->User_GetUserCurrent()->getId():null,$aReq['q'], $sType, $aExtra));
Добавялем новую строку, сразу после строки 222 — первый параметр в вызове функции Sphinx_FindContent:
$this->User_GetUserCurrent()?$this->User_GetUserCurrent()->getId():null,
IV. Изменения класса /classes/modules/sphinx/Sphinx.class.php
Самое начало класса должно стать таким (декларация и инициализация мэппера):
class ModuleSphinx extends Module { /** * Объект сфинкса * * @var SphinxClient|null */ protected $oSphinx = null; protected $oMapper; /** * Инициализация * */ public function Init() { $this->oMapper=Engine::GetMapper(__CLASS__); $this->InitSphinx(); }Функция GetNumResultsByType меняется так(новый, первый параметр):
public function GetNumResultsByType($iUserId,$sTerms, $sObjType = 'topics', $aExtraFilters){ $aResults = $this->FindContent($iUserId,$sTerms, $sObjType, 1, 1, $aExtraFilters); return $aResults['total_found']; }Самое начало функции FindContent, всё, до первого if меняем на вот это (новый, первый параметр; получение списка нужных блогов; расширение ключа кеша):
public function FindContent($iUserId, $sTerms, $sObjType, $iOffset, $iLimit, $aExtraFilters){ /** * Получаем id закрытых блогов пользователя */ $aCloseBlogs=array(0); if (isset($iUserId ) ) { $blogsCacheKey = Config::Get('module.search.entity_prefix')."userCloseBlogs_{$iUserId}"; if (false === ($blogsData = $this->Cache_Get($blogsCacheKey ) ) ) { $blogsData = $this->oMapper->GetUsersCloseBlogs($iUserId); $this->Cache_Set($blogsData, $blogsCacheKey, array(), 60*15); } $aCloseBlogs = array_merge($aCloseBlogs,$blogsData); } $sCloseBlogs = serialize($aCloseBlogs); /** * используем кеширование при поиске */ $sExtraFilters = serialize($aExtraFilters); $cacheKey = Config::Get('module.search.entity_prefix')."searchResult_{$sObjType}_{$sTerms}_{$iOffset}_{$iLimit}_{$sExtraFilters}_{$sCloseBlogs}";И внутри этого if, сразу перед комментарием «Ищем» добавляем:
/** * Фильтруем закрытые блоги */ $this->oSphinx->SetFilter('blog_id', $aCloseBlogs);
V. Итого:
- Два незначительно изменённых SQL запроса, и две новых декларации в конфигурации Sphinx
- Новый, примитивный Mapper менее, чем в 20 строк
- Два мелких изменения в экшене
- 15 добавленных, или слегка модифицированных строк в самом поиске, и даже с поддержкой кэша.
Господин ort , может воткнёте это на GitHub? Вместе с улучшенной конфигурацией Sphinx? А то нытьё про плохой стандартный поиск слегка достало.
17 комментариев
Главное что вам не тычут в морду своей плашкой или лучше б тыкали?