Разбор основан на утёкших исходниках поиска Яндекса, срез примерно 2022 года. Речь о двух стадиях: робот (краулинг и индексация) и search/ (рантайм формирования выдачи). Здесь нас интересует именно финальная стадия - то, что происходит между моментом, когда базовый поиск уже отобрал документы-кандидаты, и моментом, когда пользователь видит десять строк на странице. Имена компонентов, пути и пороги приведены как в источнике. Дисклеймер: за прошедшее время имена, константы и пороги могли смениться, веса любых факторов иллюстративны - реальные обучаемы и проприетарны.
Откуда берётся выдача: средний метапоиск и слияние
SERP не строится одним монолитом. Запрос проходит через метапоиск, который рассылает подзапросы источникам (шардам базового поиска) и собирает их ответы. Центральный класс - TMetaSearch.
Код: Выделить всё
TMetaSearch meta/metasearch.h:153 рассылка по источникам (scatter)
scatter runner meta/scatter/runner.h собственно scatter-gather
TMergeContext meta/merge.h:26 инкрементальное слияние
TIncrementalMerger meta/unionmerge.h слияние группировок на лету
Код: Выделить всё
TMetaGrouping - вся группировка целиком
TMetaGroup - одна группа = один сайт/домен
TMergedDoc - конкретный документ внутри группы
(meta/mergedres.h)
Host-collapsing: почему с домена пускают не больше двух
Самое практически значимое ограничение для SEO живёт в группировке. Конфигурация - в TGroupingInfo.
Код: Выделить всё
TGroupingInfo (grouping/groupinfo.h:26)
NumDocsInGroup - сколько документов одного домена
допускается (типично 2 в топе)
NumGroupsOnPage - сколько групп на странице
GetMaxDocsInGroup(groupIndex, mode) (groupinfo.h:78)
Тонкость в методе GetMaxDocsInGroup(groupIndex, mode). Для головных групп - тех, что стоят выше всего, - в режиме GM_UNION допускается чуть больше документов, чем для остальных. Иначе говоря, лидеру выдачи иногда разрешают занять не два, а несколько мест подряд, тогда как всем прочим доменам - строго лимит. Это не баг, а сознательная асимметрия: сильному релевантному источнику дают развернуться в топе, а длинный хвост схлопывают жёстко.
Поверх группировки работает дедупликация по URL:
Код: Выделить всё
TMergeGroupingUrlDuplicatesFilter
(web/util/grouping/grouping.h)
gKillDup - параметр агрессивности схлопывания
Blender: вставка вертикалей и состав страницыПрактический вывод первый: больше примерно двух URL с одного домена в топ не пускают. Это не штраф и не фильтр за переоптимизацию - это конструкция самой сборки выдачи. Группировка по домену включена по умолчанию, NumDocsInGroup типично равен 2, и обойти это контентом нельзя. Расчёт на захват топа десятью своими страницами по одному запросу несостоятелен: механически отрежет host-collapsing.
После того как органика сгруппирована, в дело вступает blender - компонент, который собирает итоговую страницу из разных вертикалей (интентов): веб-документы, картинки, видео, новости, колдунщики.
Код: Выделить всё
TBlender (web/blender/blender/blender.h:103)
CallRulesAfterBlenderFactors()
CallRulesAfterBlend()
SetDocPosition(url, pos, ..., TPlacement)
Решение о том, какие вертикали и куда вставлять, принимает отдельный резолвер:
Код: Выделить всё
TSerpResolver (web/blender/serp_resolver/serp_resolver.h)
режимы:
ALLOW_ALL - разрешить все интенты
ALLOW_ONLY - только перечисленные
ALLOW_EXCEPT - все, кроме перечисленных
EXCLUSIVE_INSERT_MODE - эксклюзивная вставка
Поскольку один и тот же сайт может попасть и в органику, и в картиночную или видеовертикаль, нужна сквозная дедупликация:
Код: Выделить всё
duplicate_cleaner
THostDuplicateChecker - дубли по хосту между вертикалями
TServiceDuplicateChecker - дубли по сервису
Факторы самого блендера считаются изолированно от веб-факторов:
Код: Выделить всё
web/rearrange/apply_blender/
blender_web_features/
blender_meta_features/
blender_dssm_features/
Размер выдачи: потолок в 259 документов
Самое жёсткое ограничение задаётся константами размера выдачи. Они подтверждены прямо в исходнике.
Код: Выделить всё
adjust_serp_size/smart_adjust_serp_size.h:6-8
DefaultPageSize = 10 - документов на странице
MaxLastPage = 24 - номер последней страницы
DefaultMaxDocs = 259 - максимум документов всего
Финальная сериализацияПрактический вывод второй: позиции за порогом примерно 259 документов недостижимы в принципе. Это не вопрос слабой релевантности - движок не материализует выдачу глубже этого предела. Любые рассуждения про "мы на 280-й позиции" бессмысленны: такой позиции не существует. Имеет смысл бороться только в пределах первых двух-двух с половиной сотен результатов, а реально - в пределах первых страниц, где работают host-collapsing и blender.
Собранная и обрезанная выдача уходит на формирование ответа:
Код: Выделить всё
TTmplDataReport (report/templates_json/response.h)
-> protobuf TReport / TGrouping / TGroup / TDocument
(idl/meta.proto:40)
-> JSON через TAppReportJsonPageCallback::Report()
Сводка для практики
- Выдача строится как группы по домену, а не как плоский список. NumDocsInGroup типично равен 2 - больше примерно двух URL с домена в топ не попадает, и контентом это не обходится.
- Головным группам в режиме GM_UNION иногда дают чуть больше мест - асимметрия в пользу сильного лидера, не в пользу длинного хвоста.
- gKillDup и TMergeGroupingUrlDuplicatesFilter дополнительно схлопывают близкие и дублирующиеся URL.
- Blender через TSerpResolver вставляет вертикали и считает их собственными факторами; дедуп THostDuplicateChecker распространяет лимит на домен на всю страницу, включая вертикали.
- Потолок DefaultMaxDocs равен 259 - позиций за ним не существует.