Полнотекстовая поисковая машина с обратным индексом на PHP
Статья опубликована в образовательных целях. Содержит рабочий исполняемый код и структуры данных, необходимые для понимания работы полнотекстовых поисковых машин с обратным индексом.
Полная и достоверная информация о состоянии рынков и отраслей – необходимая основа для анализа спроса и предложения, планирования производств, борьбы с контрафактом и мошенничеством, планирования и проведения маркетинговых мероприятий и многого другого. Доступ к полной и достоверной информации о ситуации на рынках, позволяет организациям увеличить прибыль, снизить риски и издержки.
Базы данных полнотекстовых поисковых машин содержат в себе такую информацию, но всилу чрезвычайно большого объёма, обеспечение доступа к такой информации представляет собой сложную инженерную задачу. Цель статьи – достичь понимания того, как работают современные поисковые системы, раскрыть сильные и слабые стороны применяемых технологий. Статья предназначена не только для разработчиков больших информационных систем, но и для широкого круга заинтересованных лиц: предпринимателей, маркетологов, seo-специалистов и пр.
С момента возникновения вэба (World Wide Web), полнотекстовый поиск стал базовым способом обработки информации, размещаемой на интернет-сайтах.
Программное обеспечение полнотекстового поиска состояло из программы сборщика (crawler) и пользовательского приложения. Crawler (краулер) – это программа, которая автоматически сканирует интернет-страницы, чтобы обнаружить новый контент и собрать информацию о них для последующего занесения в базу данных (индекс) поисковой системы. Пользовательское приложение – это программа взаимодействия с пользователем. Это то, результат работы чего, пользователь видит в своём браузере или мобильном приложении.
Индекс – способ организации базы данных для быстрого доступа, по принципу оглавления книги, словаря или справочника. При наличии оглавления, нам не нужно листать страницу за страницей, всматриваясь в содержимое, а достаточно открыть указанную в оглавлении страницу.
Первые решения, такие как например, «ht://Dig» ( en.wikipedia.org/wiki/Ht-//Dig , 1995 г.) уже состояли из программы сборщика и пользовательского приложения и до поры до времени показывали хорошие результаты, но с ростом объема индексируемой информации, стали заметно тормозить.
Качественный скачок в технологии полнотекстового поиска произошел с применением обратного индекса (inverted index) и развитием языка PHP. Если оглавление книги принять за аналог индекса, то обратный индекс – аналог предметного указателя, это когда в конце книги есть список важных слов и номера страниц, на которых они встречаются.
Обработка информации в интернете относиться к области больших данных (big data) и весьма требовательна к вычислительной мощности и объёму вычислительных ресурсов. Поэтому, изначально, программирование полнотекстовых поисковых систем проводилось на языке Си. Язык характеризуется эффективностью, гибкостью и возможностью управления низкоуровневыми системными ресурсами. Но с некоторых пор и в некоторых ситуациях, достоинства языка Си, стали его недостатками.
До момента написания этой статьи, круг лиц, посвященных в технологии полнотекстового поиска, был ограничен узкими специалистами, умевших программировать на Си и имеющих время, мотивацию и средства, чтобы заниматься развитием этих технологий.
Программирование на Си очень трудоёмко, требует большого внимания и аккуратности. Программист должен сам заботиться о выделении памяти, совместимости процессоров и операционных систем, что при постоянном обновлении линейки процессоров и переходов на новые версии операционных систем приводит к тому, что новый релиз устаревает уже к моменту выпуска. В то же самое время, команда разработчиков языка PHP успешно решает проблемы управления памятью и совместимости платформ, а производительность языка PHP растет от версии к версии.
Обратный индекс – это таблица, содержащая номера документов, в которых встречается данное слово и координату слова в этом документе.
Например, слово «контакты»:
содержится в теле документов (secno=1) и в их заголовках (secno=2). В теле документа с id = 0, слово «контакты» занимает 1011 позицию от начала документа, в теле документа с id = 1 – 1016 позицию и т.д. То же самое и для заголовков.
В целом, структура обратного индекса одинакова во всех решениях, но могут быть незначительные отличия. Например, если в документе запрашиваемое слово встречается несколько раз, то в индексе могут храниться все координаты запрашиваемого слова, или некоторое количество наиболее близких к началу документа.
Здесь и далее я буду приводить в качестве примера решение сообщества разработчиков некоммерческого исследовательского проекта, Поисковой системы Делового сообщества ( www.lookover.su ). В качестве примера индексируемого пространства, выбрана часть самарского сегмента вэба, поскольку эта часть изучена мною лучше других.
Рассмотрим последовательность действий пользовательского приложения при запросе «строительство самара»:
https://phpsearch.ru/search-test.php/select-min.php?q=строительство самара
Нам нужно получить список всех документов, содержащих как слово «строительство», так и слово «самара».
Сначала, делаем выборку из таблицы обратного индекса для слова «строительство». SQL-запрос на языке SQL (Structured Query Language):
«SELECT word,secno,intag FROM `bdict` WHERE `word`='строительство'»
означает
«ВЫБРАТЬ значения колонок word, secno и intag ИЗ таблицы `bdict`, ДЛЯ КОТОРЫХ значение колонки `word` равно 'строительство'»
Время выполнения программного кода будет незначительно меняться, в зависимости от загруженности сервера.
$time1=(0.001)
$sql=SELECT word,secno,intag FROM `bdict` WHERE `word`='строительство'
| № | word | secno | intag |
| 1 | строительство | 2 | 42:132|83:101|825:104|1135:103|1146:103|1395:103|1482:101|1505:101|2145:101|2846:101|2850:103|2851:1... |
| 2 | строительство | 1 | 42:1635|69:1141|83:1041|92:1155|100:1083|114:1060|153:1104|180:1395|182:1554|183:1073|184:1130|194:1... |
| 3 | строительство | 3 | 10222:1|10520:1|10521:1|10525:1|10946:2|11617:1|11649:1|11846:1|11955:1|14834:1|16213:1|16214:1|1621... |
$time2=(0.006) (+0.005)
Слово «строительство» содержится в 2085 документах. Выборка заняла 0,006 секунд.
Теперь, делаем выборку для слова «самара»
$time1=(0.006)
$sql=SELECT word,secno,intag FROM `bdict` WHERE `word`='самара'
| № | word | secno | intag |
| 1 | самара | 1 | 0:1367|4:1246|7:1006|8:1039|9:1267|13:1107|14:1345|18:1004|20:1022|21:1337|23:1044|24:1038|27:1353|3... |
| 2 | самара | 2 | 56:126|184:105|196:105|197:110|230:103|272:104|282:105|300:108|486:103|608:111|776:109|806:105|864:1... |
| 3 | самара | 3 | 10356:1|10368:2|10423:2|10434:2|10522:4|10526:1|10533:3|10534:3|10574:3|10634:3|10640:4|10651:3|1067... |
$time2=(0.041) (+0.035)
Слово «самара» содержится в 16079 документах. Выборка заняла 0,035 секунд.
Теперь нам нужно выяснить, в каких документах оба слова встречаются одновременно. Для этого нам нужно вычислить пересечение полученных массивов (множеств).
Начало вычисления пересечения массивов $time1=(0.076)
Закончили вычисление пересечения массивов $time2=(0.109) (+0.032)
$sectionArr=Array
(
[1] => body
[2] => title
[3] => domen
)
$time=(0.109) $total=824
Слова «строительство» и «самара» одновременно встречаются в 824 документах. Вычисление заняло 0,032 секунды.
Далее формируем выдачу, которая даёт задержку ещё в 0,081 секунд.
Суммарное время работы php-скрипта фиксируем в самом конце выдачи — 0.157 секунды.
$time=(0.157) (+0.081)
Итог наблюдений. Формирование выдачи на конечном этапе, даёт стабильную задержку, на которую мы не можем повлиять, поэтому здесь нам улучшать нечего. Остаётся два узких места: выборка из таблицы обратного индекса и вычисление пересечения массивов.
Можно заметить, что задержка выборки из таблицы обратного индекса зависит от размера записи в поле «intag». Чем чаще встречается слово в документах поисковой базы данных, тем объёмнее содержимое поля «intag» и тем больше времени нужно для извлечения и обработки этого значения. Возможный путь к обеспечению приемлемой задержки – разбиение больших баз на части и последовательно - параллельная выборка по частям.
Вычисление пересечения массивов – частный случай операций с массивами. Этих операций может быть довольно много, особенно если мы пытаемся анализировать и учитывать входящие ссылочные массы.
Необходимость учитывать входящие ссылочные массы связано с тем фактом, что координатами ключевых слов легко манипулировать. Это порождает злоупотребления, известные как поисковый спам. Значительно сложнее манипулировать входящими ссылками, поэтому анализ входящей ссылочной массы -- хороший критерий для оценки репутации владельцев и достоверности предоставляемой ими информации. Пример учета ссылочной массы приведен в следующем примере, который Вы сможете разобрать самостоятельно. Здесь лишь важно понять: чтобы гарантировать быстрый, менее 1 секунды, отклик пользовательского приложения, нам придется ограничить результаты выборки (объём массива $rec_idArr) для последующей обработки, т.к. эта обработка может оказаться ресурсоёмкой. Это неизбежно приводит к урезанию выдачи, в результате чего, выдача уже не гарантирует полноту, а нередко и репрезентативность, т.е. мы не можем гарантировать что выдаваемая информация отражает реальность, скорее наоборот, можно гарантировать, что выдача реальности не отражает. Это обстоятельство представляет собой фундаментальное ограничение, «узкое место» способа обратного индекса над преодолением которого работают коллективы разработчиков.
Так, например, популярный поисковик, на высокочастотный запрос, обрывает свою выдачу на 24 странице:
www.google.com/search?q=строительство самара&start=260
В то время как разработка участников движения Open Source, даёт репрезентативную, а нередко и полную выдачу:
Исходный код PHP-скриптов, структуры баз данных и исходных данных для индексации, приведены в конце статьи, в Приложении. Эти материалы могут служить основой для разработки Ваших собственных поисковых систем. Но для того, чтобы разобраться в работе php-скриптов и баз данных, не обязательно скачивать архивы, разворачивать их на своей площадке с необходимыми правами доступа и установленным ПО. Можно самостоятельно поработать с кодом прямо на этой площадке, задавая точки останова и вывод значений переменных процедурами echo, print_r и exit:
https://phpsearch.ru/search-test.php/view-min.php
Например, кликнув мышью на номер строки «11», получаем возможность отредактировать одиннадцатую строку кода,
убрав знаки комментариев «//» из начала строки, сохраняем изменения:
Теперь можем запустить измененный скрипт и получить в браузере содержимое массива $allowCharsRangeMin.
Для получения более информативной выдачи, поисковые системы допускают использование метаязыка запросов. Метаязык поисковых запросов, представляет собой вид Булева языка запросов (Boolean query language).
Краткая спецификация языка:
« » (пробел) - логическое И. Например, «строительство проектирование». На этот запрос получаем сайты, содержащие оба слова: «строительство» и «проектирование».
«|» - логическое ИЛИ. Например, «кафе|ресторан». Получим сайты, содержащие или слово «кафе» или слово «ресторан».
«~» - логическое НЕ. Например, «строительство ~проектирование». Получим сайты, содержащие слово "строительство" и в тоже время не содержащие слово "проектирование". Оператор «~» всего лишь исключает сайты, содержащие слово «проектирование» из результата поиска. Запрос «~проектирование» ничего не даст.
( ) – круглые скобки, оператор группирования для создания сложных запросов поиска. Например, «(строительство|проектирование) (зданий|сооружений|домов) ~каркасный».
«"» - оператор выделения фраз точного вхождения. Например, «"отдел продаж"». Получим все сайты, в которых слово «продаж» следует сразу за словом «отдел».
Представляется разумным искать не только по точному значению слова, например «контакт», а по всем его словоформам: «контакт, контакта, контакту, контактом, контакте, контакты, контактов, контактам, контактами, контактах»:
https://phpsearch.ru/search-test.php/select.php?q=контакт
Для этого используем словарь русского языка и правила образования словоформ.
Представим гиперссылку, связывающую два сайта, как абстрактный количественный ресурс ценности. Исходящая ссылка уменьшает ценность, а входящая – увеличивает. Если результат влияния входящих ссылок равен результату влияния исходящих ссылок, то мы получаем условия игры с нулевой суммой, не поддерживающей мотивацию обмениваться ссылками. Создадим мотивацию к обмену ссылками, сокращая или полностью исключая уменьшение баланса.
Возьмем для примера список связей сайтов:
| Сайт | Исходящие ссылки, найденные на сайте |
| samaramarketplace.com | samzas.ru;internet-elite.org;stanki-gr.ru; |
| internet-elite.org | samzas.ru; |
| samzas.ru | samaramarketplace.com; |
| stanki-gr.ru | samaramarketplace.com; |
| proud-and-lonely.com |
Рассчитаем процесс передачи ценности между сайтами. Для этого используем таблицы базы данных, т.к. скорость доступа к данным имеет решающее значение.
При обсчете Рунета, число записей будет превышать миллионы. Учитывая тот факт, что каждая операция вставки или обновления данных в таблице приводит к автоматическому пересчету индексов, что приводит к зависанию движка СУБД, будет разумным сначала формировать дамп таблицы, а потом этот дамп загружать.
Шаг первый. Формируем список сайтов и задаём исходное значение pop_rank по умолчанию:
SELECT * FROM `domens`:| № | id | domen | pop_rank |
| 1 | 0 | samaramarketplace.com | 10 |
| 2 | 1 | internet-elite.org | 10 |
| 3 | 2 | samzas.ru | 10 |
| 4 | 3 | stanki-gr.ru | 10 |
| 5 | 4 | proud-and-lonely.com | 10 |
Шаг второй. Формируем таблицу связей между сайтами. Ценность сайта (weight) распределяется между исходящими ссылками поровну:
SELECT * FROM `links`:| № | ot | k | weight |
| 1 | 0 | 2 | 3.33333 |
| 2 | 0 | 1 | 3.33333 |
| 3 | 0 | 3 | 3.33333 |
| 4 | 1 | 2 | 10 |
| 5 | 2 | 0 | 10 |
| 6 | 3 | 0 | 10 |
Шаг третий. Суммируем веса входящих ссылок и возвращаем в первую таблицу:
SELECT * FROM `domens`| № | id | domen | pop_rank |
| 1 | 0 | samaramarketplace.com | 30 |
| 2 | 1 | internet-elite.org | 13.3333 |
| 3 | 2 | samzas.ru | 23.3333 |
| 4 | 3 | stanki-gr.ru | 13.3333 |
| 5 | 4 | proud-and-lonely.com | 10 |
Это самый простой и быстрый способ обсчета связей Рунета. Он может быть существенно усовершенствован. Например, значение pop_rank по умолчанию могут быть не одинаковыми. Сайтам с высокой репутацией и большим количеством исходящих ссылок, таким, например, как отраслевые каталоги значение по умолчанию может быть присвоено заведомо высокое. Ценность сайта так же может распределяется между исходящими ссылками не поровну, а исходя их каких-то особых соображений разработчика.
Тестовый скрипт, в котором реализован необходимый и достаточный функционал полноценной поисковой машины, содержит обработчик метаязыка поисковых запросов, использует словоформы русского языка для расширенного поиска и ссылочное ранжирование:
https://phpsearch.ru/search-test.php/select.php
Исходный код с возможностью экспериментов:
https://phpsearch.ru/search-test.php/view.php
С функционалом пользовательского приложения как будто всё. Можно переходить с программе – индексатору.
Программа индексатор (сборщик), по заданному расписанию, обходит заданную коллекцию сайтов, закачивает и нормализует содержимое сайтов, сохраняет на диске. Затем, программа indexer.php обходит уже нормализованные данные, полученные с сайтов, парсит (разбирает на документы, части документов (sections) и слова) и формирует обратный индекс. Исходный код indexer.php есть в Приложении. Разрешение у авторов на раскрытие кода утилиты, которая обходит коллекцию сайтов и сохраняет нормализованные данные на диске, я не получил. Они объяснили, что из-за быстрого развития вэб-технологий, гарантировать устойчивую работу универсальной версии программы, они не готовы. Кроме того, эта утилита содержит в себе части, на которые не распространяется лицензия GPL. Но я не думаю, что написать программу с этим функционалом самостоятельно, конкретно для Вашей коллекции сайтов, будет для Вас слишком сложно.
Пользователю с умеренной компетентностью, нередко бывает трудно правильно сформулировать запрос. На помощь ему призвана построчная подсказка, а теперь уже и Искусственный Интеллект.
ИИ поисковой системы представляет собой языковую модель, использующей в качестве исходных данных текстовой корпус собственного обратного индекса: ru.wikipedia.org/wiki/Корпусная_лингвистика#Веб_как_корпус
Цикл создания частотного словаря принципиально не отличается от цикла создания обратного индекса. Пример скрипта и данных, приведен в Приложении и доступен для скачивания.
Частотный словарь содержит информацию о связях между словами и может служить основой для создания специализированной языковой модели именно делового вэба с региональной спецификой. Например, сразу за словом "строительство", 373 раза встретилось слово "ремонт", 218 раз -- слово "домов" и т.д.:
phpsearch.ru/indexer.php/arr_cache/?word=строительство
ремонт : 373
домов : 218
архитектура : 165
каркасных : 48
включающий : 46
бассейнов : 41
дома : 38
и т.д.
Частотный словарь, созданный на том же языковом корпусе, что и обратный индекс, даёт хорошую основу для подстрочной подсказки:
Если Вы разобрались с работой пользовательского приложения select.php, то вы умеете разбирать предложения на части речи и сможете выстраивать собственную, специализированную языковую модель. Работа нейросети, реализованной на PHP, хорошо освещена в интернете и выходит за рамки этой статьи.
Для предотвращения выхода за рамки правового поля, и связанных с этим рисков, необходимо понимание собственных прав и обязанностей, а также прав и обязанностей других, закрепленных законодательно.
Право человека на информацию закреплено:
В Конституции Российской Федерации: «Каждый имеет право свободно искать, получать, передавать, производить и распространять информацию любым законным способом. Перечень сведений, составляющих государственную тайну, определяется федеральным законом.» (Глава 2. Статья 29. Часть 4).
В Федеральном законе N 149-ФЗ "Об информации, информационных технологиях и о защите информации": «Граждане (физические лица) и организации (юридические лица) (далее - организации) вправе осуществлять поиск и получение любой информации в любых формах и из любых источников при условии соблюдения требований, установленных настоящим Федеральным законом и другими федеральными законами.» (Статья 8 «Право на доступ к информации», Пункт 1).
Чувствительная информация — это сведения, несанкционированное раскрытие, изменение или уничтожение которых может нанести ущерб, убытки или навредить их владельцу, включая личные (персональные, медицинские, финансовые), коммерческие и государственные тайны. К такой информации относятся, например, паспортные данные, информация о банковских картах, бизнес-планы, а также данные о критической инфраструктуре.
Интернет – сайты, открыто опубликованные в интернете, обычно не содержат чувствительной информации, но есть веские причины изучить федеральные законы
- Закон РФ "О государственной тайне" от 21.07.1993 N 5485-1
- Федеральный закон "О персональных данных" от 27.07.2006 № 152-ФЗ
- Федеральный закон "О коммерческой тайне" от 29.07.2004 N 98-ФЗ
- Федеральный закон от 21.11.2011 N 323-ФЗ (ред. от 23.07.2025) "Об основах охраны здоровья граждан в Российской Федерации". Статья 13. Соблюдение врачебной тайны.
Если Вы планируете размещение рекламы, то Вам необходимо изучить
- Федеральный закон "О рекламе" от 13.03.2006 N 38-ФЗ
Особенно в части маркировки рекламы в интернете:
- Федеральный закон "О внесении изменений в Федеральный закон "О рекламе" и отдельные законодательные акты Российской Федерации" от 26.12.2024 N 479-ФЗ
Не позволяйте использовать Ваши поисковые системы в качестве площадок для совершения противоправных действий, своевременно проверяйте выдачу и блокируйте информацию с сайтов, имеющих противоправные признаки.
Интернет-сайт является объектом авторского права. Авторское право регулируется Гражданским кодексом Российской Федерации (глава 70, "Авторское право").
Авторское право распространяется как на сайт в целом, так и на его части, такие как текст заголовков документов, текст, из которого составляется контекст поиска, скрин-шоты первых страниц и пр.
Формирование контекста поиска без согласия авторов и правообладателей возможно, в соответствии статье 1274 ГК РФ: «Свободное использование произведения в информационных, научных, учебных или культурных целях».
Если Вы планируете использовать Вашу поисковую систему в информационных, некоммерческих целях и указываете прямую ссылку на найденные сайты, то согласия авторов и правообладателей не потребуется. Если Вы планируете использовать Вашу поисковую систему в коммерческих целях, например, как площадку для контекстной рекламы, то Вам потребуется изучить 70-ю главу ГК РФ "Авторское право" и предусмотреть устранение спорных моментов.
С пожеланием
продуктивного бизнеса,
жизнерадостного маркетинга
и увлекательного программирования
Роман Булатов
7 ноября 2025г.
С переходом с языка Си на язык PHP, стало гораздо проще и быстрее писать программный код. Больше внимания удается уделять постановке задачи. Круг разработчиков расширился далеко за пределы сообщества высококомпетентных программистов и уже охватывает производственников, предпринимателей, представителей торговли, маркетологов, рекламщиков, вэб-разработчиков, seo-специалистов, строителей, врачей, преподавателей ВУЗов, студентов, военнослужащих, сотрудников правоохранительных органов, художников, журналистов и даже фитнес-тренеров и управдомов. Всех, кто готов поделиться собственными наблюдениями, напрямую или опосредованно имеющими отношение к Информационным Технологиям. Наблюдения могут быть ценными, могут быть не очень, а могут быть на первый взгляд не очень, но потом — очень даже.
Особая благодарность за участие и поддержку
|
Александру Владимирову Александру Ульянину Александру Стрижкову Алексею Паршину Алле Вьюговой Алефтине Мистрюковой Анастасии Гончаровой Андрею Комарчеву Анне Грановской Ашоту Санояну Вадиму Ефремову Вадиму Квасову Ванде Кринитской Владимиру Осинскому Владимиру Сапареву Владу Вьюгову Вячеславу Гурьянову Геннадию Кашлеву Дмитрию Гончарову Дмитрию Оганесову Дмитрию Хмаре Евгению Ефремову Егору Камоцкому Елене Яшиной Игорю Зингиревич Ильдару Хасаншину Илье Буякову Ирине Прокопенко (Спиридоновой) Ирине Шведовой Михаилу Федину Наталье Вихановой Николаю Никульникову Николаю Сёмкину Олегу Серебрякову Олегу Хабибулину Ольге Гурьяновой Ольге Зингиревич Рафаэлю Гиматдинову Светлане Камоцкой Сергею Копаневу Сергею Сидорову Юлии Смирновой Юрию Мамаеву Юрию Прокопенко Ярославу Похильчуку |
А также всем сотрудникам Торгово-промышленной палаты Российской Федерации, поддержавших и продолжающих поддерживать проект.
Особая признательность Дмитрию Юрьевичу Сысоеву, к.филос.н., за существенный вклад в понимание места Информационных Технологий в современной Картине Мира.
| Файл | Описание | Дата и время последнего изменения | Размер |
| search-test.tar.gz | Исполняемый php-код для поиска по сайтам с использованием обратного индекса | 2025-10-25 09:55:16 | 1 Мб |
| indexer.tar.gz | Исполняемый php-код для создания базы обратного индекса | 2025-10-25 09:55:16 | 15 Кб |
| index-dump.tar.gz | Образец поисковой базы с обратным индексом. Результат работы программы - индексатора | 2025-10-25 09:55:41 | 2 Кб |
| web-local.tar.gz | Образец исходных данных для индексации | 2025-10-25 09:55:41 | 22 Кб |
| links-structure.tar.gz | Образец ссылочной структуры Вэба для вычисления ссылочного ранжирования | 2025-10-25 09:55:48 | 22 Мб |
| artificial-intelligence.tar.gz | Создание частотного словаря для языковой модели делового вэба | 2025-10-25 09:55:41 | 54 Мб |
| LICENSE | Лицензия | 2025-10-23 00:18:52 | 2 Кб |
| gpl-3.0.txt | Стандартная общественная лицензия GNU. Версия 3, 29 июня 2007 г. | 2025-09-11 11:16:26 | 34 Кб |
| gpl-3.0-rus.txt | Стандартная общественная лицензия GNU. Версия 3, 29 июня 2007 г. Русский перевод. | 2025-10-23 00:06:25 | 67 Кб |
Авторские права ©2024-2025. Все права защищены