Manticore Search 2.x сохраняет совместимость с Sphinxsearch 2.x и может загружать существующие таблицы, созданные Sphinxsearch. В большинстве случаев обновление сводится к замене бинарных файлов.
Вместо sphinx.conf (в Linux обычно находится в /etc/sphinxsearch/sphinx.conf) Manticore по умолчанию использует /etc/manticoresearch/manticore.conf. Он также работает под другим пользователем и использует другие папки.
Имя службы systemd изменилось с sphinx/sphinxsearch на manticore, и служба работает под пользователем manticore (Sphinx использовал sphinx или sphinxsearch). Также используется другая папка для файла PID.
Папки, используемые по умолчанию: /var/lib/manticore, /var/log/manticore, /var/run/manticore. Вы всё ещё можете использовать существующую конфигурацию Sphinx, но вам нужно вручную изменить права доступа к папкам /var/lib/sphinxsearch и /var/log/sphinxsearch. Или просто глобально переименовать 'sphinx' в 'manticore' в системных файлах. Если вы используете другие папки (для данных, файлов wordforms и т.д.), владение ими также должно быть переключено на пользователя manticore. Местоположение pid_file следует изменить, чтобы оно соответствовало manticore.service на /run/manticore/searchd.pid.
Если вы хотите использовать папку Manticore, файлы таблиц нужно переместить в новую папку данных (/var/lib/manticore), и права доступа должны быть изменены на пользователя manticore.
Обновление с Sphinx / Manticore 2.x до 3.x не является простым, так как движок хранения таблиц претерпел значительное обновление, и новый searchd не может загружать старые таблицы и обновлять их в новый формат на лету.
Manticore Search 3 получил переработанное хранение таблиц. Таблицы, созданные с Manticore/Sphinx 2.x, не могут быть загружены Manticore Search 3 без конвертации. Из-за ограничения в 4 ГБ, таблица реального времени в 2.x могла иметь несколько дисковых чанков после операции оптимизации. После обновления до 3.x эти таблицы теперь можно оптимизировать до одного дискового чанка с помощью обычной команды OPTIMIZE. Файлы индексов также изменились. Единственный компонент, который не претерпел структурных изменений — это файл .spp (hitlists). .sps (строки/json) и .spm (MVA) теперь содержатся в .spb (атрибуты переменной длины). Новый формат содержит файл .spm, но он используется для карты строк (ранее он был предназначен для MVA атрибутов). Добавлены новые расширения: .spt (поиск docid), .sphi (гистограммы вторичных индексов), .spds (хранение документов). Если вы используете скрипты, которые манипулируют файлами таблиц, их следует адаптировать под новые расширения файлов.
Процедура обновления может отличаться в зависимости от вашей конфигурации (число серверов в кластере, наличие высокой доступности и т.д.), но в общем случае она включает создание новых версий таблиц 3.x и замену существующих, а также замену старых бинарных файлов 2.x на новые.
Есть два специальных требования, которые нужно учесть:
- Таблицы реального времени нужно сбросить с помощью FLUSH RAMCHUNK
- Обычные таблицы с kill-листами требуют добавления новой директивы в конфигурацию таблицы (см. killlist_target)
Manticore Search 3 включает новый инструмент — index_converter — который может конвертировать таблицы Sphinx 2.x / Manticore 2.x в формат 3.x. index_converter поставляется в отдельном пакете, который нужно установить первым. С помощью этого инструмента создайте версии таблиц 3.x. index_converter может записывать новые файлы в существующую папку данных и создавать резервные копии старых файлов, либо записывать новые файлы в выбранную папку.
Если у вас один сервер:
- Установите пакет manticore-converter
- Используйте index_converter для создания новых версий таблиц в другой папке, отличной от существующей папки данных (с помощью опции
--output-dir) - Остановите существующий Manticore/Sphinx, обновитесь до 3.0, переместите новые таблицы в папку данных и запустите Manticore
Чтобы минимизировать время простоя, вы можете скопировать таблицы 2.x, конфигурацию (вам нужно будет отредактировать пути для таблиц, логов и разных портов) и бинарные файлы в отдельное место и запустить это на отдельном порту. Направьте ваше приложение на него. После обновления до 3.0 и запуска нового сервера вы можете вернуть приложение к обычным портам. Если всё работает хорошо, остановите копию 2.x и удалите файлы, чтобы освободить место.
Если у вас есть запасной сервер (например, тестовый или staging сервер), вы можете сначала выполнить обновление таблиц там и даже установить Manticore 3 для проведения нескольких тестов. Если всё в порядке, скопируйте новые файлы таблиц на продуктивный сервер. Если у вас несколько серверов, которые можно вывести из эксплуатации, делайте это по одному и выполняйте обновление на каждом. Для распределённых конфигураций searchd 2.x может работать как мастер с узлами 3.x, так что вы можете сначала обновить узлы данных, а затем мастер-узел.
Изменений в способе подключения клиентов к движку, режиме запросов или поведении запросов не было.
Kill-листы были переработаны в Manticore Search 3. В предыдущих версиях kill-листы применялись к результирующему набору, предоставленному каждой ранее обработанной таблицей во время выполнения запроса.
Таким образом, в 2.x порядок таблиц во время запроса имел значение. Например, если у дельта-таблицы был kill-лист, чтобы применить его к основной таблице, порядок должен был быть основной, дельта (либо в распределённой таблице, либо в операторе FROM).
В Manticore 3 kill-листы применяются к таблице при её загрузке во время запуска searchd или при её ротации. Новая директива killlist_target в конфигурации таблицы указывает целевые таблицы и определяет, какие doc id из исходной таблицы должны использоваться для подавления. Это могут быть id из определённого kill-листа, фактические doc id таблицы или оба варианта.
Документы из kill-листов удаляются из целевых таблиц, они не возвращаются в результатах даже если поиск не включает таблицу, которая предоставила kill-листы. Из-за этого порядок таблиц для поиска больше не имеет значения. Теперь delta, main и main, delta дадут одинаковые результаты.
В предыдущих версиях таблицы ротировались в порядке, указанном в конфигурационном файле. В Manticore 3 порядок ротации таблиц стал гораздо умнее и работает в соответствии с целями killlist. Перед началом ротации таблиц сервер ищет цепочки таблиц по определениям killlist_target. Затем он сначала ротирует таблицы, которые нигде не упоминаются как цели kill-листов. Далее он ротирует таблицы, на которые ссылаются уже ротированные таблицы, и так далее. Например, если мы делаем indexer --all и у нас есть 3 таблицы: main, delta_big (который нацелен на main) и delta_small (с целью на delta_big), сначала ротируется delta_small, затем delta_big и наконец main. Это делается для того, чтобы при ротации зависимой таблицы она получала самый актуальный kill-лист из других таблиц.
docinfo- теперь всё externinplace_docinfo_gap- больше не нуженmva_updates_pool- MVAs больше не имеют выделенного пула для обновлений, так как теперь их можно обновлять напрямую в блобе (см. ниже).
Строковые, JSON и MVA атрибуты можно обновлять в Manticore 3.x с помощью оператора UPDATE.
В версии 2.x строковые атрибуты требовали REPLACE, для JSON можно было обновлять только скалярные свойства (так как они были фиксированной ширины), а MVAs можно было обновлять с помощью пула MVA. Теперь обновления выполняются напрямую в компоненте блоба. Одной из настроек, которую может потребоваться подстроить, является attr_update_reserve, которая позволяет изменять выделенное дополнительное пространство в конце блоба, используемое для избежания частых переразмериваний, если новые значения больше существующих в блобе.
Doc id раньше были беззнаковыми 64-битными целыми числами. Теперь они являются положительными знаковыми 64-битными целыми числами.
Читайте здесь про RT режим
Manticore 3.x распознаёт и парсит специальные суффиксы, что облегчает использование числовых значений со специальным значением. Общая форма — целое число + литерал, например 10k или 100d, но не 40.3s (так как 40.3 не целое), или не 2d 4h (так как это два значения, а не одно). Литералы не чувствительны к регистру, поэтому 10W — то же, что 10w. В настоящее время поддерживаются 2 типа таких суффиксов:
- Суффиксы размера — могут использоваться в параметрах, определяющих размер чего-либо (буфер памяти, файл на диске, лимит ОЗУ и т.п.) в байтах. «Голые» числа в таких местах означают буквально размер в байтах (октетах). Суффиксы размера:
kдля килобайт (1k=1024),mдля мегабайт (1m=1024k),gдля гигабайт (1g=1024m) иtдля терабайт (1t=1024g). - Суффиксы времени — могут использоваться в параметрах, задающих временные интервалы, такие как задержки, таймауты и т.п. «Голые» значения для таких параметров обычно имеют документированную шкалу, и вы должны знать, означает ли число, скажем, 100 — «100 секунд» или «100 миллисекунд». Однако вместо догадок можно просто написать значение с суффиксом, и оно будет однозначно определено своим суффиксом. Суффиксы времени:
usдля микросекунд,msдля миллисекунд,sдля секунд,mдля минут,hдля часов,dдля дней иwдля недель.
index_converter — это инструмент для конвертации таблиц, созданных в Sphinx/Manticore Search 2.x, в формат таблиц Manticore Search 3.x. Инструмент можно использовать несколькими способами:
$ index_converter --config /home/myuser/manticore.conf --index tablename
$ index_converter --config /home/myuser/manticore.conf --all
$ index_converter --path /var/lib/manticoresearch/data --all
Новая версия таблицы по умолчанию записывается в ту же папку. Файлы предыдущей версии сохраняются с расширением .old в имени. Исключение — файл .spp (hitlists), который является единственным компонентом таблицы, не изменившимся в новом формате.
Вы можете сохранить новую версию таблицы в другую папку, используя опцию -–output-dir
$ index_converter --config /home/myuser/manticore.conf --all --output-dir /new/path
Особый случай — таблицы, содержащие kill-листы. Поскольку поведение kill-листов изменилось (см. killlist_target), дельта-таблица должна знать, какие таблицы являются целевыми для применения kill-листов. Есть 3 способа получить конвертированную таблицу, готовую для установки целевых таблиц для применения kill-листов:
- Использовать
-–killlist-targetпри конвертации таблицы$ index_converter --config /home/myuser/manticore.conf --index deltaindex --killlist-target mainindex:kl - Добавить killlist_target в конфигурацию перед конвертацией
- Использовать команду ALTER ... KILLIST_TARGET после конвертации
Вот полный список опций index_converter:
--config <file>(-c <file>для краткости) указывает index_converter использовать данный файл в качестве конфигурации. Обычно он ищет manticore.conf в каталоге установки (например,/usr/local/manticore/etc/manticore.conf, если установлен в/usr/local/sphinx), затем в текущем каталоге, в котором вы вызываете index_converter из оболочки.--indexуказывает, какую таблицу следует конвертировать--path- вместо использования конфигурационного файла можно указать путь, содержащий таблицу(ы)--strip-path- удаляет путь из имён файлов, на которые ссылается таблица: стоп-слова, исключения и словоформы--large-docid- позволяет конвертировать документы с идентификаторами больше 2^63 и выводить предупреждение, иначе при большом идентификаторе программа просто завершится с ошибкой. Эта опция была добавлена, так как в Manticore 3.x идентификаторы документов имеют тип signed bigint, тогда как ранее они были unsigned--output-dir <dir>- записывает новые файлы в выбранную папку, а не в то же место, где находятся существующие файлы таблиц. При установке этой опции существующие файлы таблиц останутся нетронутыми на своих местах.--all- конвертирует все таблицы из конфигурации--killlist-target <targets>задаёт целевые таблицы, к которым будут применяться kill-листы. Эта опция должна использоваться только вместе с опцией--index
Вы можете легко установить и запустить Manticore на различных операционных системах, включая Ubuntu, Centos, Debian, Windows и MacOS. Кроме того, вы также можете использовать Manticore в виде Docker-контейнера.
- Ubuntu
- Debian
- Centos
- Windows
- MacOS
- Docker
wget https://repo.manticoresearch.com/manticore-repo.noarch.deb
sudo dpkg -i manticore-repo.noarch.deb
sudo apt update
sudo apt install manticore manticore-columnar-lib
sudo systemctl start manticorewget https://repo.manticoresearch.com/manticore-repo.noarch.deb
sudo dpkg -i manticore-repo.noarch.deb
sudo apt update
sudo apt install manticore manticore-columnar-lib
sudo systemctl start manticoresudo yum install https://repo.manticoresearch.com/manticore-repo.noarch.rpm
sudo yum install manticore manticore-columnar-lib
sudo systemctl start manticore- Скачайте архив для Windows с https://manticoresearch.com/install/
- Распакуйте все файлы из архива в
C:\Manticore - Выполните следующую команду для установки Manticore как службы:
-
C:\Manticore\bin\searchd --install --config C:\Manticore\sphinx.conf.in --servicename Manticore - Запустите Manticore через оснастку служб Microsoft Management Console.
brew install manticoresearch
brew services start manticoresearchdocker pull manticoresearch/manticore
docker run --name manticore -p9306:9306 -p9308:9308 -p9312:9312 -d manticoresearch/manticoreДля сохранения данных вашего каталога, прочитайте как использовать Manticore docker в продакшене
По умолчанию Manticore ожидает подключения на:
- порт 9306 для клиентов MySQL
- порт 9308 для HTTP/HTTPS соединений
- порт 9312 для соединений с другими узлами Manticore и клиентами, использующими бинарный API Manticore
Более подробную информацию о поддержке HTTPS можно найти в нашем обучающем курсе здесь.
- SQL
- HTTP
- PHP
- Python
- Python-asyncio
- Javascript
- Java
- C#
- Rust
- Typescript
- Go
mysql -h0 -P9306HTTP — это безсостояний протокол, поэтому он не требует специальной фазы подключения. Вы можете просто отправить HTTP-запрос на сервер и получить ответ. Для общения с Manticore через JSON-интерфейс вы можете использовать любую HTTP клиентскую библиотеку на выбранном вами языке программирования для отправки GET или POST запросов на сервер и парсинга JSON ответов:
curl -s "http://localhost:9308/search"// https://github.com/manticoresoftware/manticoresearch-php
require_once __DIR__ . '/vendor/autoload.php';
$config = ['host'=>'127.0.0.1','port'=>9308];
$client = new \Manticoresearch\Client($config);// https://github.com/manticoresoftware/manticoresearch-python
import manticoresearch
config = manticoresearch.Configuration(
host = "http://127.0.0.1:9308"
)
client = manticoresearch.ApiClient(config)
indexApi = manticoresearch.IndexApi(client)
searchApi = manticoresearch.SearchApi(client)
utilsApi = manticoresearch.UtilsApi(client)// https://github.com/manticoresoftware/manticoresearch-python-asyncio
import manticoresearch
config = manticoresearch.Configuration(
host = "http://127.0.0.1:9308"
)
async with manticoresearch.ApiClient(config) as client:
indexApi = manticoresearch.IndexApi(client)
searchApi = manticoresearch.searchApi(client)
utilsApi = manticoresearch.UtilsApi(client)// https://github.com/manticoresoftware/manticoresearch-javascript
var Manticoresearch = require('manticoresearch');
var client= new Manticoresearch.ApiClient()
client.basePath="http://127.0.0.1:9308";
indexApi = new Manticoresearch.IndexApi(client);
searchApi = new Manticoresearch.SearchApi(client);
utilsApi = new Manticoresearch.UtilsApi(client);// https://github.com/manticoresoftware/manticoresearch-java
import com.manticoresearch.client.*;
import com.manticoresearch.client.model.*;
import com.manticoresearch.client.api.*;
...
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("http://127.0.0.1:9308");
...
IndexApi indexApi = new IndexApi(client);
SearchApi searchApi = new UtilsApi(client);
UtilsApi utilsApi = new UtilsApi(client);// https://github.com/manticoresoftware/manticoresearch-net
using System.Net.Http;
...
using ManticoreSearch.Client;
using ManticoreSearch.Api;
using ManticoreSearch.Model;
...
config = new Configuration();
config.BasePath = "http://localhost:9308";
httpClient = new HttpClient();
httpClientHandler = new HttpClientHandler();
...
var indexApi = new IndexApi(httpClient, config, httpClientHandler);
var searchApi = new SearchApi(httpClient, config, httpClientHandler);
var utilsApi = new UtilsApi(httpClient, config, httpClientHandler);// https://github.com/manticoresoftware/manticoresearch-rust
use std::sync::Arc;
use manticoresearch::{
apis::{
{configuration::Configuration,IndexApi,IndexApiClient,SearchApi,SearchApiClient,UtilsApi,UtilsApiClient}
},
};
async fn maticore_connect {
let configuration = Configuration {
base_path: "http://127.0.0.1:9308".to_owned(),
..Default::default(),
};
let api_config = Arc::new(configuration);
let utils_api = UtilsApiClient::new(api_config.clone());
let index_api = IndexApiClient::new(api_config.clone());
let search_api = SearchApiClient::new(api_config.clone());import {
Configuration,
IndexApi,
SearchApi,
UtilsApi
} from "manticoresearch-ts";
...
const config = new Configuration({
basePath: 'http://localhost:9308',
})
const indexApi = new IndexApi(config);
const searchApi = new SearchApi(config);
const utilsApi = new UtilsApi(config);import (
"context"
manticoreclient "github.com/manticoresoftware/manticoresearch-go"
)
...
configuration := manticoreclient.NewConfiguration()
configuration.Servers[0].URL = "http://localhost:9308"
apiClient := manticoreclient.NewAPIClient(configuration)Давайте теперь создадим таблицу с названием "products" с 2 полями:
- title - полнотекстовое поле, которое будет содержать название нашего продукта
- price - типа "float"
Обратите внимание, что можно не создавать таблицу явно с помощью оператора create. Для дополнительной информации смотрите Автоматическая схема.
Больше информации о различных способах создания таблиц можно найти в наших обучающих курсах:
- SQL
- HTTP
- PHP
- Python
- Python-asyncio
- Javascript
- Java
- C#
- Rust
- TypeScript
- Go
create table products(title text, price float) morphology='stem_en';POST /cli -d "create table products(title text, price float) morphology='stem_en'"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'price'=>['type'=>'float'],
],['morphology' => 'stem_en']);utilsApi.sql('create table products(title text, price float) morphology=\'stem_en\'')await utilsApi.sql('create table products(title text, price float) morphology=\'stem_en\'')res = await utilsApi.sql('create table products(title text, price float) morphology=\'stem_en\'');utilsApi.sql("create table products(title text, price float) morphology='stem_en'", true);utilsApi.Sql("create table products(title text, price float) morphology='stem_en'", true);utils_api.sql("create table products(title text, price float) morphology='stem_en'", Some(true)).await;res = await utilsApi.sql('create table products(title text, price float) morphology=\'stem_en\'');res := apiClient.UtilsAPI.Sql(context.Background()).Body("create table products(title text, price float) morphology='stem_en'").Execute();Query OK, 0 rows affected (0.02 sec){
"total":0,
"error":"",
"warning":""
}- SQL
- JSON
- PHP
- Python
- Python-asyncio
- Javascript
- Java
- C#
- Rust
- TypeScript
- Go
insert into products(title,price) values ('Crossbody Bag with Tassel', 19.85), ('microfiber sheet set', 19.99), ('Pet Hair Remover Glove', 7.99);"id":0 или отсутствие id заставляет автоматически генерировать ID.
POST /insert
{
"table":"products",
"doc":
{
"title" : "Crossbody Bag with Tassel",
"price" : 19.85
}
}
POST /insert
{
"table":"products",
"doc":
{
"title" : "microfiber sheet set",
"price" : 19.99
}
}
POST /insert
{
"table":"products",
"doc":
{
"title" : "Pet Hair Remover Glove",
"price" : 7.99
}
}$index->addDocuments([
['title' => 'Crossbody Bag with Tassel', 'price' => 19.85],
['title' => 'microfiber sheet set', 'price' => 19.99],
['title' => 'Pet Hair Remover Glove', 'price' => 7.99]
]);indexApi.insert({"table" : "products", "doc" : {"title" : "Crossbody Bag with Tassel", "price" : 19.85}})
indexApi.insert({"table" : "products", "doc" : {"title" : "microfiber sheet set", "price" : 19.99}})
indexApi.insert({"table" : "products", "doc" : {"title" : "Pet Hair Remover Glove", "price" : 7.99}})await indexApi.insert({"table" : "products", "doc" : {"title" : "Crossbody Bag with Tassel", "price" : 19.85}})
await indexApi.insert({"table" : "products", "doc" : {"title" : "microfiber sheet set", "price" : 19.99}})
await indexApi.insert({"table" : "products", "doc" : {"title" : "Pet Hair Remover Glove", "price" : 7.99}})res = await indexApi.insert({"table" : "products", "doc" : {"title" : "Crossbody Bag with Tassel", "price" : 19.85}});
res = await indexApi.insert({"table" : "products", "doc" : {"title" : "microfiber sheet set", "price" : 19.99}});
res = await indexApi.insert({"table" : "products", "doc" : {"title" : "Pet Hair Remover Glove", "price" : 7.99}});InsertDocumentRequest newdoc = new InsertDocumentRequest();
HashMap<String,Object> doc = new HashMap<String,Object>(){{
put("title","Crossbody Bag with Tassel");
put("price",19.85);
}};
newdoc.index("products").setDoc(doc);
sqlresult = indexApi.insert(newdoc);
newdoc = new InsertDocumentRequest();
doc = new HashMap<String,Object>(){{
put("title","microfiber sheet set");
put("price",19.99);
}};
newdoc.index("products").setDoc(doc);
sqlresult = indexApi.insert(newdoc);
newdoc = new InsertDocumentRequest();
doc = new HashMap<String,Object>(){{
put("title","Pet Hair Remover Glove");
put("price",7.99);
}};
newdoc.index("products").setDoc(doc);
indexApi.insert(newdoc);Dictionary<string, Object> doc = new Dictionary<string, Object>();
doc.Add("title","Crossbody Bag with Tassel");
doc.Add("price",19.85);
InsertDocumentRequest insertDocumentRequest = new InsertDocumentRequest(index: "products", doc: doc);
sqlresult = indexApi.Insert(insertDocumentRequest);
doc = new Dictionary<string, Object>();
doc.Add("title","microfiber sheet set");
doc.Add("price",19.99);
insertDocumentRequest = new InsertDocumentRequest(index: "products", doc: doc);
sqlresult = indexApi.Insert(insertDocumentRequest);
doc = new Dictionary<string, Object>();
doc.Add("title","Pet Hair Remover Glove");
doc.Add("price",7.99);
insertDocumentRequest = new InsertDocumentRequest(index: "products", doc: doc);
sqlresult = indexApi.Insert(insertDocumentRequest);let mut doc1 = HashMap::new();
doc1.insert("title".to_string(), serde_json::json!("Crossbody Bag with Tassel"));
doc1.insert("price".to_string(), serde_json::json!(19.85));
let insert_req1 = InsertDocumentRequest::new("products".to_string(), serde_json::json!(doc1));
let insert_res1 = index_api.insert(insert_req1).await;
let mut doc2 = HashMap::new();
doc2.insert("title".to_string(), serde_json::json!("microfiber sheet set"));
doc2.insert("price".to_string(), serde_json::json!(19.99));
let insert_req2 = InsertDocumentRequest::new("products".to_string(), serde_json::json!(doc2));
let insert_res2 = index_api.insert(insert_req2).await;
let mut doc3 = HashMap::new();
doc3.insert("title".to_string(), serde_json::json!("Pet Hair Remover Glove"));
doc3.insert("price".to_string(), serde_json::json!(7.99));
let insert_req3 = InsertDocumentRequest::new("products".to_string(), serde_json::json!(doc3));
let insert_res3 = index_api.insert(insert_req3).await;res = await indexApi.insert({
index: 'test',
id: 1,
doc: { content: 'Text 1', name: 'Doc 1', cat: 1 },
});
res = await indexApi.insert({
index: 'test',
id: 2,
doc: { content: 'Text 2', name: 'Doc 2', cat: 2 },
});
res = await indexApi.insert({
index: 'test',
id: 3,
doc: { content: 'Text 3', name: 'Doc 3', cat: 7 },
});indexDoc := map[string]interface{} {"content": "Text 1", "name": "Doc 1", "cat": 1 }
indexReq := manticoreclient.NewInsertDocumentRequest("products", indexDoc)
indexReq.SetId(1)
apiClient.IndexAPI.Insert(context.Background()).InsertDocumentRequest(*indexReq).Execute()
indexDoc = map[string]interface{} {"content": "Text 2", "name": "Doc 3", "cat": 2 }
indexReq = manticoreclient.NewInsertDocumentRequest("products", indexDoc)
indexReq.SetId(2)
apiClient.IndexAPI.Insert(context.Background()).InsertDocumentRequest(*indexReq).Execute()
indexDoc = map[string]interface{} {"content": "Text 3", "name": "Doc 3", "cat": 7 }
indexReq = manticoreclient.NewInsertDocumentRequest("products", indexDoc)
indexReq.SetId(3)
apiClient.IndexAPI.Insert(context.Background()).InsertDocumentRequest(*indexReq).Execute()Query OK, 3 rows affected (0.01 sec){
"table": "products",
"_id": 0,
"created": true,
"result": "created",
"status": 201
}
{
"table": "products",
"_id": 0,
"created": true,
"result": "created",
"status": 201
}
{
"table": "products",
"_id": 0,
"created": true,
"result": "created",
"status": 201
}Более подробную информацию по теме можно найти здесь:
Давайте найдём один из документов. Запрос, который мы будем использовать — 'remove hair'. Как видите, он находит документ с заголовком 'Pet Hair Remover Glove' и выделяет в нём 'Hair remover', хотя в запросе есть "remove", а не "remover". Это потому, что при создании таблицы мы включили использование английского стемминга (morphology "stem_en").
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- Java
- C#
- Rust
- TypeScript
- Go
select id, highlight(), price from products where match('remove hair');POST /search
{
"table": "products",
"query": { "match": { "title": "remove hair" } },
"highlight":
{
"fields": ["title"]
}
}$result = $index->search('@title remove hair')->highlight(['title'])->get();
foreach($result as $doc)
{
echo "Doc ID: ".$doc->getId()."\n";
echo "Doc Score: ".$doc->getScore()."\n";
echo "Document fields:\n";
print_r($doc->getData());
echo "Highlights: \n";
print_r($doc->getHighlight());
}searchApi.search({"table":"products","query":{"query_string":"@title remove hair"},"highlight":{"fields":["title"]}})await searchApi.search({"table":"products","query":{"query_string":"@title remove hair"},"highlight":{"fields":["title"]}})res = await searchApi.search({"table":"products","query":{"query_string":"@title remove hair"}"highlight":{"fields":["title"]}});query = new HashMap<String,Object>();
query.put("query_string","@title remove hair");
searchRequest = new SearchRequest();
searchRequest.setIndex("forum");
searchRequest.setQuery(query);
HashMap<String,Object> highlight = new HashMap<String,Object>(){{
put("fields",new String[] {"title"});
}};
searchRequest.setHighlight(highlight);
searchResponse = searchApi.search(searchRequest);object query = new { query_string="@title remove hair" };
var searchRequest = new SearchRequest("products", query);
var highlight = new Highlight();
highlight.Fieldnames = new List<string> {"title"};
searchRequest.Highlight = highlight;
searchResponse = searchApi.Search(searchRequest);let query = SearchQuery {
query_string: Some(serde_json::json!("@title remove hair").into()),
..Default::default()
};
let highlight = Highlight {
fields: Some(serde_json::json!(["title"]).into()),
..Default::default()
};
let search_req = SearchRequest {
table: "products".to_string(),
query: Some(Box::new(query)),
highlight: Some(Box::new(highlight)),
..Default::default(),
};
let search_res = search_api.search(search_req).await;res = await searchApi.search({
index: 'test',
query: { query_string: {'text 1'} },
highlight: {'fields': ['content'] }
});searchRequest := manticoreclient.NewSearchRequest("test")
query := map[string]interface{} {"query_string": "text 1"};
searchRequest.SetQuery(query);
highlightField := manticoreclient.NewHighlightField("content")
fields := []interface{}{ highlightField }
highlight := manticoreclient.NewHighlight()
highlight.SetFields(fields)
searchRequest.SetHighlight(highlight);
res, _, _ := apiClient.SearchAPI.Search(context.Background()).SearchRequest(*searchRequest).Execute()+---------------------+-------------------------------+----------+
| id | highlight() | price |
+---------------------+-------------------------------+----------+
| 1513686608316989452 | Pet <b>Hair Remover</b> Glove | 7.990000 |
+---------------------+-------------------------------+----------+
1 row in set (0.00 sec){
"took": 0,
"timed_out": false,
"hits": {
"total": 1,
"hits": [
{
"_id": 1513686608316989452,
"_score": 1680,
"_source": {
"price": 7.99,
"title": "Pet Hair Remover Glove"
},
"highlight": {
"title": [
"Pet <b>Hair Remover</b> Glove"
]
}
}
]
}
}Doc ID: 1513686608316989452
Doc Score: 1680
Document fields:
Array
(
[price] => 7.99
[title] => Pet Hair Remover Glove
)
Highlights:
Array
(
[title] => Array
(
[0] => Pet <b>Hair Remover</b> Glove
)
)`
{'hits': {'hits': [{u'_id': u'1513686608316989452',
u'_score': 1680,
u'_source': {u'title': u'Pet Hair Remover Glove', u'price':7.99},
u'highlight':{u'title':[u'Pet <b>Hair Remover</b> Glove']}}}],
'total': 1},
'profile': None,
'timed_out': False,
'took': 0}{'hits': {'hits': [{u'_id': u'1513686608316989452',
u'_score': 1680,
u'_source': {u'title': u'Pet Hair Remover Glove', u'price':7.99},
u'highlight':{u'title':[u'Pet <b>Hair Remover</b> Glove']}}}],
'total': 1},
'profile': None,
'timed_out': False,
'took': 0}{"hits": {"hits": [{"_id": 1513686608316989452,
"_score": 1680,
"_source": {"title": "Pet Hair Remover Glove", "price":7.99},
"highlight":{"title":["Pet <b>Hair Remover</b> Glove"]}}],
"total": 1},
"profile": None,
"timed_out": False,
"took": 0}class SearchResponse {
took: 84
timedOut: false
hits: class SearchResponseHits {
total: 1
maxScore: null
hits: [{_id=1513686608316989452, _score=1, _source={price=7.99, title=Pet Hair Remover Glove}, highlight={title=[Pet <b>Hair Remover</b> Glove]}}]
aggregations: null
}
profile: null
}class SearchResponse {
took: 103
timedOut: false
hits: class SearchResponseHits {
total: 1
maxScore: null
hits: [{_id=1513686608316989452, _score=1, _source={price=7.99, title=Pet Hair Remover Glove}, highlight={title=[Pet <b>Hair Remover</b> Glove]}}]
aggregations: null
}
profile: null
}class SearchResponse {
took: 103
timedOut: false
hits: class SearchResponseHits {
total: 1
maxScore: null
hits: [{_id=1513686608316989452, _score=1, _source={price=7.99, title=Pet Hair Remover Glove}, highlight={title=[Pet <b>Hair Remover</b> Glove]}}]
aggregations: null
}
profile: null
}{
"hits":
{
"hits":
[{
"_id": 1,
"_score": 1400,
"_source": {"content":"Text 1","name":"Doc 1","cat":1},
"highlight": {"content":["<b>Text 1</b>"]}
}],
"total": 1
},
"profile": None,
"timed_out": False,
"took": 0
}{
"hits":
{
"hits":
[{
"_id": 1,
"_score": 1400,
"_source": {"content":"Text 1","name":"Doc 1","cat":1},
"highlight": {"content":["<b>Text 1</b>"]}
}],
"total": 1
},
"profile": None,
"timed_out": False,
"took": 0
}Более подробную информацию о различных вариантах поиска в Manticore можно найти в наших обучающих курсах:
Предположим, что теперь мы хотим обновить документ — изменить цену на 18.5. Это можно сделать, отфильтровав по любому полю, но обычно вы знаете id документа и обновляете что-то на его основе.
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- Java
- C#
- Rust
- TypeScript
- Go
update products set price=18.5 where id = 1513686608316989452;POST /update
{
"table": "products",
"id": 1513686608316989452,
"doc":
{
"price": 18.5
}
}$doc = [
'body' => [
'table' => 'products',
'id' => 2,
'doc' => [
'price' => 18.5
]
]
];
$response = $client->update($doc);indexApi = api = manticoresearch.IndexApi(client)
indexApi.update({"table" : "products", "id" : 1513686608316989452, "doc" : {"price":18.5}})indexApi = api = manticoresearch.IndexApi(client)
await indexApi.update({"table" : "products", "id" : 1513686608316989452, "doc" : {"price":18.5}})res = await indexApi.update({"table" : "products", "id" : 1513686608316989452, "doc" : {"price":18.5}});UpdateDocumentRequest updateRequest = new UpdateDocumentRequest();
doc = new HashMap<String,Object >(){{
put("price",18.5);
}};
updateRequest.index("products").id(1513686608316989452L).setDoc(doc);
indexApi.update(updateRequest);Dictionary<string, Object> doc = new Dictionary<string, Object>();
doc.Add("price", 18.5);
UpdateDocumentRequest updateDocumentRequest = new UpdateDocumentRequest(index: "products", id: 1513686608316989452L, doc: doc);
indexApi.Update(updateDocumentRequest);let mut doc = HashMap::new();
doc.insert("price".to_string(), serde_json::json!(18.5));
let update_req = UpdateDocumentRequest {
table: serde_json::json!("products"),
doc: serde_json::json!(doc),
id: serde_json::json!(1513686608316989452),
..Default::default(),
};
let update_res = index_api.update(update_req).await;res = await indexApi.update({ index: "test", id: 1, doc: { cat: 10 } });updDoc = map[string]interface{} {"cat": 10}
updRequest = manticoreclient.NewUpdateDocumentRequest("test", updDoc)
updRequest.SetId(1)
res, _, _ = apiClient.IndexAPI.Update(context.Background()).UpdateDocumentRequest(*updRequest).Execute()Query OK, 1 row affected (0.00 sec){
"table": "products",
"_id": 1513686608316989452,
"result": "updated"
}- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- Java
- C#
- Rust
- TypeScript
- Go
delete from products where price < 10;POST /delete
{
"table": "products",
"query":
{
"range":
{
"price":
{
"lte": 10
}
}
}
}$result = $index->deleteDocuments(new \Manticoresearch\Query\Range('price',['lte'=>10]));indexApi.delete({"table" : "products", "query": {"range":{"price":{"lte":10}}}})await indexApi.delete({"table" : "products", "query": {"range":{"price":{"lte":10}}}})res = await indexApi.delete({"table" : "products", "query": {"range":{"price":{"lte":10}}}});DeleteDocumentRequest deleteRequest = new DeleteDocumentRequest();
query = new HashMap<String,Object>();
query.put("range",new HashMap<String,Object>(){{
put("price",new HashMap<String,Object>(){{
put("lte",10);
}});
}});
deleteRequest.index("products").setQuery(query);
indexApi.delete(deleteRequest);Dictionary<string, Object> price = new Dictionary<string, Object>();
price.Add("lte", 10);
Dictionary<string, Object> range = new Dictionary<string, Object>();
range.Add("price", price);
DeleteDocumentRequest deleteDocumentRequest = new DeleteDocumentRequest(index: "products", query: range);
indexApi.Delete(deleteDocumentRequest);let mut price_range= HashMap::new();
price_range.insert("lte".to_string(), serde_json::json!(10));
let mut range= HashMap::new();
range.insert("price".to_string(), serde_json::json!(price_range));
let delete_req = DeleteDocumentRequest {
table: "products".to_string(),
query: serde_json::json!(range),
..Default::default(),
};
index_api.delete(delete_req).await;res = await indexApi.delete({
index: 'test',
query: { match: { '*': 'Text 1' } },
});delRequest := manticoreclient.NewDeleteDocumentRequest("test")
matchExpr := map[string]interface{} {"*": "Text 1t"}
delQuery := map[string]interface{} {"match": matchExpr }
delRequest.SetQuery(delQuery)
res, _, _ := apiClient.IndexAPI.Delete(context.Background()).DeleteDocumentRequest(*delRequest).Execute();Query OK, 1 row affected (0.00 sec){
"table": "products",
"deleted": 1
}Array
(
[_index] => products
[deleted] => 1
)После установки сервис Manticore Search не запускается автоматически. Чтобы запустить Manticore, выполните следующую команду:
sudo systemctl start manticore
Чтобы остановить Manticore, выполните следующую команду:
sudo systemctl stop manticore
Сервис Manticore настроен на запуск при загрузке. Вы можете проверить это, выполнив:
sudo systemctl is-enabled manticore
Если вы хотите отключить автоматический запуск Manticore при загрузке, выполните:
sudo systemctl disable manticore
Чтобы включить запуск Manticore при загрузке, выполните:
sudo systemctl enable manticore
Процесс searchd записывает информацию о запуске в журнал systemd. Если ведение журнала systemd включено, вы можете просмотреть записанную информацию с помощью следующей команды:
sudo journalctl -u manticore
systemctl set-environment _ADDITIONAL_SEARCHD_PARAMS позволяет указать пользовательские флаги запуска, с которыми должен запускаться демон Manticore Search. Полный список смотрите здесь.
Например, чтобы запустить Manticore с уровнем логирования debug, вы можете выполнить:
systemctl set-environment _ADDITIONAL_SEARCHD_PARAMS='--logdebug'
systemctl restart manticore
Чтобы отменить это, выполните:
systemctl set-environment _ADDITIONAL_SEARCHD_PARAMS=''
systemctl restart manticore
Обратите внимание, что переменные окружения systemd сбрасываются при перезагрузке сервера.
Manticore можно запускать и останавливать с помощью команд service:
sudo service manticore start
sudo service manticore stop
Чтобы включить sysV сервис при загрузке на системах RedHat, выполните:
chkconfig manticore on
Чтобы включить sysV сервис при загрузке на системах Debian (включая Ubuntu), выполните:
update-rc.d manticore defaults
Обратите внимание, что searchd запускается системой инициализации от имени пользователя manticore, и все файлы, созданные сервером, будут принадлежать этому пользователю. Если searchd запущен, например, от имени пользователя root, права на файлы изменятся, что может привести к проблемам при повторном запуске searchd как сервиса.