Давайте обсудим предстоящую версию FlexSearch v0.8 здесь: #415
Базовый запуск • Справочник по API • Индексы документов • Использование Worker • Журнал изменений
Вы можете помочь мне, сделав личное пожертвование на поддержание этого проекта, а также внеся весь свой вклад в решение ваших нужд.
ООО «Антитезис Оперейшнс»
Когда дело доходит до скорости поиска, FlexSearch превосходит все существующие библиотеки поиска, а также предоставляет гибкие возможности поиска, такие как поиск по нескольким полям, фонетические преобразования или частичное совпадение.
В зависимости от используемых опций он также обеспечивает наиболее эффективный индекс использования памяти. FlexSearch представляет новый алгоритм оценки, называемый «контекстным индексом», основанный на архитектуре лексического словаря с предварительной оценкой, который фактически выполняет запросы до 1 000 000 раз быстрее по сравнению с другими библиотеками. FlexSearch также предоставляет вам неблокирующую модель асинхронной обработки, а также веб-работников для параллельного выполнения любых обновлений или запросов к индексу через выделенные сбалансированные потоки.
Поддерживаемые платформы:
Библиотечное сравнение «Путешествия Гулливера»:
Плагины (сторонние проекты):
Строить | Файл | CDN |
flexsearch.bundle.js | Скачать | https://rawcdn.githack.com/nextapps-de/flexsearch/0.7.31/dist/flexsearch.bundle.js |
flexsearch.light.js | Скачать | https://rawcdn.githack.com/nextapps-de/flexsearch/0.7.31/dist/flexsearch.light.js |
flexsearch.compact.js | Скачать | https://rawcdn.githack.com/nextapps-de/flexsearch/0.7.31/dist/flexsearch.compact.js |
flexsearch.es5.js * | Скачать | https://rawcdn.githack.com/nextapps-de/flexsearch/0.7.31/dist/flexsearch.es5.js |
Модули ES6 | Скачать | Папка /dist/module/ этого репозитория Github. |
* Пакет «flexsearch.es5.js» включает полифилы для поддержки EcmaScript 5.
npm install flexsearch
Пакет Node.js включает в себя все функции
flexsearch.bundle.js
.
Особенность | flexsearch.bundle.js | flexsearch.compact.js | flexsearch.light.js |
Пресеты | ✓ | ✓ | - |
Асинхронный поиск | ✓ | ✓ | - |
Рабочие (Web + Node.js) | ✓ | - | - |
Контекстные индексы | ✓ | ✓ | ✓ |
Индексные документы (поиск по полям) | ✓ | ✓ | - |
Хранилище документов | ✓ | ✓ | - |
Частичное совпадение | ✓ | ✓ | ✓ |
Оценка релевантности | ✓ | ✓ | ✓ |
Автоматически сбалансированный кеш по популярности | ✓ | - | - |
Теги | ✓ | - | - |
Предложения | ✓ | ✓ | - |
Фонетическое соответствие | ✓ | ✓ | - |
Настраиваемая кодировка/язык (Matcher, Encoder, Tokenizer, Stemmer, Filter, Split, RTL) | ✓ | ✓ | ✓ |
Индексы экспорта/импорта | ✓ | - | - |
Размер файла (gzip) | 6,8 КБ | 5,3 КБ | 2,9 КБ |
Сравнение запусков: тест производительности «Путешествия Гулливера»
Операций в секундах, чем выше, тем лучше, кроме теста "Память", на котором ниже - лучше.
Классифицировать | Библиотека | Память | Запрос (один термин) | Запрос (многосрочный) | Запрос (длинный) | Запрос (Дупы) | Запрос (не найден) |
1 | Гибкий поиск | 17 | 7084129 | 1586856 | 511585 | 2017142 | 3202006 |
2 | JSii | 27 | 6564 | 158149 | 61290 | 95098 | 534109 |
3 | Уэйд | 424 | 20471 | 78780 | 16693 | 225824 | 213754 |
4 | JS-поиск | 193 | 8221 | 64034 | 10377 | 95830 | 167605 |
5 | Elasticlunr.js | 646 | 5412 | 7573 | 2865 | 23786 | 13982 |
6 | Массовый поиск | 1021 | 3069 | 3141 | 3333 | 3265 | 21825569 |
7 | МиниПоиск | 24348 | 4406 | 10945 | 72 | 39989 | 17624 |
8 | бм25 | 15719 | 1429 | 789 | 366 | 884 | 1823 г. |
9 | Лунр.js | 2219 | 255 | 271 | 272 | 266 | 267 |
10 | Нечеткий поиск | 157373 | 53 | 38 | 15 | 32 | 43 |
11 | Предохранитель | 7641904 | 6 | 2 | 1 | 2 | 3 |
Существует 3 типа индексов:
Index
— это плоский высокопроизводительный индекс, в котором хранятся пары идентификатор-контент.Worker
/ WorkerIndex
— это также плоский индекс, который хранит пары идентификатор-контент, но работает в фоновом режиме как выделенный рабочий поток.Document
— это индекс с несколькими полями, который может хранить сложные документы JSON (также может существовать из рабочих индексов).Большинству из вас, вероятно, понадобится только один из них в соответствии с вашим сценарием.
< script src =" node_modules/flexsearch/dist/flexsearch.bundle.min.js " > </ script >
< script >
// FlexSearch is available on window.FlexSearch
// Access FlexSearch static methods via bundled export (static class methods of FlexSearch)
const index = FlexSearch . Index ( options ) ;
const document = FlexSearch . Document ( options ) ;
const worker = FlexSearch . Worker ( options ) ;
</ script >
< script type =" module " >
// FlexSearch is NOT available on window.FlexSearch
// Access FlexSearch static methods by importing them explicitly
import Index from "./node_modules/flexsearch/dist/module/index" ;
import Document from "./node_modules/flexsearch/dist/module/document" ;
import Worker from "./node_modules/flexsearch/dist/module/worker" ;
const index = new Index ( options ) ;
const document = new Document ( options ) ;
const worker = new Worker ( options ) ;
</ script >
< script type =" module " >
// FlexSearch is NOT available on window.FlexSearch
// Access FlexSearch static methods via bundled export (static class methods of FlexSearch)
import FlexSearch from "./node_modules/flexsearch/dist/flexsearch.bundle.module.min.js" ;
const index = FlexSearch . Index ( options ) ;
const document = FlexSearch . Document ( options ) ;
const worker = FlexSearch . Worker ( options ) ;
</ script >
Или через CDN:
< script src =" https://cdn.jsdelivr.net/gh/nextapps-de/[email protected]/dist/flexsearch.bundle.min.js " > </ script >
AMD/CommonJS:
var FlexSearch = require ( "./node_modules/flexsearch/dist/flexsearch.bundle.min.js" ) ;
npm install flexsearch
В ваш код включите следующее:
const { Index , Document , Worker } = require ( "flexsearch" ) ;
const index = new Index ( options ) ;
const document = new Document ( options ) ;
const worker = new Worker ( options ) ;
Или:
const FlexSearch = require ( "flexsearch" ) ;
const index = new FlexSearch . Index ( options ) ;
const document = new FlexSearch . Document ( options ) ;
const worker = new FlexSearch . Worker ( options ) ;
index . add ( id , text ) ;
index . search ( text ) ;
index . search ( text , limit ) ;
index . search ( text , options ) ;
index . search ( text , limit , options ) ;
index . search ( options ) ;
document . add ( doc ) ;
document . add ( id , doc ) ;
document . search ( text ) ;
document . search ( text , limit ) ;
document . search ( text , options ) ;
document . search ( text , limit , options ) ;
document . search ( options ) ;
worker . add ( id , text ) ;
worker . search ( text ) ;
worker . search ( text , limit ) ;
worker . search ( text , options ) ;
worker . search ( text , limit , options ) ;
worker . search ( text , limit , options , callback ) ;
worker . search ( options ) ;
worker
наследует от типа Index
и не наследует от типа Document
. Таким образом, WorkerIndex по сути работает как стандартный индекс FlexSearch. Поддержку Worker в документах необходимо включить, просто передав соответствующую опцию во время создания { worker: true }
.
Каждый метод, вызываемый в индексе
Worker
, рассматривается как асинхронный. Вы получите обратноPromise
или в качестве альтернативы можете предоставить функцию обратного вызова в качестве последнего параметра.
Глобальные методы:
Индексные методы:
Методы WorkIndex:
Методы документа:
* Для каждого из этих методов существует асинхронный эквивалент:
Асинхронная версия:
Асинхронные методы возвращают Promise
, в качестве альтернативы вы можете передать функцию обратного вызова в качестве последнего параметра.
Методы export
и import
всегда асинхронны, как и каждый метод, который вы вызываете в индексе на основе Worker.
FlexSearch обладает широкими возможностями настройки. Использование правильных параметров может действительно улучшить ваши результаты, а также сэкономить память и время запроса.
Вариант | Ценности | Описание | По умолчанию |
предустановка | "память" "производительность" "соответствовать" "счет" "по умолчанию" | Профиль конфигурации в качестве ярлыка или основы для ваших пользовательских настроек. | "по умолчанию" |
токенизировать | "строгий" "вперед" "обеспечить регресс" "полный" | Режим индексации (токенайзер). Выберите один из встроенных модулей или передайте собственную функцию токенизатора. | "строгий" |
кэш | логическое значение Число | Включите/отключите и/или установите емкость кэшированных записей. При передаче числа в качестве ограничения кэш автоматически балансирует сохраненные записи в зависимости от их популярности . Примечание. При использовании «true» кеш не имеет ограничений и неограниченный рост. | ЛОЖЬ |
разрешение | Число | Устанавливает разрешение оценки (по умолчанию: 9). | 9 |
контекст | логическое значение Параметры контекста | Включить/отключить контекстное индексирование. При передаче значения «true» для контекста будут приниматься значения по умолчанию. | ЛОЖЬ |
оптимизировать | логическое значение | Если этот параметр включен, для индекса используется оптимизированный для памяти поток стека. | истинный |
способствовать росту | функция (arr, str, int) => float | Пользовательская функция повышения, используемая при индексировании содержимого в индекс. Функция имеет следующую подпись: Function(words[], term, index) => Float . Он имеет 3 параметра, в которых вы получаете массив всех слов: текущий термин и текущий индекс, в котором этот термин помещается в массив слов. Вы можете применить свои собственные вычисления, например, количество вхождений термина, и вернуть этот коэффициент (<1 означает, что релевантность снижена, >1 означает, что релевантность повышена).Примечание. В настоящее время эта функция ограничена использованием только «строгого» токенизатора. | нулевой |
Параметры и кодировка для конкретного языка: | |||
кодировка | Полезная нагрузка набора символов Строка (ключ) | Предоставьте пользовательскую полезную нагрузку кодировки или передайте один из ключей встроенных кодировок. | "латинский" |
язык | Языковая полезная нагрузка Строка (ключ) | Предоставьте пользовательские полезные данные языка или передайте флаг сокращенного языка (ISO-3166) для встроенных языков. | нулевой |
кодировать | ЛОЖЬ "по умолчанию" "простой" "баланс" "передовой" "дополнительный" функция(строка) => [слова] | Тип кодировки. Выберите один из встроенных модулей или передайте собственную функцию кодирования. | "по умолчанию" |
стеммер | ЛОЖЬ Нить Функция | ЛОЖЬ | |
фильтр | ЛОЖЬ Нить Функция | ЛОЖЬ | |
совпадение | ЛОЖЬ Нить Функция | ЛОЖЬ | |
Дополнительные параметры индексов документов: | |||
рабочий | логическое значение | Включить/отключить и установить количество запущенных рабочих потоков. | ЛОЖЬ |
документ | Дескриптор документа | Включает определения индекса и хранилища документов. |
Вариант | Ценности | Описание | По умолчанию |
разрешение | Число | Устанавливает разрешение оценки для контекста (по умолчанию: 1). | 1 |
глубина | ЛОЖЬ Число | Включить/отключить контекстное индексирование, а также установить контекстную дистанцию релевантности. Глубина — это максимальное количество слов/лексем, отделяющее термин, который считается релевантным. | 1 |
двунаправленный | логическое значение | Устанавливает результат двунаправленного поиска. Если включено и исходный текст содержит «красная шляпа», он будет найден по запросам «красная шляпа» и «красная шляпа». | истинный |
Вариант | Ценности | Описание | По умолчанию |
идентификатор | Нить | "идентификатор"" | |
ярлык | ЛОЖЬ Нить | "ярлык" | |
индекс | Нить Массив<Строка> Массив<Объект> | ||
магазин | логическое значение Нить Массив<Строка> | ЛОЖЬ |
Вариант | Ценности | Описание | По умолчанию |
расколоть | ЛОЖЬ Регэксп Нить | Правило разделения слов при использовании нестандартного токенизатора (встроенного, например, «вперед»). Используйте строку/символ или регулярное выражение (по умолчанию: /W+/ ). | /[W_]+/ |
ртл | логическое значение | Включает кодировку справа налево. | ЛОЖЬ |
кодировать | функция(строка) => [слова] | Пользовательская функция кодирования. | /lang/latin/default.js |
Вариант | Ценности | Описание |
стеммер | ЛОЖЬ Нить Функция | Отключите или передайте флаг сокращения языка (ISO-3166) или пользовательский объект. |
фильтр | ЛОЖЬ Нить Функция | Отключите или передайте флаг сокращения языка (ISO-3166) или собственный массив. |
совпадение | ЛОЖЬ Нить Функция | Отключите или передайте флаг сокращения языка (ISO-3166) или собственный массив. |
Вариант | Ценности | Описание | По умолчанию |
предел | число | Устанавливает лимит результатов. | 100 |
компенсировать | число | Применить смещение (пропустить элементы). | 0 |
предлагать | логическое значение | Включает предложения в результатах. | ЛОЖЬ |
Вариант | Ценности | Описание | По умолчанию |
индекс | Нить Массив<Строка> Массив<Объект> | Устанавливает поля документа, в которых следует осуществлять поиск. Если поле не задано, поиск будет осуществляться по всем полям. Также поддерживаются пользовательские параметры для каждого поля. | |
ярлык | Нить Массив<Строка> | Устанавливает поля документа, в которых следует осуществлять поиск. Если поле не задано, поиск будет осуществляться по всем полям. Также поддерживаются пользовательские параметры для каждого поля. | ЛОЖЬ |
обогащать | логическое значение | Дополните идентификаторы из результатов соответствующими документами. | ЛОЖЬ |
логическое значение | "и" "или" | Устанавливает используемый логический оператор при поиске по нескольким полям или тегам. | "или" |
Tokenizer влияет на требуемую память также на время запроса и гибкость частичных совпадений. Попробуйте выбрать из этих токенайзеров самый верхний, который соответствует вашим потребностям:
Вариант | Описание | Пример | Фактор памяти (n = длина слова) |
"строгий" | индексировать целые слова | foobar | * 1 |
"вперед" | постепенно индексировать слова в прямом направлении | fo обарfoob , а | * н |
"обеспечить регресс" | постепенно индексировать слова в обоих направлениях | ar фо obar | * 2н – 1 |
"полный" | проиндексируйте каждую возможную комбинацию | oba рoob ар | * н * (н - 1) |
Кодирование влияет на требуемую память также на время запроса и фонетические совпадения. Попробуйте выбрать самый верхний из этих кодировщиков, который соответствует вашим потребностям, или передать собственный кодировщик:
Вариант | Описание | Ложные срабатывания | Сжатие |
ЛОЖЬ | Отключить кодирование | нет | 0% |
"по умолчанию" | Кодировка без учета регистра | нет | 0% |
"простой" | Кодировка без учета регистра Нормализация кодировок | нет | ~ 3% |
"баланс" | Кодировка без учета регистра Нормализация кодировок Буквальные преобразования | нет | ~ 30% |
"передовой" | Кодировка без учета регистра Нормализация кодировок Буквальные преобразования Фонетическая нормализация | нет | ~ 40% |
"дополнительный" | Кодировка без учета регистра Нормализация кодировок Буквальные преобразования Фонетическая нормализация Преобразования Soundex | да | ~ 65% |
функция() | Передайте пользовательскую кодировку через функцию (строка):[слова] |
var index = new Index ( ) ;
Создайте новый индекс и выберите один из пресетов:
var index = new Index ( "performance" ) ;
Создайте новый индекс с настраиваемыми параметрами:
var index = new Index ( {
charset : "latin:extra" ,
tokenize : "reverse" ,
resolution : 9
} ) ;
Создайте новый индекс и дополните предустановку пользовательскими параметрами:
var index = new FlexSearch ( {
preset : "memory" ,
tokenize : "forward" ,
resolution : 5
} ) ;
Просмотрите все доступные пользовательские параметры.
Каждому контенту, который должен быть добавлен в индекс, необходим идентификатор. Если у вашего контента нет идентификатора, вам необходимо создать его, передав индекс, количество или что-то еще в качестве идентификатора (настоятельно рекомендуется использовать значение из number
типа). Эти идентификаторы являются уникальными ссылками на определенный контент. Это важно при обновлении или добавлении контента через существующие идентификаторы. Если ссылки не являются проблемой, вы можете просто использовать что-нибудь простое, например count++
.
Индекс. добавить (идентификатор, строка)
index . add ( 0 , "John Doe" ) ;
Индекс. поиск (строка | параметры, <ограничение>, <опции>)
index . search ( "John" ) ;
Ограничьте результат:
index . search ( "John" , 10 ) ;
Вы можете проверить, был ли уже проиндексирован идентификатор:
if ( index . contain ( 1 ) ) {
console . log ( "ID is already in index" ) ;
}
Вы можете вызвать каждый метод в его асинхронной версии, например index.addAsync
или index.searchAsync
.
Вы можете назначить обратные вызовы для каждой асинхронной функции:
index . addAsync ( id , content , function ( ) {
console . log ( "Task Done" ) ;
} ) ;
index . searchAsync ( query , function ( result ) {
console . log ( "Results: " , result ) ;
} ) ;
Или не передавайте функцию обратного вызова и вместо этого возвращайте Promise
:
index . addAsync ( id , content ) . then ( function ( ) {
console . log ( "Task Done" ) ;
} ) ;
index . searchAsync ( query ) . then ( function ( result ) {
console . log ( "Results: " , result ) ;
} ) ;
Или используйте async
и await
:
async function add ( ) {
await index . addAsync ( id , content ) ;
console . log ( "Task Done" ) ;
}
async function search ( ) {
const results = await index . searchAsync ( query ) ;
console . log ( "Results: " , result ) ;
}
Вы можете добавить содержимое в существующий индекс, например:
index . append ( id , content ) ;
Это не приведет к перезаписи старого индексированного содержимого, как это происходит при выполнении index.update(id, content)
. Имейте в виду, что index.add(id, content)
также будет выполнять «обновление» под капотом, когда идентификатор уже индексируется.
Добавленное содержимое будет иметь свой собственный контекст, а также собственное полное resolution
. Таким образом, релевантность не суммируется, а приобретает собственный контекст.
Давайте возьмем этот пример:
index . add ( 0 , "some index" ) ;
index . append ( 0 , "some appended content" ) ;
index . add ( 1 , "some text" ) ;
index . append ( 1 , "index appended content" ) ;
Когда вы запрашиваете index.search("index")
вы получите идентификатор индекса 1 в качестве первой записи в результате, поскольку контекст для добавленных данных начинается с нуля (не добавляется в старый контекст), а здесь "index "-это первый термин.
Если вы не хотите такого поведения, просто используйте стандартный index.add(id, content)
и укажите полную длину содержимого.
Индекс. обновление (идентификатор, строка)
index . update ( 0 , "Max Miller" ) ;
Индекс. удалить (идентификатор)
index . remove ( 0 ) ;
Токенизатор разделяет слова/термины на компоненты или части.
Определите частный пользовательский токенизатор во время создания/инициализации:
var index = new FlexSearch ( {
tokenize : function ( str ) {
return str . split ( / s-/ / g ) ;
}
} ) ;
Функция токенизатора получает строку в качестве параметра и должна вернуть массив строк, представляющих слово или термин. В некоторых языках каждый символ является термином и не разделяется пробелами.
Стеммер: несколько лингвистических мутаций одного и того же слова (например, «бежать» и «бежать»).
Фильтр: черный список слов, которые вообще следует исключить из индексации (например, «и», «чтобы» или «быть»).
Назначьте частный пользовательский стеммер или фильтр во время создания/инициализации:
var index = new FlexSearch ( {
stemmer : {
// object {key: replacement}
"ational" : "ate" ,
"tional" : "tion" ,
"enci" : "ence" ,
"ing" : ""
} ,
filter : [
// array blacklist
"in" ,
"into" ,
"is" ,
"isn't" ,
"it" ,
"it's"
]
} ) ;
Использование пользовательского фильтра, например:
var index = new FlexSearch ( {
filter : function ( value ) {
// just add values with length > 1 to the index
return value . length > 1 ;
}
} ) ;
Или назначьте стеммер/фильтры глобально для языка:
Stemmer передаются как объект (пара ключ-значение), фильтр как массив.
FlexSearch . registerLanguage ( "us" , {
stemmer : { /* ... */ } ,
filter : [ /* ... */ ]
} ) ;
Или используйте заранее определенный стеммер или фильтр предпочитаемых вами языков:
< html >
< head >
< script src =" js/flexsearch.bundle.js " > </ script >
< script src =" js/lang/en.min.js " > </ script >
< script src =" js/lang/de.min.js " > </ script >
</ head >
...
Теперь вы можете назначить встроенный стеммер во время создания/инициализации:
var index_en = new FlexSearch . Index ( {
language : "en"
} ) ;
var index_de = new FlexSearch . Index ( {
language : "de"
} ) ;
В Node.js доступны все встроенные файлы языковых пакетов:
const { Index } = require ( "flexsearch" ) ;
var index_en = new Index ( {
language : "en"
} ) ;
При использовании RTL установите для токенизатора как минимум значение «обратный» или «полный».
Просто установите для поля «rtl» значение true и используйте совместимый токенизатор:
var index = new Index ( {
encode : str => str . toLowerCase ( ) . split ( / [^a-z]+ / ) ,
tokenize : "reverse" ,
rtl : true
} ) ;
Установите собственный токенизатор, который соответствует вашим потребностям, например:
var index = FlexSearch . create ( {
encode : str => str . replace ( / [x00-x7F] / g , "" ) . split ( "" )
} ) ;
Вы также можете передать специальную функцию кодировщика для применения некоторых лингвистических преобразований.
index . add ( 0 , "一个单词" ) ;
var results = index . search ( "单词" ) ;
Предположим, что наш документ имеет такую структуру данных:
{
"id" : 0 ,
"content" : " some text "
}
Старый синтаксис FlexSearch v0.6.3 ( больше не поддерживается! ):
const index = new Document ( {
doc : {
id : "id" ,
field : [ "content" ]
}
} ) ;
Дескриптор документа немного изменился, ветки
field
больше нет, вместо этого просто применяется один уровень выше, поэтомуkey
становится основным элементом параметров.
В новом синтаксисе поле «doc» было переименовано в document
, а поле «field» — в index
:
const index = new Document ( {
document : {
id : "id" ,
index : [ "content" ]
}
} ) ;
index . add ( {
id : 0 ,
content : "some text"
} ) ;
id
поля описывает, где в ваших документах находится идентификатор или уникальный ключ. Ключ по умолчанию получает значение id
по умолчанию, если он не передан, поэтому вы можете сократить приведенный выше пример до:
const index = new Document ( {
document : {
index : [ "content" ]
}
} ) ;
index
участника содержит список полей, которые вы хотите проиндексировать из своих документов. Просто выбрав одно поле, вы можете передать строку. При использовании id
ключа по умолчанию это сокращается до:
const index = new Document ( { document : "content" } ) ;
index . add ( { id : 0 , content : "some text" } ) ;
Предполагая, что у вас есть несколько полей, вы можете добавить несколько полей в индекс:
var docs = [ {
id : 0 ,
title : "Title A" ,
content : "Body A"
} , {
id : 1 ,
title : "Title B" ,
content : "Body B"
} ] ;
const index = new Document ( {
id : "id" ,
index : [ "title" , "content" ]
} ) ;
Вы можете передать пользовательские параметры для каждого поля:
const index = new Document ( {
id : "id" ,
index : [ {
field : "title" ,
tokenize : "forward" ,
optimize : true ,
resolution : 9
} , {
field : "content" ,
tokenize : "strict" ,
optimize : true ,
resolution : 5 ,
minlength : 3 ,
context : {
depth : 1 ,
resolution : 3
}
} ]
} ) ;
Параметры поля наследуются, если были переданы также глобальные параметры, например:
const index = new Document ( {
tokenize : "strict" ,
optimize : true ,
resolution : 9 ,
document : {
id : "id" ,
index : [ {
field : "title" ,
tokenize : "forward"
} , {
field : "content" ,
minlength : 3 ,
context : {
depth : 1 ,
resolution : 3
}
} ]
}
} ) ;
Примечание. Параметры контекста из поля «Содержимое» также наследуются соответствующими параметрами поля, тогда как параметры этого поля были унаследованы глобальным параметром.
Предположим, что массив документов выглядит более сложным (имеет вложенные ветки и т. д.), например:
{
"record" : {
"id" : 0 ,
"title" : " some title " ,
"content" : {
"header" : " some text " ,
"footer" : " some text "
}
}
}
Затем используйте обозначение root:child:child
разделенное двоеточиями, чтобы определить иерархию внутри дескриптора документа:
const index = new Document ( {
document : {
id : "record:id" ,
index : [
"record:title" ,
"record:content:header" ,
"record:content:footer"
]
}
} ) ;
Просто добавьте поля, к которым вы хотите выполнить запрос. Не добавляйте поля в индекс, вам просто нужен результат (но не запрос). Для этого вы можете хранить документы независимо от их индекса (читайте ниже).
Если вы хотите выполнить запрос через поле, вам необходимо передать точный ключ поля, который вы определили в doc
в качестве имени поля (с синтаксисом двоеточия):
index . search ( query , {
index : [
"record:title" ,
"record:content:header" ,
"record:content:footer"
]
} ) ;
То же, что:
index . search ( query , [
"record:title" ,
"record:content:header" ,
"record:content:footer"
] ) ;
Использование опций для конкретных полей:
index . search ( [ {
field : "record:title" ,
query : "some query" ,
limit : 100 ,
suggest : true
} , {
field : "record:title" ,
query : "some other query" ,
limit : 100 ,
suggest : true
} ] ) ;
Вы можете выполнить поиск по одному и тому же полю с разными запросами.
При передаче параметров для конкретных полей вам необходимо предоставить полную конфигурацию для каждого поля. Они не наследуются, как дескриптор документа.
При работе с документами необходимо соблюдать 2 правила:
[ // <-- not allowed as document start!
{
"id" : 0 ,
"title" : "title"
}
]
{
"records" : [ // <-- not allowed when ID or tag lives inside!
{
"id" : 0 ,
"title" : "title"
}
]
}
Вот пример поддерживаемого сложного документа:
{
"meta" : {
"tag" : " cat " ,
"id" : 0
},
"contents" : [
{
"body" : {
"title" : " some title " ,
"footer" : " some text "
},
"keywords" : [ " some " , " key " , " words " ]
},
{
"body" : {
"title" : " some title " ,
"footer" : " some text "
},
"keywords" : [ " some " , " key " , " words " ]
}
]
}
Соответствующий дескриптор документа (когда все поля должны быть проиндексированы) выглядит так:
const index = new Document ( {
document : {
id : "meta:id" ,
tag : "meta:tag" ,
index : [
"contents[]:body:title" ,
"contents[]:body:footer" ,
"contents[]:keywords"
]
}
} ) ;
Опять же, при поиске вам придется использовать ту же строку, разделенную двоеточиями, из определения поля.
index . search ( query , {
index : "contents[]:body:title"
} ) ;
Этот пример нарушает оба правила, приведенные выше:
[ // <-- not allowed as document start!
{
"tag" : "cat" ,
"records" : [ // <-- not allowed when ID or tag lives inside!
{
"id" : 0 ,
"body" : {
"title" : "some title" ,
"footer" : "some text"
} ,
"keywords" : [ "some" , "key" , "words" ]
} ,
{
"id" : 1 ,
"body" : {
"title" : "some title" ,
"footer" : "some text"
} ,
"keywords" : [ "some" , "key" , "words" ]
}
]
}
]
Вам нужно применить некоторую нормализацию структуры.
Обходной путь для такой структуры данных выглядит следующим образом:
const index = new Document ( {
document : {
id : "record:id" ,
tag : "tag" ,
index : [
"record:body:title" ,
"record:body:footer" ,
"record:body:keywords"
]
}
} ) ;
function add ( sequential_data ) {
for ( let x = 0 , data ; x < sequential_data . length ; x ++ ) {
data = sequential_data [ x ] ;
for ( let y = 0 , record ; y < data . records . length ; y ++ ) {
record = data . records [ y ] ;
index . add ( {
id : record . id ,
tag : data . tag ,
record : record
} ) ;
}
}
}
// now just use add() helper method as usual:
add ( [ {
// sequential structured data
// take the data example above
} ] ) ;
Вы можете пропустить первый цикл, если данные вашего документа имеют только один индекс в качестве внешнего массива.
Добавьте документ в индекс:
index . add ( {
id : 0 ,
title : "Foo" ,
content : "Bar"
} ) ;
Обновите индекс с помощью одного объекта или массива объектов:
index . update ( {
data : {
id : 0 ,
title : "Foo" ,
body : {
content : "Bar"
}
}
} ) ;
Удалить один объект или массив объектов из индекса:
index . remove ( docs ) ;
Если идентификатор известен, вы также можете просто удалить его (быстрее):
index . remove ( id ) ;
В приведенном выше сложном примере поле keywords
представляет собой массив, но здесь в разметке нет скобок, таких как keywords[]
. Это также обнаружит массив, но вместо добавления каждой записи в новый контекст массив будет объединен в большую строку и добавлен в индекс.
Разница обоих видов добавления содержимого массива заключается в релевантности при поиске. При добавлении каждого элемента массива с помощью append()
в его собственный контекст с использованием синтаксического field[]
релевантность последней записи совпадает с первой записью. Если вы оставили скобки в обозначениях, массив будет объединен в одну строку, разделенную пробелами. Здесь первая запись имеет наибольшую релевантность, тогда как последняя запись имеет наименьшую релевантность.
Итак, если предположить, что ключевые слова из приведенного выше примера предварительно отсортированы по релевантности их популярности, то вы хотите сохранить этот порядок (информацию об релевантности). Для этого не ставьте в обозначениях скобки. В противном случае записи будут приниматься в новом контексте оценки (старый порядок теряется).
Также вы можете оставить скобки для повышения производительности и уменьшения объема памяти. Используйте его, когда вам не нужна степень детализации релевантности записей.
Поиск по всем полям:
index . search ( query ) ;
Поиск по определенному полю:
index . search ( query , { index : "title" } ) ;
Поиск по заданному набору полей:
index . search ( query , { index : [ "title" , "content" ] } ) ;
То же, что:
index . search ( query , [ "title" , "content" ] ) ;
Передайте пользовательские модификаторы и запросы в каждое поле:
index . search ( [ {
field : "content" ,
query : "some query" ,
limit : 100 ,
suggest : true
} , {
field : "content" ,
query : "some other query" ,
limit : 100 ,
suggest : true
} ] ) ;
Вы можете выполнить поиск по одному и тому же полю с разными запросами.
Просмотрите все доступные параметры поиска по полям.
Схема набора результатов:
fields[] => { field, result[] => { document }}
Первый индекс представляет собой массив полей, к которым был применен запрос. Каждое из этих полей имеет запись (объект) с двумя свойствами «поле» и «результат». «Результат» также является массивом и включает результат для этого конкретного поля. Результатом может быть массив идентификаторов или обогащенный сохраненными данными документа.
Необогащенный набор результатов теперь выглядит так:
[ {
field : "title" ,
result : [ 0 , 1 , 2 ]
} , {
field : "content" ,
result : [ 3 , 4 , 5 ]
} ]
Расширенный набор результатов теперь выглядит так:
[ {
field : "title" ,
result : [
{ id : 0 , doc : { /* document */ } } ,
{ id : 1 , doc : { /* document */ } } ,
{ id : 2 , doc : { /* document */ } }
]
} , {
field : "content" ,
result : [
{ id : 3 , doc : { /* document */ } } ,
{ id : 4 , doc : { /* document */ } } ,
{ id : 5 , doc : { /* document */ } }
]
} ]
При использовании pluck
вместо «поля» вы можете явно выбрать только одно поле и получить плоское представление:
index . search ( query , { pluck : "title" , enrich : true } ) ;
[
{ id : 0 , doc : { /* document */ } } ,
{ id : 1 , doc : { /* document */ } } ,
{ id : 2 , doc : { /* document */ } }
]
Этот набор результатов является заменой «логического поиска». Вместо применения логической логики к вложенному объекту вы можете динамически применять свою логику поверх набора результатов. Это открывает огромные возможности по обработке результатов. Таким образом, результаты полей больше не объединяются в один результат. Это сохраняет некоторую важную информацию, такую как имя поля, а также релевантность результатов каждого поля, которые больше не смешиваются.
При поиске по полю по умолчанию применяется запрос с логической логикой «или». Каждое поле имеет свой результат для данного запроса.
Существует одна ситуация, когда свойство bool
все еще поддерживается. Если вы хотите переключить логику «или» по умолчанию из поиска полей на «и», например:
index . search ( query , {
index : [ "title" , "content" ] ,
bool : "and"
} ) ;
Вы просто получите результаты, содержащие запрос в обоих полях. Вот и все.
Как и key
для идентификатора, просто определите путь к тегу:
const index = new Document ( {
document : {
id : "id" ,
tag : "tag" ,
index : "content"
}
} ) ;
index . add ( {
id : 0 ,
tag : "cat" ,
content : "Some content ..."
} ) ;
Ваши данные также могут иметь несколько тегов в виде массива:
index . add ( {
id : 1 ,
tag : [ "animal" , "dog" ] ,
content : "Some content ..."
} ) ;
Вы можете выполнить поиск по конкретному тегу:
index . search ( query , {
index : "content" ,
tag : "animal"
} ) ;
Это просто дает вам результат, который был помечен данным тегом.
Используйте несколько тегов при поиске:
index . search ( query , {
index : "content" ,
tag : [ "cat" , "dog" ]
} ) ;
Это дает вам результат, помеченный одним из заданных тегов.
По умолчанию несколько тегов будут применяться как логическое «или». Просто нужно, чтобы один из тегов существовал.
Это еще одна ситуация, когда свойство bool
все еще поддерживается. Если вы хотите переключить логику «или» по умолчанию из поиска тегов на «и», например:
index . search ( query , {
index : "content" ,
tag : [ "dog" , "animal" ] ,
bool : "and"
} ) ;
Вы просто получите результаты, содержащие оба тега (в этом примере есть только одна запись с тегами «собака» и «животное»).
Вы также можете получить результаты из одного или нескольких тегов, если запрос не был передан:
index . search ( { tag : [ "cat" , "dog" ] } ) ;
В этом случае набор результатов выглядит так:
[ {
tag : "cat" ,
result : [ /* all cats */ ]
} , {
tag : "dog" ,
result : [ /* all dogs */ ]
} ]
По умолчанию каждый запрос ограничен 100 записями. Неограниченные запросы приводят к проблемам. Вам необходимо установить предел в качестве опции для регулировки размера.
Вы можете установить лимит и смещение для каждого запроса:
index . search ( query , { limit : 20 , offset : 100 } ) ;
Вы не можете предварительно подсчитать размер набора результатов. Это ограничение, заложенное в FlexSearch. Если вам действительно нужно подсчитать все результаты, которые вы можете просмотреть по страницам, просто назначьте достаточно высокий предел, верните все результаты и вручную примените смещение по страницам (это также работает на стороне сервера). FlexSearch достаточно быстр, поэтому это не проблема.
Только индекс документа может иметь хранилище. Вы можете использовать индекс документа вместо плоского индекса, чтобы получить эту функциональность даже при хранении только пар идентификатор-контент.
Вы можете самостоятельно определить, какие поля следует индексировать, а какие сохранять. Таким образом, вы можете индексировать поля, которые не должны включаться в результаты поиска.
Не используйте хранилище, если: 1. массив идентификаторов, поскольку результат достаточно хорош, или 2. содержимое/документы уже хранятся в другом месте (вне индекса).
Когда атрибут
store
установлен, вам необходимо включить все поля, которые должны храниться явно (действует как белый список).
Если атрибут
store
не установлен, исходный документ сохраняется как резервный.
Это добавит в магазин весь оригинальный контент:
const index = new Document ( {
document : {
index : "content" ,
store : true
}
} ) ;
index . add ( { id : 0 , content : "some text" } ) ;
Получить проиндексированные документы из магазина можно:
var data = index . get ( 1 ) ;
Вы можете обновить/изменить содержимое магазина напрямую, не меняя индекс:
index . set ( 1 , data ) ;
Чтобы обновить хранилище, а также обновить индекс, просто используйте index.update
, index.add
или index.append
.
Когда вы выполняете запрос, независимо от того, является ли это индексом документа или плоским индексом, вы всегда получаете массив идентификаторов.
При желании вы можете автоматически обогатить результаты запроса сохраненным содержимым:
index . search ( query , { enrich : true } ) ;
Ваши результаты теперь выглядят так:
[ {
id : 0 ,
doc : { /* content from store */ }
} , {
id : 1 ,
doc : { /* content from store */ }
} ]
Это добавит в хранилище только определенные поля из документа (идентификатор не обязательно хранить в хранилище):
const index = new Document ( {
document : {
index : "content" ,
store : [ "author" , "email" ]
}
} ) ;
index . add ( id , content ) ;
Вы можете самостоятельно настроить, что индексировать, а что хранить. Настоятельно рекомендуется использовать это всякий раз, когда вы можете.
Вот полезный пример настройки doc и store:
const index = new Document ( {
document : {
index : "content" ,
store : [ "author" , "email" ]
}
} ) ;
index . add ( {
id : 0 ,
author : "Jon Doe" ,
email : "[email protected]" ,
content : "Some content for the index ..."
} ) ;
Вы можете запросить содержимое и вместо этого получить сохраненные значения:
index . search ( "some content" , { enrich : true } ) ;
Ваши результаты теперь выглядят так:
[ {
field : "content" ,
result : [ {
id : 0 ,
doc : {
author : "Jon Doe" ,
email : "[email protected]" ,
}
} ]
} ]
Оба поля «автор» и «электронная почта» не индексируются.
Просто создайте цепочку таких методов, как:
var index = FlexSearch . create ( )
. addMatcher ( { 'â' : 'a' } )
. add ( 0 , 'foo' )
. add ( 1 , 'bar' ) ;
index . remove ( 0 ) . update ( 1 , 'foo' ) . add ( 2 , 'foobar' ) ;
Примечание. Эта функция по умолчанию отключена из-за расширенного использования памяти. Прочтите здесь, чтобы получить дополнительную информацию о том, как включить.
FlexSearch представляет новый механизм оценки, называемый контекстным поиском , который был изобретен Томасом Вилкерлингом, автором этой библиотеки. Контекстный поиск невероятно поднимает запросы на совершенно новый уровень, но также требует некоторой дополнительной памяти (в зависимости от глубины ). Основная идея этой концепции состоит в том, чтобы ограничить релевантность контекстом вместо расчета релевантности на всем расстоянии соответствующего документа. Таким образом, контекстный поиск также улучшает результаты релевантных запросов к большому объему текстовых данных.
Создайте индекс и используйте контекст по умолчанию:
var index = new FlexSearch ( {
tokenize : "strict" ,
context : true
} ) ;
Создайте индекс и примените пользовательские параметры для контекста:
var index = new FlexSearch ( {
tokenize : "strict" ,
context : {
resolution : 5 ,
depth : 3 ,
bidirectional : true
}
} ) ;
Контекстным индексом фактически поддерживается только токенизатор «строгий».
Контекстный индекс требует дополнительного объема памяти в зависимости от глубины.
Вам необходимо инициализировать кеш и его лимит во время создания индекса:
const index = new Index ( { cache : 100 } ) ;
const results = index . searchCache ( query ) ;
Распространенный сценарий использования кэша — автозаполнение или мгновенный поиск при наборе текста.
При передаче числа в качестве ограничения кэш автоматически балансирует сохраненные записи в зависимости от их популярности.
При простом использовании «true» кеш не ограничен и работает фактически в 2-3 раза быстрее (поскольку балансировщик не нужно запускать).
Новая модель работника из версии 0.7.0 разделена на «поля» документа (1 рабочий = 1 индекс поля). Таким образом, работник получает возможность полностью решать задачи (подзадачи). Недостатком этой парадигмы является то, что они могут быть не идеально сбалансированы при хранении содержимого (поля могут иметь разную длину содержимого). С другой стороны, нет никаких указаний на то, что балансировка хранилища дает какое-либо преимущество (все они требуют одинакового объема).
При использовании индекса документа просто примените опцию «рабочий»:
const index = new Document ( {
index : [ "tag" , "name" , "title" , "text" ] ,
worker : true
} ) ;
index . add ( {
id : 1 , tag : "cat" , name : "Tom" , title : "some" , text : "some"
} ) . add ( {
id : 2 , tag : "dog" , name : "Ben" , title : "title" , text : "content"
} ) . add ( {
id : 3 , tag : "cat" , name : "Max" , title : "to" , text : "to"
} ) . add ( {
id : 4 , tag : "dog" , name : "Tim" , title : "index" , text : "index"
} ) ;
Worker 1: { 1: "cat", 2: "dog", 3: "cat", 4: "dog" }
Worker 2: { 1: "Tom", 2: "Ben", 3: "Max", 4: "Tim" }
Worker 3: { 1: "some", 2: "title", 3: "to", 4: "index" }
Worker 4: { 1: "some", 2: "content", 3: "to", 4: "index" }
Когда вы выполняете поиск по всем полям, то эта задача прекрасно балансируется по всем воркерам, которые могут самостоятельно решать свои подзадачи.
Выше мы видели, что документы автоматически создают работника для каждого поля. Вы также можете создать WorkerIndex напрямую (так же, как использовать Index
вместо Document
).
Использовать в качестве модуля ES6:
import WorkerIndex from "./worker/index.js" ;
const index = new WorkerIndex ( options ) ;
index . add ( 1 , "some" )
. add ( 2 , "content" )
. add ( 3 , "to" )
. add ( 4 , "index" ) ;
Или когда вместо этого использовалась связанная версия:
var index = new FlexSearch . Worker ( options ) ;
index . add ( 1 , "some" )
. add ( 2 , "content" )
. add ( 3 , "to" )
. add ( 4 , "index" ) ;
Такой WorkerIndex работает почти так же, как созданный экземпляр Index
.
WorkerIndex поддерживает только
async
вариант всех методов. Это означает, что когда вы вызываетеindex.search()
для WorkerIndex, это также будет выполняться асинхронно, так же, как иindex.searchAsync()
.
Рабочая модель Node.js основана на «рабочих потоках» и работает точно так же:
const { Document } = require ( "flexsearch" ) ;
const index = new Document ( {
index : [ "tag" , "name" , "title" , "text" ] ,
worker : true
} ) ;
Или создайте один рабочий экземпляр для недокументированного индекса:
const { Worker } = require ( "flexsearch" ) ;
const index = new Worker ( { options } ) ;
Рабочий всегда будет работать как асинхронный. При вызове метода запроса вы всегда должны обрабатывать возвращаемое обещание (например, использовать await
) или передавать функцию обратного вызова в качестве последнего параметра.
const index = new Document ( {
index : [ "tag" , "name" , "title" , "text" ] ,
worker : true
} ) ;
Все запросы и подзадачи будут выполняться параллельно (приоритет «все задачи завершены»):
index . searchAsync ( query , callback ) ;
index . searchAsync ( query , callback ) ;
index . searchAsync ( query , callback ) ;
Также (отдайте приоритет «все задачи выполнены»):
index . searchAsync ( query ) . then ( callback ) ;
index . searchAsync ( query ) . then ( callback ) ;
index . searchAsync ( query ) . then ( callback ) ;
Или, когда у вас есть только один обратный вызов, когда все запросы выполнены, просто используйте Promise.all()
который также определяет приоритет «всех завершенных задач»:
Promise . all ( [
index . searchAsync ( query ) ,
index . searchAsync ( query ) ,
index . searchAsync ( query )
] ) . then ( callback ) ;
Внутри обратного вызова Promise.all()
вы также получите массив результатов в качестве первого параметра соответственно для каждого введенного вами запроса.
При использовании await
вы можете расставить приоритеты в заказе (установить приоритет «первой выполненной задачи») и решать запросы один за другим и просто обрабатывать подзадачи параллельно:
await index . searchAsync ( query ) ;
await index . searchAsync ( query ) ;
await index . searchAsync ( query ) ;
То же самое для index.add()
, index.append()
, index.remove()
или index.update()
. Здесь есть особый случай, который не отключается библиотекой, но о нем нужно помнить при использовании Workers.
Когда вы вызываете «синхронизированную» версию рабочего индекса:
index . add ( doc ) ;
index . add ( doc ) ;
index . add ( doc ) ;
// contents aren't indexed yet,
// they just queued on the message channel
Конечно, вы можете это сделать, но имейте в виду, что у основного потока нет дополнительной очереди для распределенных рабочих задач. Запуск их в длинном цикле массово отправляет контент в канал сообщений через внутренний метод worker.postMessage()
. К счастью, браузер и Node.js автоматически обрабатывают такие входящие задачи (при наличии достаточного количества свободной оперативной памяти). При использовании «синхронизированной» версии в рабочем индексе содержимое не индексируется на одну строку ниже, поскольку все вызовы по умолчанию обрабатываются как асинхронные.
При добавлении, обновлении или удалении больших объемов контента в индекс (или при высокой частоте) рекомендуется использовать асинхронную версию вместе с
async/await
чтобы снизить потребление памяти во время длительных процессов.
Экспорт немного изменился. Экспорт теперь состоит из нескольких более мелких частей, а не из одной большой партии. Вам нужно передать функцию обратного вызова, которая имеет два аргумента: «ключ» и «данные». Эта функция обратного вызова вызывается каждой частью, например:
index . export ( function ( key , data ) {
// you need to store both the key and the data!
// e.g. use the key for the filename and save your data
localStorage . setItem ( key , data ) ;
} ) ;
Экспорт данных в localStorage на самом деле не является хорошей практикой, но если размер не имеет значения, используйте его, если хотите. Экспорт в первую очередь предназначен для использования в Node.js или для хранения индексов, которые вы хотите делегировать с сервера клиенту.
Размер экспорта соответствует потреблению памяти библиотекой. Чтобы уменьшить размер экспорта, вам необходимо использовать конфигурацию, которая требует меньше памяти (используйте таблицу внизу, чтобы получить информацию о конфигурациях и распределении памяти).
Когда ваша процедура сохранения выполняется асинхронно, вам нужно вернуть обещание:
index . export ( function ( key , data ) {
return new Promise ( function ( resolve ) {
// do the saving as async
resolve ( ) ;
} ) ;
} ) ;
Вы не можете экспортировать дополнительную таблицу для функции fastupdate. Эта таблица состоит из ссылок, и при сохранении они полностью сериализуются и становятся слишком большими. Библиотека автоматически обработает это за вас. При импорте данных индекс автоматически отключает «fastupdate».
Прежде чем вы сможете импортировать данные, вам необходимо сначала создать индекс. Для индексов документов укажите тот же дескриптор документа, который вы использовали при экспорте данных. Эта конфигурация не сохраняется в экспорте.
var index = new Index ( { ... } ) ;
Чтобы импортировать данные, просто передайте ключ и данные:
index . import ( key , localStorage . getItem ( key ) ) ;
Вам необходимо импортировать каждый ключ! В противном случае ваш индекс не будет работать. Вам необходимо сохранить ключи из экспорта и использовать эти ключи для импорта (порядок ключей может отличаться).
Это сделано только для демонстрации и не рекомендуется, поскольку в вашем локальном хранилище могут быть другие ключи, которые не поддерживаются при импорте:
var keys = Object . keys ( localStorage ) ;
for ( let i = 0 , key ; i < keys . length ; i ++ ) {
key = keys [ i ] ;
index . import ( key , localStorage . getItem ( key ) ) ;
}
Определения, специфичные для языка, делятся на две группы:
function(string):string[]
boolean
{string: string}
{string: string}
string[]
Кодировка содержит логику кодирования, язык содержит стеммер, фильтр стоп-слов и средства сопоставления. Определения нескольких языков могут использовать один и тот же кодировщик кодировки. Кроме того, такое разделение позволяет вам управлять различными определениями языков для особых случаев использования (например, имена, города, диалекты/сленг и т. д.).
Чтобы полностью описать пользовательский язык на лету, вам необходимо передать:
const index = FlexSearch ( {
// mandatory:
encode : ( content ) => [ words ] ,
// optionally:
rtl : false ,
stemmer : { } ,
matcher : { } ,
filter : [ ]
} ) ;
Если параметр не передается, по умолчанию используется схема latin:default
.
Поле | Категория | Описание |
кодировать | кодировка | Функция кодировщика. Должен возвращать массив разделенных слов (или пустую строку). |
ртл | кодировка | Логическое свойство, указывающее кодировку справа налево. |
фильтр | язык | Фильтры также известны как «стоп-слова», они полностью отфильтровывают слова от индексации. |
стеммер | язык | Стеммер удаляет окончания слов и представляет собой своего рода «частичную нормализацию». Окончание слова только что совпало, если длина слова больше, чем совпавшая часть. |
совпадение | язык | Matcher заменяет все вхождения данной строки независимо от ее позиции, а также является своего рода «частичной нормализацией». |
Самый простой способ назначить кодировку конкретной кодировки/языка через модули:
import charset from "./dist/module/lang/latin/advanced.js" ;
import lang from "./dist/module/lang/en.js" ;
const index = FlexSearch ( {
charset : charset ,
lang : lang
} ) ;
Просто импортируйте экспорт по умолчанию для каждого модуля и назначьте его соответствующим образом.
Полный квалифицированный пример выше:
import { encode , rtl } from "./dist/module/lang/latin/advanced.js" ;
import { stemmer , filter , matcher } from "./dist/module/lang/en.js" ;
const index = FlexSearch ( {
encode : encode ,
rtl : rtl ,
stemmer : stemmer ,
matcher : matcher ,
filter : filter
} ) ;
Приведенный выше пример представляет собой стандартный интерфейс, который как минимум экспортируется из каждой кодировки/языка.
Вы также можете определить кодировщик напрямую, оставив все остальные параметры:
import simple from "./dist/module/lang/latin/simple.js" ;
const index = FlexSearch ( {
encode : simple
} ) ;
Вы можете назначить кодировку, передав ее во время инициализации, например, charset: "latin"
для кодера кодировки по умолчанию или charset: "latin:soundex"
для варианта кодера.
Определения языка (особенно средства сопоставления) также могут использоваться для нормализации диалекта и сленга определенного языка.
Вам необходимо сделать определения кодировки и/или языка доступными:
flexsearch.bundle.js
по умолчанию, но определения для конкретного языка не включены./dist/lang/
(файлы относятся к языкам, папки — к кодировкам)При загрузке языковых пакетов убедитесь, что библиотека была загружена ранее:
< script src =" dist/flexsearch.light.js " > </ script >
< script src =" dist/lang/latin/default.min.js " > </ script >
< script src =" dist/lang/en.min.js " > </ script >
При использовании полной версии «пакет» встроенные латинские кодеры уже включены, и вам просто нужно загрузить языковой файл:
< script src =" dist/flexsearch.bundle.js " > </ script >
< script src =" dist/lang/en.min.js " > </ script >
Поскольку вы загружаете пакеты в виде внешних пакетов (не ES6-модулей), вы должны инициализировать их с помощью ярлыков:
const index = FlexSearch ( {
charset : "latin:soundex" ,
lang : "en"
} ) ;
Используйте
charset:variant
нотации, чтобы назначить Charset и его варианты. При прохождении Charset без варианта автоматически решается какcharset:default
.
Вы также можете переопределить существующие определения, например:
const index = FlexSearch ( {
charset : "latin" ,
lang : "en" ,
matcher : { }
} ) ;
Пропускаемые определения не будут расширять определения по умолчанию, они заменит их.
Если вы любите расширить определение, просто создайте новый языковой файл и вставьте всю логику.
Это довольно просто при использовании варианта энкодера:
< script src =" dist/flexsearch.light.js " > </ script >
< script src =" dist/lang/latin/advanced.min.js " > </ script >
< script src =" dist/lang/latin/extra.min.js " > </ script >
< script src =" dist/lang/en.min.js " > </ script >
При использовании полной версии «пакет» встроенные латинские кодеры уже включены, и вам просто нужно загрузить языковой файл:
< script src =" dist/flexsearch.bundle.js " > </ script >
< script src =" dist/lang/en.min.js " > </ script >
const index_advanced = FlexSearch ( {
charset : "latin:advanced"
} ) ;
const index_extra = FlexSearch ( {
charset : "latin:extra"
} ) ;
В FlexSearch вы не можете предоставить свой собственный частичный токенизатор, потому что это прямая зависимость от основного устройства. Встроенный токенизатор FlexSearch разбивает каждое слово в фрагменты по разным узорам:
Это конвейер по умолчанию, предоставленный FlexSearch:
Сначала посмотрите на трубопровод по умолчанию в src/common.js
. Это очень просто и просто. Трубопровод будет обрабатываться как какая -то инверсия управления, окончательная реализация энкодера должна обрабатывать charset, а также специфичные для языковых преобразования. Этот обходной путь остался от многих тестов.
Введите трубопровод по умолчанию, например:
this . pipeline (
/* string: */ str . toLowerCase ( ) ,
/* normalize: */ false ,
/* split: */ split ,
/* collapse: */ false
) ;
Используйте схему трубопровода сверху, чтобы понять итерацию и разницу в предварительном кодировании и после кодирования. Ствол и маткеры должны применяться после нормализации Charset, но до преобразования языка также фильтры также.
Вот хороший пример расширения трубопроводов: src/lang/latin/extra.js
→ src/lang/latin/advanced.js
→ src/lang/latin/simple.js
.
Ищите свой язык в src/lang/
, если он существует, вы можете расширить или предоставить варианты (например, диалект/сленг). Если язык не существует, создайте новый файл и проверьте, соответствует ли какой -либо из существующих Charsets (например, латинский) вашему языку. Когда не существует Charset, вам необходимо предоставить Charset в качестве базы для языка.
Новый Charset должен предоставить хотя бы:
encode
функцию, которая нормализует Charset прошедшего текстового содержимого (удалите специальные ChARS, язычные преобразования и т. Д.) И возвращает массив отдельных слов . Также необходимо применять ствол, матлер или остановки фильтра. Когда на языке нет слов, чтобы обеспечить что -то похожее, например, каждый китайский знак также может быть «словом». Не возвращайте весь текстовый контент без разделения.rtl
логический флаг, который указывает кодирование справа на летоПо сути, Charset необходимо, чтобы просто обеспечить функцию энкодера вместе с индикатором для кодирования правого на лето:
export function encode ( str ) { return [ str ] }
export const rtl = false ;
Справочная строка: "Björn-Phillipp Mayer"
Запрос | по умолчанию | простой | передовой | дополнительный |
Бьорн | да | да | да | да |
Бьёр | да | да | да | да |
Бьёрн | нет | да | да | да |
Bjoern | нет | нет | да | да |
Филипп | нет | нет | да | да |
филип | нет | нет | да | да |
Björnphillip | нет | да | да | да |
Мейер | нет | нет | да | да |
Бьорн Мейер | нет | нет | да | да |
Meier fhilip | нет | нет | да | да |
Byorn Mair | нет | нет | нет | да |
(ложные срабатывания) | нет | нет | нет | да |
Книга «Путешествие Гулливера Свифт Джонатан 1726» была полностью проиндексирована для примеров ниже.
Самая оптимизированная память значимая настройка будет выделять всего 1,2 МБ для всей книги, индексированной! Это, наверное, самый крошечный след памяти, который вы получите из библиотеки поиска.
import { encode } from "./lang/latin/extra.js" ;
index = new Index ( {
encode : encode ,
tokenize : "strict" ,
optimize : true ,
resolution : 1 ,
minlength : 3 ,
fastupdate : false ,
context : false
} ) ;
Книга «Путешествия Гулливера» (Swift Jonathan 1726) была полностью проиндексирована для этого теста:
По умолчанию лексический индекс очень маленький:
depth: 0, bidirectional: 0, resolution: 3, minlength: 0
=> 2,1 МБ
Более высокое разрешение увеличит распределение памяти:
depth: 0, bidirectional: 0, resolution: 9, minlength: 0
=> 2,9 МБ
Использование контекстуального индекса увеличит распределение памяти:
depth: 1, bidirectional: 0, resolution: 9, minlength: 0
=> 12,5 МБ
Более высокая контекстуальная глубина увеличит распределение памяти:
depth: 2, bidirectional: 0, resolution: 9, minlength: 0
=> 21,5 МБ
Более высокая длина minle будет уменьшить распределение памяти:
depth: 2, bidirectional: 0, resolution: 9, minlength: 3
=> 19,0 МБ
Использование двунаправленного распределения памяти:
depth: 2, bidirectional: 1, resolution: 9, minlength: 3
=> 17,9 МБ
Включить вариант «FastuPdate» увеличит распределение памяти:
depth: 2, bidirectional: 1, resolution: 9, minlength: 3
=> 6,3 МБ
Каждая поисковая библиотека постоянно конкурирует с этими 4 свойствами:
FlexSearch предоставляет вам много параметров, которые вы можете использовать для настройки оптимального баланса для вашего конкретного варианта использования.
Модификатор | Влияние памяти * | Воздействие на производительность ** | Сопоставление воздействия ** | Воздействие на счет ** |
разрешение | +1 (на уровень) | +1 (на уровень) | 0 | +2 (на уровень) |
глубина | +4 (на уровень) | -1 (на уровень) | -10 + глубина | +10 |
Minlength | -2 (на уровень) | +2 (на уровень) | -3 (на уровень) | +2 (на уровень) |
двунаправленный | -2 | 0 | +3 | -1 |
Fastupdate | +1 | +10 (обновление, удалить) | 0 | 0 |
Оптимизировать: правда | -7 | -1 | 0 | -3 |
Encoder: "icase" | 0 | 0 | 0 | 0 |
Encoder: "просто" | -2 | -1 | +2 | 0 |
Encoder: "Advanced" | -3 | -2 | +4 | 0 |
Encoder: "Extra" | -5 | -5 | +6 | 0 |
Encoder: "Soundex" | -6 | -2 | +8 | 0 |
Токенизировать: "строго" | 0 | 0 | 0 | 0 |
Токенизировать: "вперед" | +3 | -2 | +5 | 0 |
Токенизировать: «Реверс» | +5 | -4 | +7 | 0 |
Токенизировать: "Полный" | +8 | -5 | +10 | 0 |
Индекс документов | +3 (за поле) | -1 (на поле) | 0 | 0 |
Теги документов | +1 (за тег) | -1 (за тег) | 0 | 0 |
Магазин: правда | +5 (за документ) | 0 | 0 | 0 |
Магазин: [Поля] | +1 (на поле) | 0 | 0 | 0 |
Кэш: верно | +10 | +10 | 0 | 0 |
Кэш: 100 | +1 | +9 | 0 | 0 |
Тип идентификаторов: номер | 0 | 0 | 0 | 0 |
Тип идентификаторов: строка | +3 | -3 | 0 | 0 |
memory
(первичная оптимизация для памяти)performance
(первичная оптимизация для производительности)match
(первичная оптимизация для соответствия)score
(первичная оптимизация для оценки)default
(сбалансированный профиль по умолчанию)Эти профили охватывают стандартные варианты использования. Рекомендуется применять пользовательскую конфигурацию вместо использования профилей, чтобы получить максимальную отдачу для вашей ситуации. Каждый профиль может быть оптимизирован дальше для его конкретной задачи, например, оптимизированная конфигурация или экстремальная память с экстремальными характеристиками и так далее.
Вы можете передать предустановку во время создания/инициализации индекса.
Рекомендуется использовать числовые значения идентификатора в качестве ссылки при добавлении контента в индекс. Длина байта проходящих идентификаторов значительно влияет на потребление памяти. Если это невозможно, вам следует рассмотреть возможность использовать таблицу индексов и отобразить идентификаторы с индексами, это становится важным, особенно при использовании контекстных индексов на большом объеме содержания.
Когда вы можете, старайтесь разделить контент по категориям и добавить его в свой собственный индекс, например:
var action = new FlexSearch ( ) ;
var adventure = new FlexSearch ( ) ;
var comedy = new FlexSearch ( ) ;
Таким образом, вы также можете предоставить различные настройки для каждой категории. Это на самом деле самый быстрый способ выполнить нечеткий поиск.
Чтобы сделать этот обходной путь более продленным, вы можете использовать короткого помощника:
var index = { } ;
function add ( id , cat , content ) {
( index [ cat ] || (
index [ cat ] = new FlexSearch
) ) . add ( id , content ) ;
}
function search ( cat , query ) {
return index [ cat ] ?
index [ cat ] . search ( query ) : [ ] ;
}
Добавьте контент в индекс:
add ( 1 , "action" , "Movie Title" ) ;
add ( 2 , "adventure" , "Movie Title" ) ;
add ( 3 , "comedy" , "Movie Title" ) ;
Выполните запросы:
var results = search ( "action" , "movie title" ) ; // --> [1]
Индексы разделения по категориям значительно повышают производительность.
Copyright 2018-2023 Томас Уилкерлинг, организованный NextApps Gmbh
Выпущено по лицензии Apache 2.0