3
/** @noinspection PhpUnused */
7
require_once 'Search.php';
9
static $_stopWords = array(
10
'a', 'about', 'after', 'against', 'all', 'als', 'an', 'and', 'as', 'at', 'auf', 'aus', 'aux', 'b', 'by', 'c', 'd',
11
'da','dans', 'das', 'de', 'der', 'des', 'di', 'die', 'do', 'du', 'e', 'ein', 'eine', 'einen', 'eines', 'einer',
12
'el', 'en', 'et', 'f', 'for', 'from', 'fur', 'g', 'h', 'i', 'ihr', 'ihre', 'im', 'in', 'into', 'its', 'j', 'k',
13
'l', 'la', 'las', 'le', 'les', 'los', 'm', 'mit', 'mot', 'n', 'near', 'non', 'not', 'o', 'of', 'on', 'or', 'over',
14
'out', 'p', 'par', 'para', 'qui', 'r', 's', 'some', 'sur', 't', 'the', 'their', 'through', 'till', 'to', 'u',
15
'uber', 'und', 'under', 'upon', 'used', 'using', 'v', 'van', 'w', 'when', 'with', 'x', 'y', 'your','z', 'а',
16
'ая', 'б', 'без', 'бы', 'в', 'вблизи', 'вдоль','во', 'вокруг', 'всех', 'г', 'го', 'д', 'для', 'до', 'е','его',
17
'ее', 'ж', 'же', 'з', 'за', 'и', 'из', 'или', 'им', 'ими', 'их', 'к', 'как', 'ко', 'кое', 'л', 'летию', 'ли',
18
'м', 'между', 'млн', 'н', 'на', 'над', 'не', 'него', 'ним', 'них', 'о', 'об', 'от', 'п', 'по', 'под', 'после',
19
'при', 'р', 'с', 'со', 'т', 'та', 'так', 'такой', 'также', 'то', 'тоже', 'у', 'ф', 'х', 'ц', 'ч', 'ш', 'щ', 'ы',
20
'ые', 'ый', 'э', 'этих', 'этой', 'ю', 'я',
24
* Проверка, является ли заданный текст стоп-словом.
26
* @param $text string Текст для проверки.
27
* @return bool Результат.
29
function isStopWord($text)
37
foreach ($_stopWords as $word) {
38
if (!strcasecmp($word, $text)) {
47
* Коэффициент релевантности.
49
final class RelevanceCoefficient
52
* @var array Массив меток полей, для которых действует данный коэффициент.
57
* @var double Значение коэффициента.
64
public function __construct($value, $tags)
66
$this->fields = $tags;
67
$this->value = $value;
72
* Настройки для оценки релевантности.
74
final class RelevanceSettings
77
* @var array Массив коэффициентов релевантности.
82
* @var double Релевантность для упоминаний в посторонних полях.
87
* @var double Мультипликатор для случая полного совпадения.
94
public function __construct()
96
$this->coefficents = array();
97
$this->extraneous = 1.0;
98
$this->multiplier = 2.0;
102
* @return RelevanceSettings Настройки по умолчанию для базы IBIS.
104
public static function forIbis()
106
$result = new RelevanceSettings();
107
$result->extraneous = 1.0;
108
$result->multiplier = 2.0;
109
$result->coefficents = [
111
// заглавие или авторы
112
new RelevanceCoefficient (10,
114
200, // основное заглавие
115
700, 701, // индивидуальные авторы
116
710, 711, 971, 972, // коллективные авторы
117
923, // выпуск, часть
118
922, // статья сборника
119
925, // несколько томов в одной книге
120
961, // индивидуальные авторы общей части
121
962, // коллективы общей части
122
461, // заглавие общей части
123
463 // издание, в котором опубликована статья
127
new RelevanceCoefficient(7, [702]),
130
new RelevanceCoefficient(6, [
131
510, // параллельное заглавие
132
517, // разночтение заглавия
133
541, // перевод заглавия
134
924, // "другое" заглавие
135
921 // транслитерированное заглавие
139
new RelevanceCoefficient(6, [
141
922 // статья из журнала
145
new RelevanceCoefficient(5, [
146
606, // предметная рубрика
147
607, // географическая рубрика
148
600, 601, // персоналия
153
new RelevanceCoefficient(4, [225]),
155
// ключевые слова и аннотации
156
new RelevanceCoefficient(3, [
157
610, // ненормированное ключевое слово
166
* Загрузка настроек из указанного файла.
168
* @param $filename string Имя файла.
169
* @return RelevanceSettings
171
public static function load($filename)
173
$text = file_get_contents($filename);
174
$text = preg_replace( '![ \t]*//.*[ \t]*[\r\n]!', '', $text );
175
return json_decode($text, false);
180
* Оценщик релевантности найденных библиографических записей.
182
final class RelevanceEvaluator
185
* @var RelevanceSettings Настройки для оценки.
190
* @var array Массив терминов, на которые разбивается поисковый запрос.
195
* Оценка содержимого подполя.
197
* @param $text string Содержимое подполя.
198
* @param $value double Важность поля.
199
* @return double Оценка, выраженная числом.
201
private function evaluateText($text, $value) {
205
foreach ($this->terms as $term) {
206
if (stripos($text, $term) !== false) {
207
if (strcasecmp($text, $term) === 0) {
208
$result += $value * $this->settings->multiplier;
220
* Оценка содержимого поля.
222
* @param $field RecordField Поле, подоежащее оценке.
223
* @param $value double Важность поля.
224
* @return double Оценка, выраженная числом.
226
private function evaluateField ($field, $value) {
227
$result = $this->evaluateText($field->value, $value);
229
foreach ($field->subfields as $subfield) {
230
$result += $this->evaluateText($subfield->value, $value);
237
* Оценка реалеватности записи.
239
* @param $record MarcRecord Запись, подлежащая оценке.
240
* @return double Оценка, выраженная числом.
242
public function evaluate($record)
246
foreach ($this->settings->coefficents as $coefficent) {
247
foreach ($coefficent->fields as $tag) {
248
$fields = $record->getFields($tag);
249
foreach ($fields as $field) {
250
$result += $this->evaluateField($field, $coefficent->value);
255
foreach ($record->fields as $field) {
256
$result += $this->evaluateField($field, $this->settings->extraneous);
264
* Простой поиск "для чайников".
269
* @var array Массив префиксов для терминов.
274
* @var string Суффикс для терминов.
279
* @var RelevanceSettings Настройки для оценки релевантности.
284
* @var int Максимальное количество возвращаемых записей.
291
public function __construct()
293
// поиск по: автору, заглавию, коллективу, ключевым словам
294
$this->prefixes = array('A=', 'T=', 'M=', 'K=');
296
$this->settings = RelevanceSettings::forIbis();
301
* @var array Массив терминов.
306
* Построение поискового выражения по запросу на естественном языке.
308
* @param $query string Запрос на естественном языке.
309
* @return string Выражение для поиска по словарю.
311
public function buildSearchExpression($query)
319
$query = trim($query);
326
preg_match_all('/\w+/u', $query, $words);
327
foreach ($words[0] as $word) {
333
$terms = array_keys($terms);
335
foreach ($terms as $term) {
336
if (isStopWord($term)) {
340
$this->terms []= $term;
341
foreach ($this->prefixes as $prefix) {
346
$result .= Search::wrapIfNeeded($prefix . $term . $this->suffix);
356
* Поиск "для чайников" в текущей базе.
358
* @param $connection Connection Активное подключение к серверу.
359
* @param $query string Запрос на естественном языке.
360
* @return array Массив найденных MFN.
362
public function search($connection, $query) {
363
$query = trim($query);
368
$expression = $this->buildSearchExpression($query);
373
$found = $connection->searchRead($expression, $this->limit);
378
$evaluator = new RelevanceEvaluator();
379
$evaluator->settings = $this->settings;
380
$evaluator->terms = $this->terms;
382
foreach ($found as $record) {
383
$item = (object) array (
385
'rating' => $evaluator->evaluate($record)
390
usort($rating, static function ($first, $second) {
391
// сортировка по убыванию
392
return $second->rating - $first->rating;
395
return array_map (static function ($item) {
396
return $item->record->mfn;