如果您正在查找有关向普通表添加文档的信息,请参阅从外部存储添加数据部分。
实时添加文档仅支持实时表和Percolate表。相应的 SQL 命令、HTTP 端点或客户端函数会将新行(文档)插入到表中,并提供字段值。添加文档之前不必先创建表。如果表不存在,Manticore 会尝试自动创建它。更多信息请参见自动模式。
您可以插入单个或多个文档,为表的所有字段或部分字段提供值。在这种情况下,其他字段将填充其默认值(标量类型为 0,文本类型为空字符串)。
INSERT 目前不支持表达式,因此必须显式指定值。
ID 字段/值可以省略,因为 RT 和 PQ 表支持自动 ID功能。您也可以使用 0 作为 id 值以强制自动生成 ID。具有重复 ID 的行不会被 INSERT 覆盖。相反,您可以使用REPLACE来实现该目的。
使用 HTTP JSON 协议时,您可以选择两种不同的请求格式:通用 Manticore 格式和类似 Elasticsearch 的格式。下面的示例中演示了这两种格式。
此外,使用 Manticore JSON 请求格式时,请注意必须包含 doc 节点,所有值都应包含在其中。
- SQL
- JSON
- Elasticsearch
- PHP
- Python
- Python-asyncio
- Javascript
- Java
- C#
- Rust
通用语法:
INSERT INTO <table name> [(column, ...)]
VALUES (value, ...)
[, (...)]INSERT INTO products(title,price) VALUES ('Crossbody Bag with Tassel', 19.85);
INSERT INTO products(title) VALUES ('Crossbody Bag with Tassel');
INSERT INTO products VALUES (0,'Yellow bag', 4.95);POST /insert
{
"table":"products",
"id":1,
"doc":
{
"title" : "Crossbody Bag with Tassel",
"price" : 19.85
}
}
POST /insert
{
"table":"products",
"id":2,
"doc":
{
"title" : "Crossbody Bag with Tassel"
}
}
POST /insert
{
"table":"products",
"id":0,
"doc":
{
"title" : "Yellow bag"
}
}注意:
_create需要Manticore Buddy。如果无法使用,请确保已安装 Buddy。
POST /products/_create/3
{
"title": "Yellow Bag with Tassel",
"price": 19.85
}
POST /products/_create/
{
"title": "Red Bag with Tassel",
"price": 19.85
}$index->addDocuments([
['id' => 1, 'title' => 'Crossbody Bag with Tassel', 'price' => 19.85]
]);
$index->addDocuments([
['id' => 2, 'title' => 'Crossbody Bag with Tassel']
]);
$index->addDocuments([
['id' => 0, 'title' => 'Yellow bag']
]);indexApi.insert({"table" : "test", "id" : 1, "doc" : {"title" : "Crossbody Bag with Tassel", "price" : 19.85}})
indexApi.insert({"table" : "test", "id" : 2, "doc" : {"title" : "Crossbody Bag with Tassel"}})
indexApi.insert({"table" : "test", "id" : 0, "doc" : {{"title" : "Yellow bag"}})await indexApi.insert({"table" : "test", "id" : 1, "doc" : {"title" : "Crossbody Bag with Tassel", "price" : 19.85}})
await indexApi.insert({"table" : "test", "id" : 2, "doc" : {"title" : "Crossbody Bag with Tassel"}})
await indexApi.insert({"table" : "test", "id" : 0, "doc" : {{"title" : "Yellow bag"}})res = await indexApi.insert({"table" : "test", "id" : 1, "doc" : {"title" : "Crossbody Bag with Tassel", "price" : 19.85}});
res = await indexApi.insert({"table" : "test", "id" : 2, "doc" : {"title" : "Crossbody Bag with Tassel"}});
res = await indexApi.insert({"table" : "test", "id" : 0, "doc" : {{"title" : "Yellow bag"}});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").id(1L).setDoc(doc);
sqlresult = indexApi.insert(newdoc);
newdoc = new InsertDocumentRequest();
HashMap<String,Object> doc = new HashMap<String,Object>(){{
put("title","Crossbody Bag with Tassel");
}};
newdoc.index("products").id(2L).setDoc(doc);
sqlresult = indexApi.insert(newdoc);
newdoc = new InsertDocumentRequest();
HashMap<String,Object> doc = new HashMap<String,Object>(){{
put("title","Yellow bag");
}};
newdoc.index("products").id(0L).setDoc(doc);
sqlresult = indexApi.insert(newdoc);Dictionary<string, Object> doc = new Dictionary<string, Object>();
doc.Add("title", "Crossbody Bag with Tassel");
doc.Add("price", 19.85);
InsertDocumentRequest newdoc = new InsertDocumentRequest(index: "products", id: 1, doc: doc);
var sqlresult = indexApi.Insert(newdoc);
doc = new Dictionary<string, Object>();
doc.Add("title", "Crossbody Bag with Tassel");
newdoc = new InsertDocumentRequest(index: "products", id: 2, doc: doc);
sqlresult = indexApi.Insert(newdoc);
doc = new Dictionary<string, Object>();
doc.Add("title", "Yellow bag");
newdoc = new InsertDocumentRequest(index: "products", id: 0, doc: doc);
sqlresult = indexApi.Insert(newdoc);let mut doc = HashMap::new();
doc.insert("title".to_string(), serde_json::json!("Crossbody Bag with Tassel"));
doc.insert("price".to_string(), serde_json::json!(19.85));
let mut insert_req = InsertDocumentRequest {
table: serde_json::json!("products"),
doc: serde_json::json!(doc),
id: serde_json::json!(1),
..Default::default(),
};
let mut insert_res = index_api.insert(insert_req).await;
doc = HashMap::new();
doc.insert("title".to_string(), serde_json::json!("Crossbody Bag with Tassel"));
insert_req = InsertDocumentRequest {
table: serde_json::json!("products"),
doc: serde_json::json!(doc),
id: serde_json::json!(2),
..Default::default(),
};
insert_res = index_api.insert(insert_req).await;
doc = HashMap::new();
doc.insert("title".to_string(), serde_json::json!("Tellow bag"));
insert_req = InsertDocumentRequest {
table: serde_json::json!("products"),
doc: serde_json::json!(doc),
id: serde_json::json!(0),
..Default::default(),
};
insert_res = index_api.insert(insert_req).await;Query OK, 1 rows affected (0.00 sec)
Query OK, 1 rows affected (0.00 sec)
Query OK, 1 rows affected (0.00 sec){
"table": "products",
"_id": 1,
"created": true,
"result": "created",
"status": 201
}
{
"table": "products",
"_id": 2,
"created": true,
"result": "created",
"status": 201
}
{
"table": "products",
"_id": 1657860156022587406,
"created": true,
"result": "created",
"status": 201
}{
"_id":3,
"table":"products",
"_primary_term":1,
"_seq_no":0,
"_shards":{
"failed":0,
"successful":1,
"total":1
},
"_type":"_doc",
"_version":1,
"result":"updated"
}
{
"_id":2235747273424240642,
"table":"products",
"_primary_term":1,
"_seq_no":0,
"_shards":{
"failed":0,
"successful":1,
"total":1
},
"_type":"_doc",
"_version":1,
"result":"updated"
}注意:自动模式需要Manticore Buddy。如果无法使用,请确保已安装 Buddy。
Manticore 具有自动创建表的机制,当插入查询中指定的表尚不存在时会激活。该机制默认启用。要禁用它,请在 Manticore 配置文件的Searchd部分设置 auto_schema = 0。
默认情况下,VALUES 子句中的所有文本值都被视为 text 类型,除非值表示有效的电子邮件地址,则视为 string 类型。
如果尝试插入多行且同一字段的值类型不同且不兼容,自动创建表将被取消,并返回错误消息。但如果不同的值类型兼容,结果字段类型将是能容纳所有值的类型。可能发生的一些自动数据类型转换包括:
- mva -> mva64
- uint -> bigint -> float(这可能导致一定的精度损失)
- string -> text
自动模式机制不支持创建带有用于KNN(K 最近邻)相似度搜索的向量字段(float_vector 类型)的表。要在表中使用向量字段,必须显式创建定义这些字段的表模式。如果需要在普通表中存储向量数据但不具备 KNN 搜索功能,可以使用标准 JSON 语法将其存储为 JSON 数组,例如:INSERT INTO table_name (vector_field) VALUES ('[1.0, 2.0, 3.0]')。
此外,以下日期格式将被识别并转换为时间戳,所有其他日期格式将被视为字符串:
%Y-%m-%dT%H:%M:%E*S%Z%Y-%m-%d'T'%H:%M:%S%Z%Y-%m-%dT%H:%M:%E*S%Y-%m-%dT%H:%M:%s%Y-%m-%dT%H:%M%Y-%m-%dT%H
请注意,/bulk HTTP 端点不支持自动创建表(自动模式)。只有 /_bulk(类似 Elasticsearch)HTTP 端点和 SQL 接口支持此功能。
- SQL
- JSON
MySQL [(none)]> drop table if exists t; insert into t(i,f,t,s,j,b,m,mb) values(123,1.2,'text here','test@mail.com','{"a": 123}',1099511627776,(1,2),(1099511627776,1099511627777)); desc t; select * from t;POST /insert -d
{
"table":"t",
"id": 2,
"doc":
{
"i" : 123,
"f" : 1.23,
"t": "text here",
"s": "test@mail.com",
"j": {"a": 123},
"b": 1099511627776,
"m": [1,2],
"mb": [1099511627776,1099511627777]
}
}--------------
drop table if exists t
--------------
Query OK, 0 rows affected (0.42 sec)
--------------
insert into t(i,f,t,j,b,m,mb) values(123,1.2,'text here','{"a": 123}',1099511627776,(1,2),(1099511627776,1099511627777))
--------------
Query OK, 1 row affected (0.00 sec)
--------------
desc t
--------------
+-------+--------+----------------+
| Field | Type | Properties |
+-------+--------+----------------+
| id | bigint | |
| t | text | indexed stored |
| s | string | |
| j | json | |
| i | uint | |
| b | bigint | |
| f | float | |
| m | mva | |
| mb | mva64 | |
+-------+--------+----------------+
8 rows in set (0.00 sec)
--------------
select * from t
--------------
+---------------------+------+---------------+----------+------+-----------------------------+-----------+---------------+------------+
| id | i | b | f | m | mb | t | s | j |
+---------------------+------+---------------+----------+------+-----------------------------+-----------+---------------+------------+
| 5045949922868723723 | 123 | 1099511627776 | 1.200000 | 1,2 | 1099511627776,1099511627777 | text here | test@mail.com | {"a": 123} |
+---------------------+------+---------------+----------+------+-----------------------------+-----------+---------------+------------+
1 row in set (0.00 sec){"table":"t","_id":2,"created":true,"result":"created","status":201}Manticore 为插入或替换到实时表或Percolate 表的文档的 ID 列提供自动 ID 生成功能。该生成器为文档生成唯一 ID 并提供一定保证,但不应视为自增 ID。
生成的 ID 值在以下条件下保证唯一:
- 当前服务器的 server_id 值在 0 到 127 范围内,并且在集群节点中唯一,或者使用从 MAC 地址生成的默认值作为种子
- Manticore 节点在服务器重启之间系统时间不变
- 在搜索服务器重启之间,自动 ID 生成次数少于每秒 1600 万次
自动 ID 生成器为文档 ID 创建一个 64 位整数,并使用以下方案:
- 位 0 到 23 形成一个计数器,每次调用自动 ID 生成器时递增
- 位 24 到 55 表示服务器启动的 Unix 时间戳
- 位 56 到 63 对应 server_id
该方案确保生成的 ID 在集群所有节点中唯一,并且插入到不同集群节点的数据不会在节点之间产生冲突。
因此,自动 ID 生成器生成的第一个 ID 不是 1,而是一个更大的数字。此外,如果在调用之间对其他表进行插入,插入到表中的文档流可能具有非连续的 ID 值,因为 ID 生成器在服务器中是唯一的,并且在其所有表之间共享。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- Javascript
- Java
- C#
- Rust
INSERT INTO products(title,price) VALUES ('Crossbody Bag with Tassel', 19.85);
INSERT INTO products VALUES (0,'Yello bag', 4.95);
select * from products;POST /insert
{
"table":"products",
"id":0,
"doc":
{
"title" : "Yellow bag"
}
}
GET /search
{
"table":"products",
"query":{
"query_string":""
}
}$index->addDocuments([
['id' => 0, 'title' => 'Yellow bag']
]);indexApi.insert({"table" : "products", "id" : 0, "doc" : {"title" : "Yellow bag"}})await indexApi.insert({"table" : "products", "id" : 0, "doc" : {"title" : "Yellow bag"}})res = await indexApi.insert({"table" : "products", "id" : 0, "doc" : {"title" : "Yellow bag"}});newdoc = new InsertDocumentRequest();
HashMap<String,Object> doc = new HashMap<String,Object>(){{
put("title","Yellow bag");
}};
newdoc.index("products").id(0L).setDoc(doc);
sqlresult = indexApi.insert(newdoc);Dictionary<string, Object> doc = new Dictionary<string, Object>();
doc.Add("title", "Yellow bag");
InsertDocumentRequest newdoc = new InsertDocumentRequest(index: "products", id: 0, doc: doc);
var sqlresult = indexApi.Insert(newdoc);let doc = HashMap::new();
doc.insert("title".to_string(), serde_json::json!("Yellow bag"));
let insert_req = InsertDocumentRequest {
table: serde_json::json!("products"),
doc: serde_json::json!(doc),
id: serde_json::json!(0),
..Default::default(),
};
let insert_res = index_api.insert(insert_req).await;+---------------------+-----------+---------------------------+
| id | price | title |
+---------------------+-----------+---------------------------+
| 1657860156022587404 | 19.850000 | Crossbody Bag with Tassel |
| 1657860156022587405 | 4.950000 | Yello bag |
+---------------------+-----------+---------------------------+{
"took": 0,
"timed_out": false,
"hits": {
"total": 1,
"hits": [
{
"_id": 1657860156022587406,
"_score": 1,
"_source": {
"price": 0,
"title": "Yellow bag"
}
}
]
}
}CALL UUID_SHORT(N)
CALL UUID_SHORT(N) 语句允许在一次调用中生成 N 个唯一的 64 位 ID,而无需插入任何文档。当您需要在 Manticore 中预生成 ID 以供其他系统或存储解决方案使用时,这非常有用。例如,您可以在 Manticore 中生成自动 ID,然后在另一个数据库、应用程序或工作流中使用它们,确保不同环境中的标识符一致且唯一。
- Example
CALL UUID_SHORT(3)+---------------------+
| uuid_short() |
+---------------------+
| 1227930988733973183 |
| 1227930988733973184 |
| 1227930988733973185 |
+---------------------+您不仅可以向实时表插入单个文档,还可以插入任意数量的文档。向实时表插入数万个文档的批次是完全可以接受的。但需要注意以下几点:
- 批次越大,每次插入操作的延迟越高
- 批次越大,索引速度越快
- 您可能需要增加 max_packet_size 的值以允许更大的批次
- 通常,每个批量插入操作被视为一个具有原子性保证的单个 事务,因此您要么一次性拥有表中的所有新文档,要么在失败时一个都不会添加。有关空行或切换到另一张表的更多细节,请参见“JSON”示例。
请注意,/bulk HTTP 端点不支持自动创建表(自动模式)。只有 /_bulk(类似 Elasticsearch)HTTP 端点和 SQL 接口支持此功能。/_bulk(类似 Elasticsearch)HTTP 端点允许表名包含集群名称,格式为 cluster_name:table_name。
/_bulk 端点接受与 Elasticsearch 相同格式的文档 ID,您也可以在文档本身中包含 id:
{ "index": { "table" : "products", "_id" : "1" } }
{ "title" : "Crossbody Bag with Tassel", "price": 19.85 }
或
{ "index": { "table" : "products" } }
{ "title" : "Crossbody Bag with Tassel", "price": 19.85, "id": "1" }
/bulk(Manticore 模式)端点支持分块传输编码。您可以使用它来传输大型批次。它:
- 减少峰值内存使用,降低 OOM 风险
- 缩短响应时间
- 允许您绕过 max_packet_size 限制,传输远大于最大允许值(128MB)的批次,例如一次传输 1GB。
- SQL
- JSON
- Elasticsearch
- PHP
- Python
- Python-asyncio
- Javascript
- Java
- C#
- Rust
对于批量插入,只需在 VALUES() 后提供更多文档。语法为:
INSERT INTO <table name>[(column1, column2, ...)] VALUES(value1[, value2 , ...]), (...)可选的列名列表允许您显式指定表中某些列的值。所有其他列将填充默认值(标量类型为 0,字符串类型为空字符串)。
例如:
INSERT INTO products(title,price) VALUES ('Crossbody Bag with Tassel', 19.85), ('microfiber sheet set', 19.99), ('Pet Hair Remover Glove', 7.99);语法通常与插入单个文档相同。只需提供更多行,每行一个文档,并使用 /bulk 端点替代 /insert。将每个文档包裹在 "insert" 节点中。请注意,还需要:
Content-Type: application/x-ndjson- 数据应格式化为换行分隔的 JSON(NDJSON)。本质上,这意味着每行应包含一个完整的 JSON 语句,并以换行符
\n和可能的\r结尾。
/bulk 端点支持 'insert'、'replace'、'delete' 和 'update' 查询。请记住,您可以将操作指向多个表,但事务仅适用于单个表。如果您指定了多个表,Manticore 会将指向同一表的操作收集到一个事务中。当表发生变化时,它会提交已收集的操作,并在新表上启动一个新事务。分批之间的空行也会导致提交前一批并开始一个新事务。
在 /bulk 请求的响应中,您可以找到以下字段:
- "errors":显示是否发生了任何错误(true/false)
- "error":描述发生的错误
- "current_line":执行停止(或失败)的位置行号;空行,包括第一个空行,也会被计数
- "skipped_lines":从
current_line向后开始未提交的行数
POST /bulk
-H "Content-Type: application/x-ndjson" -d '
{"insert": {"table":"products", "id":1, "doc": {"title":"Crossbody Bag with Tassel","price" : 19.85}}}
{"insert":{"table":"products", "id":2, "doc": {"title":"microfiber sheet set","price" : 19.99}}}
'
POST /bulk
-H "Content-Type: application/x-ndjson" -d '
{"insert":{"table":"test1","id":21,"doc":{"int_col":1,"price":1.1,"title":"bulk doc one"}}}
{"insert":{"table":"test1","id":22,"doc":{"int_col":2,"price":2.2,"title":"bulk doc two"}}}
{"insert":{"table":"test1","id":23,"doc":{"int_col":3,"price":3.3,"title":"bulk doc three"}}}
{"insert":{"table":"test2","id":24,"doc":{"int_col":4,"price":4.4,"title":"bulk doc four"}}}
{"insert":{"table":"test2","id":25,"doc":{"int_col":5,"price":5.5,"title":"bulk doc five"}}}
'注意:如果表尚不存在,
_bulk需要 Manticore Buddy。如果不起作用,请确保已安装 Buddy。
POST /_bulk
-H "Content-Type: application/x-ndjson" -d '
{ "index" : { "table" : "products" } }
{ "title" : "Yellow Bag", "price": 12 }
{ "create" : { "table" : "products" } }
{ "title" : "Red Bag", "price": 12.5, "id": 3 }
'使用方法 addDocuments():
$index->addDocuments([
['id' => 1, 'title' => 'Crossbody Bag with Tassel', 'price' => 19.85],
['id' => 2, 'title' => 'microfiber sheet set', 'price' => 19.99],
['id' => 3, 'title' => 'Pet Hair Remover Glove', 'price' => 7.99]
]);docs = [ \
{"insert": {"table" : "products", "id" : 1, "doc" : {"title" : "Crossbody Bag with Tassel", "price" : 19.85}}}, \
{"insert": {"table" : "products", "id" : 2, "doc" : {"title" : "microfiber sheet set", "price" : 19.99}}}, \
{"insert": {"table" : "products", "id" : 3, "doc" : {"title" : "CPet Hair Remover Glove", "price" : 7.99}}}
]
res = indexApi.bulk('\n'.join(map(json.dumps,docs)))docs = [ \
{"insert": {"table" : "products", "id" : 1, "doc" : {"title" : "Crossbody Bag with Tassel", "price" : 19.85}}}, \
{"insert": {"table" : "products", "id" : 2, "doc" : {"title" : "microfiber sheet set", "price" : 19.99}}}, \
{"insert": {"table" : "products", "id" : 3, "doc" : {"title" : "CPet Hair Remover Glove", "price" : 7.99}}}
]
res = await indexApi.bulk('\n'.join(map(json.dumps,docs)))let docs = [
{"insert": {"table" : "products", "id" : 3, "doc" : {"title" : "Crossbody Bag with Tassel", "price" : 19.85}}},
{"insert": {"table" : "products", "id" : 4, "doc" : {"title" : "microfiber sheet set", "price" : 19.99}}},
{"insert": {"table" : "products", "id" : 5, "doc" : {"title" : "CPet Hair Remover Glove", "price" : 7.99}}}
];
res = await indexApi.bulk(docs.map(e=>JSON.stringify(e)).join('\n'));String body = "{\"insert\": {\"index\" : \"products\", \"id\" : 1, \"doc\" : {\"title\" : \"Crossbody Bag with Tassel\", \"price\" : 19.85}}}"+"\n"+
"{\"insert\": {\"index\" : \"products\", \"id\" : 4, \"doc\" : {\"title\" : \"microfiber sheet set\", \"price\" : 19.99}}}"+"\n"+
"{\"insert\": {\"index\" : \"products\", \"id\" : 5, \"doc\" : {\"title\" : \"CPet Hair Remover Glove\", \"price\" : 7.99}}}"+"\n";
BulkResponse bulkresult = indexApi.bulk(body);string body = "{\"insert\": {\"index\" : \"products\", \"id\" : 1, \"doc\" : {\"title\" : \"Crossbody Bag with Tassel\", \"price\" : 19.85}}}"+"\n"+
"{\"insert\": {\"index\" : \"products\", \"id\" : 4, \"doc\" : {\"title\" : \"microfiber sheet set\", \"price\" : 19.99}}}"+"\n"+
"{\"insert\": {\"index\" : \"products\", \"id\" : 5, \"doc\" : {\"title\" : \"CPet Hair Remover Glove\", \"price\" : 7.99}}}"+"\n";
BulkResponse bulkresult = indexApi.Bulk(string.Join("\n", docs));let bulk_body = r#"{"insert": "index" : "products", "id" : 1, "doc" : {"title" : "Crossbody Bag with Tassel", "price" : 19.85}}}
{"insert": {"index" : "products", "id" : 4, "doc" : {"title" : "microfiber sheet set", "price" : 19.99}}}
{"insert": {"index" : "products", "id" : 5, "doc" : {"title" : "CPet Hair Remover Glove", "price" : 7.99}}}
"#;
index_api.bulk(bulk_body).await;Query OK, 3 rows affected (0.01 sec)INSERT 目前不支持表达式,值应显式指定。
{
"items": [
{
"bulk": {
"table": "products",
"_id": 2,
"created": 2,
"deleted": 0,
"updated": 0,
"result": "created",
"status": 201
}
}
],
"current_line": 4,
"skipped_lines": 0,
"errors": false,
"error": ""
}
{
"items": [
{
"bulk": {
"table": "test1",
"_id": 22,
"created": 2,
"deleted": 0,
"updated": 0,
"result": "created",
"status": 201
}
},
{
"bulk": {
"table": "test1",
"_id": 23,
"created": 1,
"deleted": 0,
"updated": 0,
"result": "created",
"status": 201
}
},
{
"bulk": {
"table": "test2",
"_id": 25,
"created": 2,
"deleted": 0,
"updated": 0,
"result": "created",
"status": 201
}
}
],
"current_line": 8,
"skipped_lines": 0,
"errors": false,
"error": ""
}{
"items": [
{
"table": {
"table": "products",
"_type": "doc",
"_id": 1657860156022587406,
"_version": 1,
"result": "created",
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1,
"status": 201
}
},
{
"create": {
"table": "products",
"_type": "doc",
"_id": 3,
"_version": 1,
"result": "created",
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1,
"status": 201
}
}
],
"errors": false,
"took": 1
}- SQL
- JSON
- Elasticsearch
- PHP
- Python
- Python-asyncio
- Javascript
- Java
- C#
- Rust
INSERT INTO products(title, sizes) VALUES('shoes', (40,41,42,43));POST /insert
{
"table":"products",
"id":1,
"doc":
{
"title" : "shoes",
"sizes" : [40, 41, 42, 43]
}
}POST /products/_create/1
{
"title": "shoes",
"sizes" : [40, 41, 42, 43]
}或者,作为替代
POST /products/_doc/
{
"title": "shoes",
"sizes" : [40, 41, 42, 43]
}$index->addDocument(
['title' => 'shoes', 'sizes' => [40,41,42,43]],
1
);indexApi.insert({"table" : "products", "id" : 0, "doc" : {"title" : "Yellow bag","sizes":[40,41,42,43]}})await indexApi.insert({"table" : "products", "id" : 0, "doc" : {"title" : "Yellow bag","sizes":[40,41,42,43]}})res = await indexApi.insert({"table" : "products", "id" : 0, "doc" : {"title" : "Yellow bag","sizes":[40,41,42,43]}});newdoc = new InsertDocumentRequest();
HashMap<String,Object> doc = new HashMap<String,Object>(){{
put("title","Yellow bag");
put("sizes",new int[]{40,41,42,43});
}};
newdoc.index("products").id(0L).setDoc(doc);
sqlresult = indexApi.insert(newdoc);Dictionary<string, Object> doc = new Dictionary<string, Object>();
doc.Add("title", "Yellow bag");
doc.Add("sizes", new List<Object> {40,41,42,43});
InsertDocumentRequest newdoc = new InsertDocumentRequest(index: "products", id: 0, doc: doc);
var sqlresult = indexApi.Insert(newdoc);let mut doc = HashMap::new();
doc.insert("title".to_string(), serde_json::json!("Yellow bag"));
doc.insert("sizes".to_string(), serde_json::json!([40,41,42,43]));
let insert_req = InsertDocumentRequest::new("products".to_string(), serde_json::json!(doc));
let insert_res = index_api.insert(insert_req).await;- SQL
- JSON
- Elasticsearch
- PHP
- Python
- Python-asyncio
- Javascript
- Java
- C#
- Rust
INSERT INTO products VALUES (1, 'shoes', '{"size": 41, "color": "red"}');JSON 值可以作为 JSON 对象插入
POST /insert
{
"table":"products",
"id":1,
"doc":
{
"title" : "shoes",
"meta" : {
"size": 41,
"color": "red"
}
}
}JSON 值也可以作为包含转义 JSON 的字符串插入:
POST /insert
{
"table":"products",
"id":1,
"doc":
{
"title" : "shoes",
"meta" : "{\"size\": 41, \"color\": \"red\"}"
}
}POST /products/_create/1
{
"title": "shoes",
"meta" : {
"size": 41,
"color": "red"
}
}或者,作为替代
POST /products/_doc/
{
"title": "shoes",
"meta" : {
"size": 41,
"color": "red"
}
}$index->addDocument(
['title' => 'shoes', 'meta' => '{"size": 41, "color": "red"}'],
1
);indexApi = api = manticoresearch.IndexApi(client)
indexApi.insert({"table" : "products", "id" : 0, "doc" : {"title" : "Yellow bag","meta":'{"size": 41, "color": "red"}'}})indexApi = api = manticoresearch.IndexApi(client)
await indexApi.insert({"table" : "products", "id" : 0, "doc" : {"title" : "Yellow bag","meta":'{"size": 41, "color": "red"}'}})res = await indexApi.insert({"table" : "products", "id" : 0, "doc" : {"title" : "Yellow bag","meta":'{"size": 41, "color": "red"}'}});newdoc = new InsertDocumentRequest();
HashMap<String,Object> doc = new HashMap<String,Object>(){{
put("title","Yellow bag");
put("meta",
new HashMap<String,Object>(){{
put("size",41);
put("color","red");
}});
}};
newdoc.index("products").id(0L).setDoc(doc);
sqlresult = indexApi.insert(newdoc);Dictionary<string, Object> meta = new Dictionary<string, Object>();
meta.Add("size", 41);
meta.Add("color", "red");
Dictionary<string, Object> doc = new Dictionary<string, Object>();
doc.Add("title", "Yellow bag");
doc.Add("meta", meta);
InsertDocumentRequest newdoc = new InsertDocumentRequest(index: "products", id: 0, doc: doc);
var sqlresult = indexApi.Insert(newdoc);let mut meta = HashMap::new();
metadoc.insert("size".to_string(), serde_json::json!(41));
meta.insert("color".to_string(), serde_json::json!("red"));
let mut doc = HashMap::new();
doc.insert("title".to_string(), serde_json::json!("Yellow bag"));
doc.insert("meta".to_string(), serde_json::json!(meta));
let insert_req = InsertDocumentRequest::new("products".to_string(), serde_json::json!(doc));
let insert_res = index_api.insert(insert_req).await;在 percolate 表 中,percolate 查询规则被存储,且必须遵循四个字段的精确模式:
| 字段 | 类型 | 描述 |
|---|---|---|
| id | bigint | PQ 规则标识符(如果省略,将自动分配) |
| query | string | 全文查询(可以为空),兼容 percolate 表 |
| filters | string | 通过非全文字段的附加过滤器(可以为空),兼容 percolate 表 |
| tags | string | 一个包含一个或多个逗号分隔标签的字符串,可用于选择性显示/删除已保存的查询 |
任何其他字段名称不被支持,会触发错误。
警告: 通过 SQL 插入/替换 JSON 格式的 PQ 规则将不起作用。换句话说,JSON 特定的操作符(如 match 等)将仅被视为规则文本的一部分,应与文档匹配。如果你偏好 JSON 语法,请使用 HTTP 端点而非 INSERT/REPLACE。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- Javascript
- Java
- C#
- Rust
INSERT INTO pq(id, query, filters) VALUES (1, '@title shoes', 'price > 5');
INSERT INTO pq(id, query, tags) VALUES (2, '@title bag', 'Louis Vuitton');
SELECT * FROM pq;有两种方式可以将 percolate 查询添加到 percolate 表中:
-
兼容 JSON /search 格式的查询,详见 json/search
PUT /pq/pq_table/doc/1 { "query": { "match": { "title": "shoes" }, "range": { "price": { "gt": 5 } } }, "tags": ["Loius Vuitton"] } -
SQL 格式的查询,详见 search query syntax
PUT /pq/pq_table/doc/2 { "query": { "ql": "@title shoes" }, "filters": "price > 5", "tags": ["Loius Vuitton"] }
$newstoredquery = [
'table' => 'test_pq',
'body' => [
'query' => [
'match' => [
'title' => 'shoes'
]
],
'range' => [
'price' => [
'gt' => 5
]
]
],
'tags' => ['Loius Vuitton']
];
$client->pq()->doc($newstoredquery);newstoredquery ={"table" : "test_pq", "id" : 2, "doc" : {"query": {"ql": "@title shoes"},"filters": "price > 5","tags": ["Loius Vuitton"]}}
indexApi.insert(newstoredquery)newstoredquery ={"table" : "test_pq", "id" : 2, "doc" : {"query": {"ql": "@title shoes"},"filters": "price > 5","tags": ["Loius Vuitton"]}}
await indexApi.insert(newstoredquery)newstoredquery ={"table" : "test_pq", "id" : 2, "doc" : {"query": {"ql": "@title shoes"},"filters": "price > 5","tags": ["Loius Vuitton"]}};
indexApi.insert(newstoredquery);newstoredquery = new HashMap<String,Object>(){{
put("query",new HashMap<String,Object >(){{
put("q1","@title shoes");
put("filters","price>5");
put("tags",new String[] {"Loius Vuitton"});
}});
}};
newdoc.index("test_pq").id(2L).setDoc(doc);
indexApi.insert(newdoc);Dictionary<string, Object> query = new Dictionary<string, Object>();
query.Add("q1", "@title shoes");
query.Add("filters", "price>5");
query.Add("tags", new List<string> {"Loius Vuitton"});
Dictionary<string, Object> newstoredquery = new Dictionary<string, Object>();
newstoredquery.Add("query", query);
InsertDocumentRequest newdoc = new InsertDocumentRequest(index: "test_pq", id: 2, doc: doc);
indexApi.Insert(newdoc);let mut pq_doc = HashMap::new();
pq_doc.insert("q1".to_string(), serde_json::json!("@title shoes"));
pq_doc.insert("filters".to_string(), serde_json::json!("price>5"));
pq_doc.insert("tags".to_string(), serde_json::json!(["Louis Vitton"]));
let mut doc = HashMap::new();
pq_doc.insert("query".to_string(), serde_json::json!(pq_doc));
let insert_req = InsertDocumentRequest::new("test_pq".to_string(), serde_json::json!(doc));
let insert_res = index_api.insert(insert_req).await;+------+--------------+---------------+---------+
| id | query | tags | filters |
+------+--------------+---------------+---------+
| 1 | @title shoes | | price>5 |
| 2 | @title bag | Louis Vuitton | |
+------+--------------+---------------+---------+- SQL
- JSON
- PHP
- Python
- Python-asyncio
- Javascript
- Java
- C#
- Rust
INSERT INTO pq(query, filters) VALUES ('wristband', 'price > 5');
SELECT * FROM pq;PUT /pq/pq_table/doc
{
"query": {
"match": {
"title": "shoes"
},
"range": {
"price": {
"gt": 5
}
}
},
"tags": ["Loius Vuitton"]
}
PUT /pq/pq_table/doc
{
"query": {
"ql": "@title shoes"
},
"filters": "price > 5",
"tags": ["Loius Vuitton"]
}$newstoredquery = [
'table' => 'pq_table',
'body' => [
'query' => [
'match' => [
'title' => 'shoes'
]
],
'range' => [
'price' => [
'gt' => 5
]
]
],
'tags' => ['Loius Vuitton']
];
$client->pq()->doc($newstoredquery);indexApi = api = manticoresearch.IndexApi(client)
newstoredquery ={"table" : "test_pq", "doc" : {"query": {"ql": "@title shoes"},"filters": "price > 5","tags": ["Loius Vuitton"]}}
indexApi.insert(store_query)indexApi = api = manticoresearch.IndexApi(client)
newstoredquery ={"table" : "test_pq", "doc" : {"query": {"ql": "@title shoes"},"filters": "price > 5","tags": ["Loius Vuitton"]}}
await indexApi.insert(store_query)newstoredquery ={"table" : "test_pq", "doc" : {"query": {"ql": "@title shoes"},"filters": "price > 5","tags": ["Loius Vuitton"]}};
res = await indexApi.insert(store_query);newstoredquery = new HashMap<String,Object>(){{
put("query",new HashMap<String,Object >(){{
put("q1","@title shoes");
put("filters","price>5");
put("tags",new String[] {"Loius Vuitton"});
}});
}};
newdoc.index("test_pq").setDoc(doc);
indexApi.insert(newdoc);Dictionary<string, Object> query = new Dictionary<string, Object>();
query.Add("q1", "@title shoes");
query.Add("filters", "price>5");
query.Add("tags", new List<string> {"Loius Vuitton"});
Dictionary<string, Object> newstoredquery = new Dictionary<string, Object>();
newstoredquery.Add("query", query);
InsertDocumentRequest newdoc = new InsertDocumentRequest(index: "test_pq", doc: doc);
indexApi.Insert(newdoc);let mut pq_doc = HashMap::new();
pq_doc.insert("q1".to_string(), serde_json::json!("@title shoes"));
pq_doc.insert("filters".to_string(), serde_json::json!("price>5"));
pq_doc.insert("tags".to_string(), serde_json::json!(["Louis Vitton"]));
let mut doc = HashMap::new();
pq_doc.insert("query".to_string(), serde_json::json!(pq_doc));
let insert_req = InsertDocumentRequest::new("test_pq".to_string(), serde_json::json!(doc));
let insert_res = index_api.insert(insert_req).await;+---------------------+-----------+------+---------+
| id | query | tags | filters |
+---------------------+-----------+------+---------+
| 1657843905795719192 | wristband | | price>5 |
+---------------------+-----------+------+---------+{
"table": "pq_table",
"type": "doc",
"_id": 1657843905795719196,
"result": "created"
}
{
"table": "pq_table",
"type": "doc",
"_id": 1657843905795719198,
"result": "created"
}Array(
[index] => pq_table
[type] => doc
[_id] => 1657843905795719198
[result] => created
){'created': True,
'found': None,
'id': 1657843905795719198,
'table': 'test_pq',
'result': 'created'}{'created': True,
'found': None,
'id': 1657843905795719198,
'table': 'test_pq',
'result': 'created'}{"table":"test_pq","_id":1657843905795719198,"created":true,"result":"created"}如果在 SQL INSERT 命令中省略了模式,预期的参数如下:
- ID。你可以使用
0作为 ID 以触发自动 ID 生成。 - Query - 全文查询。
- Tags - PQ 规则标签字符串。
- Filters - 通过属性的附加过滤器。
- SQL
INSERT INTO pq VALUES (0, '@title shoes', '', '');
INSERT INTO pq VALUES (0, '@title shoes', 'Louis Vuitton', '');
SELECT * FROM pq;+---------------------+--------------+---------------+---------+
| id | query | tags | filters |
+---------------------+--------------+---------------+---------+
| 2810855531667783688 | @title shoes | | |
| 2810855531667783689 | @title shoes | Louis Vuitton | |
+---------------------+--------------+---------------+---------+要在 SQL 中用新规则替换现有的 PQ 规则,只需使用常规的 REPLACE 命令。通过 HTTP JSON 接口以 JSON 模式定义的 PQ 规则替换,有一个特殊语法 ?refresh=1。
- SQL
- JSON
mysql> select * from pq;
+---------------------+--------------+------+---------+
| id | query | tags | filters |
+---------------------+--------------+------+---------+
| 2810823411335430148 | @title shoes | | |
+---------------------+--------------+------+---------+
1 row in set (0.00 sec)
mysql> replace into pq(id,query) values(2810823411335430148,'@title boots');
Query OK, 1 row affected (0.00 sec)
mysql> select * from pq;
+---------------------+--------------+------+---------+
| id | query | tags | filters |
+---------------------+--------------+------+---------+
| 2810823411335430148 | @title boots | | |
+---------------------+--------------+------+---------+
1 row in set (0.00 sec)GET /pq/pq/doc/2810823411335430149
{
"took": 0,
"timed_out": false,
"hits": {
"total": 1,
"hits": [
{
"_id": 2810823411335430149,
"_score": 1,
"_source": {
"query": {
"match": {
"title": "shoes"
}
},
"tags": "",
"filters": ""
}
}
]
}
}
PUT /pq/pq/doc/2810823411335430149?refresh=1 -d '{
"query": {
"match": {
"title": "boots"
}
}
}'
GET /pq/pq/doc/2810823411335430149
{
"took": 0,
"timed_out": false,
"hits": {
"total": 1,
"hits": [
{
"_id": 2810823411335430149,
"_score": 1,
"_source": {
"query": {
"match": {
"title": "boots"
}
},
"tags": "",
"filters": ""
}
}
]
}
}您可以使用多种方法将数据从外部存储导入到 Manticore:
- 使用 Indexer 工具 从各种数据库获取数据到普通表中。
- 使用 Logstash、Filebeat 和 Vector.dev 集成,将数据从这些工具导入到 Manticore 实时表中。
- 使用 Kafka 集成 将 Kafka 主题中的数据同步到实时表中。
纯表是通过在创建时从一个或多个源获取数据一次性创建的表。纯表是不可变的,因为在其生命周期内无法添加或删除文档。只能更新数值属性(包括 MVA)的值。刷新数据只能通过重新创建整个表来实现。
纯表仅在纯模式中可用,其定义由表声明和一个或多个源声明组成。数据收集和表创建不是由 searchd 服务器完成,而是由辅助工具 indexer 完成。
Indexer 是一个命令行工具,可以直接从命令行或 shell 脚本调用。
调用时它可以接受多个参数,但在 Manticore 配置文件中也有一些自身的设置。
在典型场景中,indexer 执行以下操作:
- 从源获取数据
- 构建纯表
- 写入表文件
- (可选)通知搜索服务器新表,触发表轮换
indexer 工具用于在 Manticore Search 中创建纯表。其通用语法为:
indexer [OPTIONS] [table_name1 [table_name2 [...]]]
使用 indexer 创建表时,生成的表文件必须具有允许 searchd 读取、写入和删除的权限。在官方 Linux 包中,searchd 以 manticore 用户身份运行。因此,indexer 也必须以 manticore 用户身份运行:
sudo -u manticore indexer ...
如果你以不同方式运行 searchd,可能需要省略 sudo -u manticore。只需确保运行 searchd 实例的用户对使用 indexer 生成的表具有读写权限。
要创建纯表,需要列出要处理的表。例如,如果你的 manticore.conf 文件包含两个表的详细信息,mybigindex 和 mysmallindex,你可以运行:
sudo -u manticore indexer mysmallindex mybigindex
你也可以使用通配符匹配表名:
?匹配任意单个字符*匹配任意数量的任意字符%匹配无或任意单个字符
sudo -u manticore indexer indexpart*main --rotate
indexer 的退出代码如下:
- 0:一切正常
- 1:索引时出现问题(如果指定了
--rotate,则跳过)或操作发出警告 - 2:索引正常,但
--rotate尝试失败
你也可以使用以下 systemctl 单元文件启动 indexer:
systemctl start --no-block manticore-indexer
或者,如果你想构建特定表:
systemctl start --no-block manticore-indexer@specific-table-name
使用 systemctl set-environment INDEXER_CONFIG 命令以自定义配置运行 Indexer,替代默认设置。
systemctl set-environment INDEXER_ARGS 命令允许你为 Indexer 添加自定义启动选项。完整的命令行选项列表,请参见这里。
例如,要以静默模式启动 Indexer,运行:
systemctl set-environment INDEXER_ARGS='--quiet'
systemctl restart manticore-indexer
要恢复更改,运行:
systemctl set-environment INDEXER_ARGS=''
systemctl restart manticore-indexer
-
--config <file>(简写为-c <file>)告诉indexer使用指定文件作为配置。通常,它会在安装目录(例如/etc/manticoresearch/manticore.conf)中查找manticore.conf,然后是调用indexer时所在的当前目录。这在共享环境中非常有用,例如二进制文件安装在全局文件夹(如/usr/bin/),但你希望用户能够创建自己的自定义 Manticore 设置,或者你想在单台服务器上运行多个实例。在这种情况下,你可以允许他们创建自己的manticore.conf文件,并通过此选项传递给indexer。例如:sudo -u manticore indexer --config /home/myuser/manticore.conf mytable -
--all告诉indexer更新manticore.conf中列出的所有表,而不是列出单个表。这在小型配置或定时任务或维护作业中很有用,其中整个表集每天、每周或其他最佳周期重建。请注意,由于--all尝试更新配置中找到的所有表,如果遇到实时表会发出警告,即使纯表完成没有问题,命令的退出代码也会是1而非0。示例用法:sudo -u manticore indexer --config /home/myuser/manticore.conf --all -
--rotate用于轮换表。除非你能让搜索功能离线而不影响用户,否则几乎肯定需要在索引新文档时保持搜索运行。--rotate创建第二个表,与第一个表并行(在同一位置,只是在文件名中包含.new)。完成后,indexer通过发送SIGHUP信号通知searchd,searchd会尝试重命名表(将现有表重命名为包含.old,并将.new重命名替换它们),然后开始从新文件提供服务。根据 seamless_rotate 的设置,搜索新表可能会有轻微延迟。如果一次轮换多个通过 killlist_target 关联的表,轮换将从非目标表开始,最后完成目标链末端的表。示例用法:sudo -u manticore indexer --rotate --all -
--quiet告诉indexer除非发生错误,否则不输出任何内容。这主要用于定时任务或其他脚本作业,其中输出无关紧要或不必要,除非发生错误。示例用法:sudo -u manticore indexer --rotate --all --quiet -
--noprogress不会显示进度详情。相反,只有在索引完成时才报告最终状态详情(例如索引的文档数、索引速度等)。在脚本未在控制台(或 'tty')上运行的情况下,默认启用此选项。示例用法:sudo -u manticore indexer --rotate --all --noprogress -
--buildstops <outputfile.text> <N>会审查表的源数据,就像正在索引数据一样,并生成正在被索引的词汇列表。换句话说,它生成所有成为表一部分的可搜索词汇列表。注意,它不会更新相关表,只是像索引一样处理数据,包括运行用 sql_query_pre 或 sql_query_post 定义的查询。outputfile.txt将包含词汇列表,每行一个,按频率排序,最频繁的词在前,N指定列出的最大词数。如果足够大以涵盖表中的所有词,则只返回那么多词。这样的词典列表可用于客户端应用的“你是想说……”功能,通常与下面的--buildfreqs一起使用。示例:sudo -u manticore indexer mytable --buildstops word_freq.txt 1000这将在当前目录生成一个文档
word_freq.txt,包含 'mytable' 中最常见的 1000 个词,按最常见排序。注意,如果指定多个表或--all,该文件将对应最后一个被索引的表(即配置文件中最后列出的表)。 -
--buildfreqs与--buildstops配合使用(如果未指定--buildstops,则忽略此选项)。由于--buildstops提供了表中使用的词列表,--buildfreqs会添加这些词在表中出现的数量,这对于确定某些词是否应被视为停用词(如果它们过于常见)很有用。它还帮助开发“你是想说……”功能,了解某词相较于另一个相似词的出现频率。例如:sudo -u manticore indexer mytable --buildstops word_freq.txt 1000 --buildfreqs这将生成上述的
word_freq.txt,但每个词后面会跟着它在相关表中出现的次数。 -
--merge <dst-table> <src-table>用于物理合并表,例如如果你有一个 主+增量方案,主表很少更改,但增量表经常重建,--merge用于合并两者。操作从右向左进行——检查src-table的内容并与dst-table的内容物理合并,结果保留在dst-table中。伪代码表达为:dst-table += src-table示例:sudo -u manticore indexer --merge main delta --rotate在上述示例中,主表是主控且很少修改的表,增量表更频繁修改,你可以用上述命令调用
indexer将增量表内容合并到主表并旋转表。 -
--merge-dst-range <attr> <min> <max>在合并时应用给定的过滤范围。具体来说,由于合并应用于目标表(作为--merge的一部分,如果未指定--merge则忽略),indexer也会过滤进入目标表的文档,只有通过给定过滤器的文档才会出现在最终表中。例如,在一个有 'deleted' 属性的表中,0 表示“未删除”,可以用如下命令合并:sudo -u manticore indexer --merge main delta --merge-dst-range deleted 0 0标记为已删除(值为 1)的文档将从新合并的目标表中移除。此选项可以多次添加到命令行,添加多个连续过滤器,所有过滤条件都必须满足,文档才会成为最终表的一部分。
-
--
merge-killlists(及其简写别名--merge-klists)改变合并表时杀死列表的处理方式。默认情况下,合并后两个杀死列表都会被丢弃。这支持最典型的主+增量合并场景。但启用此选项后,两个表的杀死列表会被连接并存储到目标表中。注意,源(增量)表的杀死列表始终用于抑制目标(主)表中的行。 -
--keep-attrs允许在重新索引时重用现有属性。每当表重建时,会检查每个新文档 ID 是否存在于“旧”表中,如果存在,其属性会被转移到“新”表;如果未找到,则使用新表的属性。如果用户更新了表中的属性,但未更新实际用于表的源数据,重新索引时所有更新都会丢失;使用--keep-attrs可以保存之前表中更新的属性值。可以指定表文件路径以替代配置中的参考路径:sudo -u manticore indexer mytable --keep-attrs=/path/to/index/files -
--keep-attrs-names=<attributes list>允许指定在重新索引时从现有表重用的属性。默认情况下,所有属性都会从现有表重用到新表:sudo -u manticore indexer mytable --keep-attrs=/path/to/table/files --keep-attrs-names=update,state -
--dump-rows <FILE>将由 SQL 源获取的行以 MySQL 兼容的语法转储到指定文件中。生成的转储是indexer接收到的数据的精确表示,有助于重复索引时出现的问题。该命令执行从源的获取,并创建表文件和转储文件。 -
--print-rt <rt_index> <table>以 INSERT 语句的形式输出从源获取的实时表数据。转储的前几行将包含实时字段和属性(作为普通表字段和属性的反映)。该命令执行从源的获取,并创建表文件和转储输出。该命令可用作sudo -u manticore indexer -c manticore.conf --print-rt indexrt indexplain > dump.sql。仅支持基于 SQL 的源。不支持 MVA。 -
--sighup-each在重建许多大型表时非常有用,您希望每个表尽快旋转到searchd。使用--sighup-each,indexer会在成功完成每个表的工作后向 searchd 发送 SIGHUP 信号。(默认行为是在所有表构建完成后发送单个 SIGHUP)。 -
--nohup在您想在实际旋转表之前使用 indextool 检查表时非常有用。如果启用此选项,indexer 不会发送 SIGHUP。表文件将重命名为 .tmp。使用 indextool 将表文件重命名为 .new 并旋转它。示例用法:sudo -u manticore indexer --rotate --nohup mytable sudo -u manticore indextool --rotate --check mytable -
--print-queries打印indexer发送到数据库的 SQL 查询,以及 SQL 连接和断开事件。这对于诊断和修复 SQL 源的问题很有用。 -
--help(简写为-h)列出indexer中可调用的所有参数。 -
-v显示indexer版本。
您还可以在 Manticore 配置文件的 indexer 部分配置 indexer 行为:
indexer {
...
}
lemmatizer_cache = 256M
词形还原缓存大小。可选,默认值为 256K。
我们的词形还原器实现使用压缩字典格式,支持空间与速度的权衡。它可以直接在压缩数据上执行词形还原,使用更多 CPU 但更少 RAM,或者可以部分或完全解压并预缓存字典,从而使用更少 CPU 但更多 RAM。lemmatizer_cache 指令让您控制用于未压缩字典缓存的 RAM 大小。
目前,唯一可用的字典是ru.pak、en.pak 和 de.pak,分别是俄语、英语和德语字典。压缩字典大小约为 2 到 10 MB。请注意,字典始终驻留在内存中。默认缓存大小为 256 KB。接受的缓存大小范围是 0 到 2047 MB。将缓存大小设置得过高是安全的;词形还原器只会使用所需的内存。例如,整个俄语字典解压后约为 110 MB;因此将 lemmatizer_cache 设置高于此值不会影响内存使用。即使允许缓存为 1024 MB,如果只需 110 MB,它也只会使用这 110 MB。
max_file_field_buffer = 128M
文件字段自适应缓冲区的最大大小(字节)。可选,默认值为 8MB,最小值为 1MB。
文件字段缓冲区用于加载来自 sql_file_field 列引用的文件。该缓冲区是自适应的,首次分配时为 1 MB,并以 2 倍的步长增长,直到文件内容可以加载或达到 max_file_field_buffer 指令指定的最大缓冲区大小。
因此,如果未指定文件字段,则不会分配缓冲区。如果索引期间加载的所有文件大小均小于(例如)2 MB,但 max_file_field_buffer 值为 128 MB,则峰值缓冲区使用量仍仅为 2 MB。然而,超过 128 MB 的文件将被完全跳过。
max_iops = 40
最大每秒 I/O 操作数,用于 I/O 限速。可选,默认值为 0(无限制)。
与 I/O 限速相关的选项。它限制任意给定秒内的最大 I/O 操作数(读或写)。值为 0 表示不施加限制。
indexer 在构建表时可能会引起磁盘 I/O 的突发高峰,可能希望限制其磁盘活动(并为同一机器上运行的其他程序,如 searchd,保留资源)。I/O 限速有助于实现这一点。它通过强制 indexer 执行的连续磁盘 I/O 操作之间的最小保证延迟来工作。限制 I/O 可以帮助减少构建期间导致的搜索性能下降。此设置对其他类型的数据摄取无效,例如向实时表插入数据。
max_iosize = 1048576
最大允许的 I/O 操作大小(字节),用于 I/O 限速。可选,默认值为 0(无限制)。
与 I/O 限速相关的选项。它限制 indexer 执行的所有文件 I/O 操作(读或写)的最大大小。值为 0 表示不施加限制。大于限制的读写操作将被拆分为多个较小的操作,并由 max_iops 设置计为多个操作。撰写本文时,所有 I/O 调用应均小于 256 KB(默认内部缓冲区大小),因此 max_iosize 设置高于 256 KB 不应有任何影响。
max_xmlpipe2_field = 8M
XMLpipe2 源类型允许的最大字段大小(字节)。可选,默认值为 2 MB。
mem_limit = 256M
# mem_limit = 262144K # same, but in KB
# mem_limit = 268435456 # same, but in bytes
纯表构建内存使用限制。可选,默认值为128 MB。强制执行的内存使用限制,indexer 不会超过该限制。可以以字节、千字节(使用K后缀)或兆字节(使用M后缀)指定;参见示例。如果设置为极低值导致I/O缓冲区小于8 KB,该限制将自动提高;具体下限取决于构建数据的大小。如果缓冲区小于256 KB,将发出警告。
最大可能的限制是2047M。过低的值会影响纯表构建速度,但256M到1024M对于大多数(如果不是全部)数据集来说应该足够。设置此值过高可能导致SQL服务器超时。在文档收集阶段,会有内存缓冲区部分排序且不与数据库通信的时间段;数据库服务器可能会超时。您可以通过提高SQL服务器端的超时设置或降低mem_limit来解决此问题。
on_file_field_error = skip_document
如何处理文件字段中的IO错误。可选,默认值为ignore_field。
当索引由文件字段引用的文件(sql_file_field)时出现问题,indexer 可以处理文档,假设该字段内容为空,或者跳过文档,或者完全失败索引。on_file_field_error 指令控制该行为。它可以取的值有:
ignore_field,处理当前文档但不包含该字段;skip_document,跳过当前文档但继续索引;fail_index,索引失败并显示错误信息。
可能出现的问题包括:打开错误、大小错误(文件过大)和数据读取错误。无论阶段和on_file_field_error设置如何,任何问题都会发出警告信息。
注意,使用on_file_field_error = skip_document时,只有在早期检查阶段检测到问题时文档才会被忽略,而不是在实际文件解析阶段。indexer 会在进行任何工作之前打开每个引用的文件并检查其大小,然后在实际解析时再次打开它。因此,如果文件在这两次打开之间消失,文档仍将被索引。
write_buffer = 4M
写缓冲区大小,单位字节。可选,默认值为1MB。写缓冲区用于在索引时写入临时和最终的表文件。较大的缓冲区减少所需的磁盘写入次数。缓冲区的内存分配是额外的,独立于mem_limit。注意,将为不同文件分配多个(目前最多4个)缓冲区,比例增加RAM使用量。
ignore_non_plain = 1
ignore_non_plain 允许您完全忽略关于跳过非纯表的警告。默认值为0(不忽略)。
有两种调度 indexer 运行的方法。第一种是使用 crontab 的经典方法。第二种是使用带有用户定义计划的 systemd 计时器。要创建计时器单元文件,应将它们放置在 systemd 查找此类单元文件的适当目录中。在大多数 Linux 发行版中,该目录通常是 /etc/systemd/system。操作步骤如下:
-
为您的自定义计划创建计时器单元文件:
cat << EOF > /etc/systemd/system/manticore-indexer@.timer [Unit] Description=Run Manticore Search's indexer on schedule [Timer] OnCalendar=minutely RandomizedDelaySec=5m Unit=manticore-indexer@%i.service [Install] WantedBy=timers.target EOF关于
OnCalendar语法和示例的更多信息,请参见这里。 -
根据您的具体需求编辑计时器单元。
-
启用计时器:
systemctl enable manticore-indexer@idx1.timer -
启动计时器:
systemctl start manticore-indexer@idx1.timer -
对任何额外的计时器重复步骤2-4。