Manticore 的数据类型可以分为两类:全文字段和属性。
Manticore 中的字段名必须遵循以下规则:
- 可以包含字母(a-z, A-Z)、数字(0-9)和连字符(-)
- 必须以字母开头
- 数字只能出现在字母之后
- 下划线(
_)是唯一允许的特殊字符 - 字段名不区分大小写
例如:
- 有效的字段名:
title、product_id、user_name_2 - 无效的字段名:
2title、-price、user@name
全文字段:
- 可以使用自然语言处理算法进行索引,因此可以搜索关键词
- 不能用于排序或分组
- 可以检索原始文档内容
- 原始文档内容可用于高亮显示
全文字段由数据类型 text 表示。所有其他数据类型都称为“属性”。
属性是与每个文档关联的非全文值,可用于在搜索期间执行非全文过滤、排序和分组。
通常,不仅需要根据匹配的文档 ID 及其排名来处理全文搜索结果,还需要基于许多其他每文档值进行处理。例如,可能需要按日期和相关性对新闻搜索结果进行排序,或在指定价格范围内搜索产品,或将博客搜索限制在选定用户发布的帖子中,或按月份对结果进行分组。为了实现这一点,Manticore 不仅支持全文字段,还允许为每个文档添加额外的属性。这些属性可用于过滤、排序或分组全文匹配项,或仅按属性进行搜索。
与全文字段不同,属性不进行全文索引。它们存储在表中,但无法像全文那样进行搜索。
属性的一个很好的例子是论坛帖子表。假设只有标题和内容字段需要进行全文搜索——但有时还需要将搜索限制在特定作者或子论坛(即,仅搜索那些具有特定 author_id 或 forum_id 值的行);或按 post_date 列对匹配项进行排序;或按 post_date 的月份对匹配的帖子进行分组并计算每组的匹配计数。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- Javascript
- Java
- C#
- Rust
- config
CREATE TABLE forum(title text, content text, author_id int, forum_id int, post_date timestamp);POST /cli -d "CREATE TABLE forum(title text, content text, author_id int, forum_id int, post_date timestamp)"$index = new \Manticoresearch\Index($client);
$index->setName('forum');
$index->create([
'title'=>['type'=>'text'],
'content'=>['type'=>'text'],
'author_id'=>['type'=>'int'],
'forum_id'=>['type'=>'int'],
'post_date'=>['type'=>'timestamp']
]);utilsApi.sql('CREATE TABLE forum(title text, content text, author_id int, forum_id int, post_date timestamp)')await utilsApi.sql('CREATE TABLE forum(title text, content text, author_id int, forum_id int, post_date timestamp)')res = await utilsApi.sql('CREATE TABLE forum(title text, content text, author_id int, forum_id int, post_date timestamp)');utilsApi.sql("CREATE TABLE forum(title text, content text, author_id int, forum_id int, post_date timestamp)");utilsApi.Sql("CREATE TABLE forum(title text, content text, author_id int, forum_id int, post_date timestamp)");utils_api.sql("CREATE TABLE forum(title text, content text, author_id int, forum_id int, post_date timestamp)", Some(true)).await;table forum
{
type = rt
path = forum
# when configuring fields via config, they are indexed (and not stored) by default
rt_field = title
rt_field = content
# this option needs to be specified for the field to be stored
stored_fields = title, content
rt_attr_uint = author_id
rt_attr_uint = forum_id
rt_attr_timestamp = post_date
}此示例展示了按 author_id、forum_id 过滤并按 post_date 排序的全文查询。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
select * from forum where author_id=123 and forum_id in (1,3,7) order by post_date descPOST /search
{
"table": "forum",
"query":
{
"match_all": {},
"bool":
{
"must":
[
{ "equals": { "author_id": 123 } },
{ "in": { "forum_id": [1,3,7] } }
]
}
},
"sort": [ { "post_date": "desc" } ]
}$client->search([
'table' => 'forum',
'query' =>
[
'match_all' => [],
'bool' => [
'must' => [
'equals' => ['author_id' => 123],
'in' => [
'forum_id' => [
1,3,7
]
]
]
]
],
'sort' => [
['post_date' => 'desc']
]
]);searchApi.search({"table":"forum","query":{"match_all":{},"bool":{"must":[{"equals":{"author_id":123}},{"in":{"forum_id":[1,3,7]}}]}},"sort":[{"post_date":"desc"}]})await searchApi.search({"table":"forum","query":{"match_all":{},"bool":{"must":[{"equals":{"author_id":123}},{"in":{"forum_id":[1,3,7]}}]}},"sort":[{"post_date":"desc"}]})res = await searchApi.search({"table":"forum","query":{"match_all":{},"bool":{"must":[{"equals":{"author_id":123}},{"in":{"forum_id":[1,3,7]}}]}},"sort":[{"post_date":"desc"}]});HashMap<String,Object> filters = new HashMap<String,Object>(){{
put("must", new HashMap<String,Object>(){{
put("equals",new HashMap<String,Integer>(){{
put("author_id",123);
}});
put("in",
new HashMap<String,Object>(){{
put("forum_id",new int[] {1,3,7});
}});
}});
}};
Map<String,Object> query = new HashMap<String,Object>();
query.put("match_all",null);
query.put("bool",filters);
SearchRequest searchRequest = new SearchRequest();
searchRequest.setIndex("forum");
searchRequest.setQuery(query);
searchRequest.setSort(new ArrayList<Object>(){{
add(new HashMap<String,String>(){{ put("post_date","desc");}});
}});
SearchResponse searchResponse = searchApi.search(searchRequest);object query = new { match_all=null };
var searchRequest = new SearchRequest("forum", query);
var boolFilter = new BoolFilter();
boolFilter.Must = new List<Object> {
new EqualsFilter("author_id", 123),
new InFilter("forum_id", new List<Object> {1,3,7})
};
searchRequest.AttrFilter = boolFilter;
searchRequest.Sort = new List<Object> { new SortOrder("post_date", SortOrder.OrderEnum.Desc) };
var searchResponse = searchApi.Search(searchRequest);let query = SearchQuery::new();
let mut sort = HashMap::new();
sort.insert("post_date".to_string(), serde_json::json!("desc"));
let search_request = SearchRequest {
table: "forum".to_string(),
query: Some(Box::new(query)),
sort: serde_json::json!(sort)
..Default::default()
};
let search_res = search_api.search(search_req).await;Manticore 支持两种类型的属性存储:
- 行式 - Manticore Search 开箱即用的传统存储
- 列式 - 由 Manticore 列式库 提供
从它们的名称可以理解,它们以不同的方式存储数据。传统的 行式存储:
- 以未压缩的方式存储属性
- 同一文档的所有属性存储在彼此靠近的一行中
- 行按顺序存储
- 访问属性基本上是通过将行 ID 乘以步幅(单个向量的长度)并从计算出的内存位置获取请求的属性来实现。这提供了非常低的随机访问延迟。
- 属性必须位于内存中才能获得可接受的性能,否则由于存储的行式特性,Manticore 可能不得不从磁盘读取太多不需要的数据,这在许多情况下是次优的。
使用 列式存储:
- 每个属性都独立于所有其他属性存储在其单独的“列”中
- 存储被分成 65536 个条目的块
- 块以压缩形式存储。这通常允许仅存储几个不同的值,而不是像行式存储那样存储所有值。高压缩比允许更快地从磁盘读取,并大大降低内存需求
- 当数据被索引时,每个块独立选择存储方案。例如,如果一个块中的所有值都相同,则采用“常量”存储,整个块只存储一个值。如果每个块的唯一值少于 256 个,则采用“表”存储,存储值表的索引而不是值本身
- 如果明确请求的值不在块中,可以提前拒绝在块中的搜索。
列式存储旨在处理无法放入 RAM 的大数据量,因此建议如下:
- 如果您有足够的内存容纳所有属性,您将从行式存储中受益
- 否则,列式存储仍然可以为您提供不错的性能,同时内存占用低得多,这将允许您在表中存储更多的文档
传统的行式存储是默认方式,因此如果您希望所有内容都以行式存储,创建表时无需进行任何操作。
要启用列式存储,您需要:
- 在 CREATE TABLE 中指定
engine='columnar',使表的所有属性变为列式。然后,如果您希望保留特定属性为行式,需要在声明该属性时添加engine='rowwise'。例如:create table tbl(title text, type int, price float engine='rowwise') engine='columnar' - 在
CREATE TABLE中为特定属性指定engine='columnar',使其变为列式。例如:create table tbl(title text, type int, price float engine='columnar');或
create table tbl(title text, type int, price float engine='columnar') engine='rowwise'; - 在 plain 模式 下,您需要在 columnar_attrs 中列出希望为列式的属性。
以下是 Manticore Search 支持的数据类型列表:
文档标识符是一个必需的属性,必须是唯一的 64 位无符号整数。创建表时可以显式指定文档 ID,但即使未指定,文档 ID 也始终会被启用。文档 ID 无法更新。
创建表时,您可以显式指定 ID,但无论您使用何种数据类型,其行为始终如上所述——以无符号 64 位存储,但以有符号 64 位整数形式暴露。
mysql> CREATE TABLE tbl(id bigint, content text);
DESC tbl;
+---------+--------+----------------+
| Field | Type | Properties |
+---------+--------+----------------+
| id | bigint | |
| content | text | indexed stored |
+---------+--------+----------------+
2 rows in set (0.00 sec)
您也可以完全省略指定 ID,它将自动启用。
mysql> CREATE TABLE tbl(content text);
DESC tbl;
+---------+--------+----------------+
| Field | Type | Properties |
+---------+--------+----------------+
| id | bigint | |
| content | text | indexed stored |
+---------+--------+----------------+
2 rows in set (0.00 sec)
处理文档 ID 时,重要的是要知道它们在内部以无符号 64 位整数存储,但根据接口的不同,处理方式也有所不同:
MySQL/SQL 接口:
- 大于 2^63-1 的 ID 将显示为负数。
- 过滤此类大 ID 时,必须使用其有符号表示。
- 使用 UINT64() 函数查看实际的无符号值。
JSON/HTTP 接口:
- ID 始终以其原始无符号值显示,无论大小。
- 过滤时可以使用有符号或无符号表示。
- 插入操作接受完整的无符号 64 位范围。
例如,让我们创建一个表并插入一些接近 2^63 的值:
mysql> create table t(id_text string);
Query OK, 0 rows affected (0.01 sec)
mysql> insert into t values(9223372036854775807, '2 ^ 63 - 1'),(9223372036854775808, '2 ^ 63');
Query OK, 2 rows affected (0.00 sec)
一些 ID 在结果中显示为负数,因为它们超过了 2^63-1。然而,使用 UINT64(id) 可以揭示它们的实际无符号值:
mysql> select *, uint64(id) from t;
+----------------------+------------+---------------------+
| id | id_text | uint64(id) |
+----------------------+------------+---------------------+
| 9223372036854775807 | 2 ^ 63 - 1 | 9223372036854775807 |
| -9223372036854775808 | 2 ^ 63 | 9223372036854775808 |
+----------------------+------------+---------------------+
2 rows in set (0.00 sec)
--- 2 out of 2 results in 0ms ---
对于查询 ID 小于 2^63 的文档,可以直接使用无符号值:
mysql> select * from t where id = 9223372036854775807;
+---------------------+------------+
| id | id_text |
+---------------------+------------+
| 9223372036854775807 | 2 ^ 63 - 1 |
+---------------------+------------+
1 row in set (0.00 sec)
--- 1 out of 1 results in 0ms ---
但是,对于从 2^63 开始的 ID,需要使用有符号值:
mysql> select * from t where id = -9223372036854775808;
+----------------------+---------+
| id | id_text |
+----------------------+---------+
| -9223372036854775808 | 2 ^ 63 |
+----------------------+---------+
1 row in set (0.00 sec)
--- 1 out of 1 results in 0ms ---
如果使用无符号值,将会出错:
mysql> select * from t where id = 9223372036854775808;
ERROR 1064 (42000): number 9223372036854775808 is out of range [-9223372036854775808..9223372036854775807]
超出 64 位范围的值将触发类似错误:
mysql> select * from t where id = -9223372036854775809;
ERROR 1064 (42000): number -9223372036854775809 is out of range [-9223372036854775808..9223372036854775807]
MySQL/SQL 和 JSON/HTTP 接口之间的行为差异在处理非常大的文档 ID 时变得更加明显。以下是一个全面的示例:
MySQL/SQL 接口:
mysql> drop table if exists t; create table t; insert into t values(17581446260360033510);
Query OK, 0 rows affected (0.01 sec)
mysql> select * from t;
+---------------------+
| id |
+---------------------+
| -865297813349518106 |
+---------------------+
mysql> select *, uint64(id) from t;
+---------------------+----------------------+
| id | uint64(id) |
+---------------------+----------------------+
| -865297813349518106 | 17581446260360033510 |
+---------------------+----------------------+
mysql> select * from t where id = -865297813349518106;
+---------------------+
| id |
+---------------------+
| -865297813349518106 |
+---------------------+
mysql> select * from t where id = 17581446260360033510;
ERROR 1064 (42000): number 17581446260360033510 is out of range [-9223372036854775808..9223372036854775807]
JSON/HTTP 接口:
# Search returns the original unsigned value
curl -s 0:9308/search -d '{"table": "t"}'
{
"took": 0,
"timed_out": false,
"hits": {
"total": 1,
"total_relation": "eq",
"hits": [
{
"_id": 17581446260360033510,
"_score": 1,
"_source": {}
}
]
}
}
# Both signed and unsigned values work for filtering
curl -s 0:9308/search -d '{"table": "t", "query": {"equals": {"id": 17581446260360033510}}}'
curl -s 0:9308/search -d '{"table": "t", "query": {"equals": {"id": -865297813349518106}}}'
# Insert with maximum unsigned 64-bit value
curl -s 0:9308/insert -d '{"table": "t", "id": 18446744073709551615, "doc": {}}'
这意味着在处理大文档 ID 时:
- MySQL 接口 要求查询时使用有符号表示,但可以通过
UINT64()显示无符号值 - JSON 接口 始终使用无符号值显示,并接受两种表示进行过滤
通用语法:
string|text [stored|attribute] [indexed]
属性:
indexed- 全文索引(可用于全文查询)stored- 存储在文档存储中(存储在磁盘上,而非 RAM 中,惰性读取)attribute- 使其成为字符串属性(可以按其排序/分组)
指定至少一个属性会覆盖所有默认属性(见下文),即,如果您决定使用自定义属性组合,需要列出所有您想要的属性。
未指定任何属性:
string 和 text 是别名,但如果您不指定任何属性,它们默认表示不同的含义:
文本(仅 text 或 text/string indexed)数据类型构成表的全文部分。文本字段被索引,可以用于搜索关键词。
文本通过分析器管道处理,将文本转换为单词,应用词形变换等。最终,从该文本构建出全文表(一种特殊的数据结构,能够快速搜索关键词)。
全文字段只能在 MATCH() 子句中使用,不能用于排序或聚合。单词存储在倒排索引中,并带有它们所属字段的引用和字段中的位置。这允许在每个字段内搜索单词,并使用邻近等高级运算符。默认情况下,字段的原始文本既被索引,也被存储在文档存储中。这意味着原始文本可以随查询结果返回,并用于 搜索结果高亮。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text);POST /cli -d "CREATE TABLE products(title text)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text']
]);utilsApi.sql('CREATE TABLE products(title text)')await utilsApi.sql('CREATE TABLE products(title text)')res = await utilsApi.sql('CREATE TABLE products(title text)');utilsApi.sql("CREATE TABLE products(title text)");utilsApi.Sql("CREATE TABLE products(title text)");utils_api.sql("CREATE TABLE products(title text)", Some(true)).await;table products
{
type = rt
path = products
# when configuring fields via config, they are indexed (and not stored) by default
rt_field = title
# this option needs to be specified for the field to be stored
stored_fields = title
}此行为可以通过显式指定文本仅被索引来覆盖。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text indexed);POST /cli -d "CREATE TABLE products(title text indexed)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text','options'=>['indexed']]
]);utilsApi.sql('CREATE TABLE products(title text indexed)')await utilsApi.sql('CREATE TABLE products(title text indexed)')res = await utilsApi.sql('CREATE TABLE products(title text indexed)');utilsApi.sql("CREATE TABLE products(title text indexed)");utilsApi.Sql("CREATE TABLE products(title text indexed)");utils_api.sql("CREATE TABLE products(title text indexed)", Some(true)).await;table products
{
type = rt
path = products
# when configuring fields via config, they are indexed (and not stored) by default
rt_field = title
}字段是有名称的,您可以将搜索限制在单个字段(例如仅搜索"title")或字段子集(例如仅"title"和"abstract")。您最多可以拥有256个全文字段。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
select * from products where match('@title first');POST /search
{
"table": "products",
"query":
{
"match": { "title": "first" }
}
}$index->setName('products')->search('@title')->get();searchApi.search({"table":"products","query":{"match":{"title":"first"}}})await searchApi.search({"table":"products","query":{"match":{"title":"first"}}})res = await searchApi.search({"table":"products","query":{"match":{"title":"first"}}});utilsApi.sql("CREATE TABLE products(title text indexed)");utilsApi.Sql("CREATE TABLE products(title text indexed)");utils_api.sql("CREATE TABLE products(title text indexed)", Some(true)).await;与全文字段不同,字符串属性(仅string或string/text attribute)按接收时的原样存储,不能用于全文搜索。相反,它们会在结果中返回,可以在WHERE子句中用于比较过滤或REGEX,并且可以用于排序和聚合。通常,不建议在字符串属性中存储大文本,而是将字符串属性用于元数据,如名称、标题、标签、键。
如果您还想索引字符串属性,可以同时指定为string attribute indexed。这将允许全文搜索并作为属性使用。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text, keys string);POST /cli -d "CREATE TABLE products(title text, keys string)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'keys'=>['type'=>'string']
]);utilsApi.sql('CREATE TABLE products(title text, keys string)')await utilsApi.sql('CREATE TABLE products(title text, keys string)')res = await utilsApi.sql('CREATE TABLE products(title text, keys string)');utilsApi.sql("CREATE TABLE products(title text, keys string)");utilsApi.Sql("CREATE TABLE products(title text, keys string)");utils_api.sql("CREATE TABLE products(title text, keys string)", Some(true)).await;table products
{
type = rt
path = products
rt_field = title
stored_fields = title
rt_attr_string = keys
}Manticore没有专门的二进制数据类型字段,但您可以通过使用base64编码和text stored或string stored字段类型(它们是同义词)来安全地存储它。如果您不对二进制数据进行编码,部分数据可能会丢失——例如,如果遇到空字节,Manticore会修剪字符串的末尾。
这是一个示例,我们使用base64编码ls命令,将其存储在Manticore中,然后解码以验证MD5校验和保持不变:
- Example
# md5sum /bin/ls
43d1b8a7ccda411118e2caba685f4329 /bin/ls
# encoded_data=`base64 -i /bin/ls `
# mysql -P9306 -h0 -e "drop table if exists test; create table test(data text stored); insert into test(data) values('$encoded_data')"
# mysql -P9306 -h0 -NB -e "select data from test" | base64 -d > /tmp/ls | md5sum
43d1b8a7ccda411118e2caba685f4329 -整数类型允许存储32位无符号整数值。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text, price int);POST /cli -d "CREATE TABLE products(title text, price int)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'price'=>['type'=>'int']
]);utilsApi.sql('CREATE TABLE products(title text, price int)')await utilsApi.sql('CREATE TABLE products(title text, price int)')res = await utilsApi.sql('CREATE TABLE products(title text, price int)');utilsApi.sql("CREATE TABLE products(title text, price int)");utilsApi.Sql("CREATE TABLE products(title text, price int)");utils_api.sql("CREATE TABLE products(title text, price int)", Some(true)).await;table products
{
type = rt
path = products
rt_field = title
stored_fields = title
rt_attr_uint = type
}整数可以通过指定位数以比32位更短的尺寸存储。例如,如果我们想存储一个我们知道不会大于8的数值,类型可以定义为bit(3)。位计数整数比全尺寸整数性能更慢,但它们需要更少的RAM。它们以32位块保存,因此为了节省空间,它们应该分组在属性定义的末尾(否则,在两个全尺寸整数之间的位计数整数也将占用32位)。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text, flags bit(3), tags bit(2) );POST /cli -d "CREATE TABLE products(title text, flags bit(3), tags bit(2))"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'flags'=>['type'=>'bit(3)'],
'tags'=>['type'=>'bit(2)']
]);utilsApi.sql('CREATE TABLE products(title text, flags bit(3), tags bit(2) ')await utilsApi.sql('CREATE TABLE products(title text, flags bit(3), tags bit(2) ')res = await utilsApi.sql('CREATE TABLE products(title text, flags bit(3), tags bit(2) ');utilsApi.sql("CREATE TABLE products(title text, flags bit(3), tags bit(2)");utilsApi.Sql("CREATE TABLE products(title text, flags bit(3), tags bit(2)");utils_api.sql("CREATE TABLE products(title text, flags bit(3), tags bit(2)", Some(true)).await;table products
{
type = rt
path = products
rt_field = title
stored_fields = title
rt_attr_uint = flags:3
rt_attr_uint = tags:2
}大整数(bigint)是64位宽的有符号整数。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text, price bigint );POST /cli -d "CREATE TABLE products(title text, price bigint)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'price'=>['type'=>'bigint']
]);utilsApi.sql('CREATE TABLE products(title text, price bigint )')await utilsApi.sql('CREATE TABLE products(title text, price bigint )')res = await utilsApi.sql('CREATE TABLE products(title text, price bigint )');utilsApi.sql("CREATE TABLE products(title text, price bigint )");utilsApi.Sql("CREATE TABLE products(title text, price bigint )");utils_api.sql("CREATE TABLE products(title text, price bigint )", Some(true)).await;table products
{
type = rt
path = products
rt_field = title
stored_fields = title
rt_attr_bigint = type
}声明一个布尔属性。等同于位数为1的整数属性。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text, sold bool );POST /cli -d "CREATE TABLE products(title text, sold bool)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'sold'=>['type'=>'bool']
]);utilsApi.sql('CREATE TABLE products(title text, sold bool )')await utilsApi.sql('CREATE TABLE products(title text, sold bool )')res = await utilsApi.sql('CREATE TABLE products(title text, sold bool )');utilsApi.sql("CREATE TABLE products(title text, sold bool )");utilsApi.Sql("CREATE TABLE products(title text, sold bool )");utils_api.sql("CREATE TABLE products(title text, sold bool )", Some(true)).await;table products
{
type = rt
path = products
rt_field = title
stored_fields = title
rt_attr_bool = sold
}时间戳类型表示Unix时间戳,以32位整数存储。与基本整数不同,时间戳类型允许使用时间和日期函数。从字符串值转换遵循以下规则:
- 不带分隔符的数字,且长度至少为10个字符,按原样转换为时间戳。
%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%Y-%m-%d%Y-%m%Y
这些转换格式的含义详见strptime手册,除%E*S表示毫秒外。
注意,纯表中不支持时间戳的自动转换。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text, date timestamp);POST /cli -d "CREATE TABLE products(title text, date timestamp)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'date'=>['type'=>'timestamp']
]);utilsApi.sql('CREATE TABLE products(title text, date timestamp)')await utilsApi.sql('CREATE TABLE products(title text, date timestamp)')res = await utilsApi.sql('CREATE TABLE products(title text, date timestamp)');utilsApi.sql("CREATE TABLE products(title text, date timestamp)");utilsApi.Sql("CREATE TABLE products(title text, date timestamp)");utils_api.sql("CREATE TABLE products(title text, date timestamp)", Some(true)).await;table products
{
type = rt
path = products
rt_field = title
stored_fields = title
rt_attr_timestamp = date
}实数存储为32位IEEE 754单精度浮点数。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text, coeff float);POST /cli -d "CREATE TABLE products(title text, coeff float)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'coeff'=>['type'=>'float']
]);utilsApi.sql('CREATE TABLE products(title text, coeff float)')await utilsApi.sql('CREATE TABLE products(title text, coeff float)')res = await utilsApi.sql('CREATE TABLE products(title text, coeff float)');utilsApi.sql("CREATE TABLE products(title text, coeff float)");utilsApi.Sql("CREATE TABLE products(title text, coeff float)");utils_api.sql("CREATE TABLE products(title text, coeff float)", Some(true)).await;table products
{
type = rt
path = products
rt_field = title
stored_fields = title
rt_attr_float = coeff
}与整数类型不同,不建议直接比较两个浮点数是否相等,因为可能存在舍入误差。更可靠的做法是使用近似比较,通过检查绝对误差范围。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
select abs(a-b)<=0.00001 from productsPOST /search
{
"table": "products",
"query": { "match_all": {} } },
"expressions": { "eps": "abs(a-b)" }
}$index->setName('products')->search('')->expression('eps','abs(a-b)')->get();searchApi.search({"table":"products","query":{"match_all":{}},"expressions":{"eps":"abs(a-b)"}})await searchApi.search({"table":"products","query":{"match_all":{}},"expressions":{"eps":"abs(a-b)"}})res = await searchApi.search({"table":"products","query":{"match_all":{}},"expressions":{"eps":"abs(a-b)"}});searchRequest = new SearchRequest();
searchRequest.setIndex("forum");
query = new HashMap<String,Object>();
query.put("match_all",null);
searchRequest.setQuery(query);
Object expressions = new HashMap<String,Object>(){{
put("ebs","abs(a-b)");
}};
searchRequest.setExpressions(expressions);
searchResponse = searchApi.search(searchRequest);object query = new { match_all=null };
var searchRequest = new SearchRequest("forum", query);
searchRequest.Expressions = new List<Object>{
new Dictionary<string, string> { {"ebs", "abs(a-b)"} }
};
var searchResponse = searchApi.Search(searchRequest);let query = SearchQuery::new();
let mut expr = HashMap::new();
expr.insert("ebs".to_string(), serde_json::json!("abs(a-b)"));
let expressions: [HashMap; 1] = [expr];
let search_request = SearchRequest {
table: "forum".to_string(),
query: Some(Box::new(query)),
expressions: serde_json::json!(expressions),
..Default::default(),
};
let search_res = search_api.search(search_req).await;另一个替代方案,也可用于执行IN(attr,val1,val2,val3),是通过选择一个乘数因子将浮点数转换为整数进行比较操作。以下例子展示了如何将IN(attr,2.0,2.5,3.5)修改为整型值。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
select in(ceil(attr*100),200,250,350) from productsPOST /search
{
"table": "products",
"query": { "match_all": {} } },
"expressions": { "inc": "in(ceil(attr*100),200,250,350)" }
}$index->setName('products')->search('')->expression('inc','in(ceil(attr*100),200,250,350)')->get();searchApi.search({"table":"products","query":{"match_all":{}}},"expressions":{"inc":"in(ceil(attr*100),200,250,350)"}})await searchApi.search({"table":"products","query":{"match_all":{}}},"expressions":{"inc":"in(ceil(attr*100),200,250,350)"}})res = await searchApi.search({"table":"products","query":{"match_all":{}}},"expressions":{"inc":"in(ceil(attr*100),200,250,350)"}});searchRequest = new SearchRequest();
searchRequest.setIndex("forum");
query = new HashMap<String,Object>();
query.put("match_all",null);
searchRequest.setQuery(query);
Object expressions = new HashMap<String,Object>(){{
put("inc","in(ceil(attr*100),200,250,350)");
}};
searchRequest.setExpressions(expressions);
searchResponse = searchApi.search(searchRequest);object query = new { match_all=null };
var searchRequest = new SearchRequest("forum", query);
searchRequest.Expressions = new List<Object> {
new Dictionary<string, string> { {"ebs", "in(ceil(attr*100),200,250,350)"} }
};
var searchResponse = searchApi.Search(searchRequest);let query = SearchQuery::new();
let mut expr = HashMap::new();
expr.insert("ebs".to_string(), serde_json::json!("in(ceil(attr*100),200,250,350)"));
let expressions: [HashMap; 1] = [expr];
let search_request = SearchRequest {
table: "forum".to_string(),
query: Some(Box::new(query)),
expressions: serde_json::json!(expressions),
..Default::default(),
};
let search_res = search_api.search(search_req).await;浮点值在Manticore中以精确的方式显示,以确保它们反映存储的确切值。这种方法是为了防止精度丢失,特别是在地理坐标等情况下,四舍五入到6位小数会导致不准确。
现在,Manticore首先输出一个带有6位数字的数字,然后解析并将其与原始值进行比较。如果不匹配,则会添加更多位数,直到匹配为止。
例如,如果浮点值插入为19.45,Manticore将显示为19.450001以准确表示存储的值。
- Example
insert into t(id, f) values(1, 19.45)
--------------
Query OK, 1 row affected (0.02 sec)
--------------
select * from t
--------------
+------+-----------+
| id | f |
+------+-----------+
| 1 | 19.450001 |
+------+-----------+
1 row in set (0.00 sec)
--- 1 out of 1 results in 0ms ---此数据类型允许存储JSON对象,特别适用于处理无模式数据。在定义JSON值时,请确保对象的开闭花括号{和}包含,或数组的开闭方括号[和]包含。虽然JSON不支持列存储,但它可以存储在传统的行存储中。值得注意的是,这两种存储类型可以在同一表中结合使用。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text, data json);POST /cli -d "CREATE TABLE products(title text, data json)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'data'=>['type'=>'json']
]);utilsApi.sql('CREATE TABLE products(title text, data json)')await utilsApi.sql('CREATE TABLE products(title text, data json)')res = await utilsApi.sql('CREATE TABLE products(title text, data json)');utilsApi.sql("CREATE TABLE products(title text, data json)");utilsApi.Sql("CREATE TABLE products(title text, data json)");utils_api.sql("CREATE TABLE products(title text, data json)", Some(true)).await;table products
{
type = rt
path = products
rt_field = title
stored_fields = title
rt_attr_json = data
}JSON属性可以在大多数操作中使用。还有一些特殊函数,如ALL()、ANY()、GREATEST()、LEAST()和INDEXOF(),允许遍历属性数组。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
select indexof(x>2 for x in data.intarray) from productsPOST /search
{
"table": "products",
"query": { "match_all": {} } },
"expressions": { "idx": "indexof(x>2 for x in data.intarray)" }
}$index->setName('products')->search('')->expression('idx','indexof(x>2 for x in data.intarray)')->get();searchApi.search({"table":"products","query":{"match_all":{}},"expressions":{"idx":"indexof(x>2 for x in data.intarray)"}})await searchApi.search({"table":"products","query":{"match_all":{}},"expressions":{"idx":"indexof(x>2 for x in data.intarray)"}})res = await searchApi.search({"table":"products","query":{"match_all":{}},"expressions":{"idx":"indexof(x>2 for x in data.intarray)"}});searchRequest = new SearchRequest();
searchRequest.setIndex("forum");
query = new HashMap<String,Object>();
query.put("match_all",null);
searchRequest.setQuery(query);
Object expressions = new HashMap<String,Object>(){{
put("idx","indexof(x>2 for x in data.intarray)");
}};
searchRequest.setExpressions(expressions);
searchResponse = searchApi.search(searchRequest);object query = new { match_all=null };
var searchRequest = new SearchRequest("forum", query);
searchRequest.Expressions = new List<Object> {
new Dictionary<string, string> { {"idx", "indexof(x>2 for x in data.intarray)"} }
};
var searchResponse = searchApi.Search(searchRequest);let query = SearchQuery::new();
let mut expr = HashMap::new();
expr.insert("idx".to_string(), serde_json::json!("indexof(x>2 for x in data.intarray)"));
let expressions: [HashMap; 1] = [expr];
let search_request = SearchRequest {
table: "forum".to_string(),
query: Some(Box::new(query)),
expressions: serde_json::json!(expressions),
..Default::default(),
};
let search_res = search_api.search(search_req).await;文本属性与字符串相同,因此无法在全文匹配表达式中使用。但是,可以使用字符串函数,如REGEX()。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
select regex(data.name, 'est') as c from products where c>0POST /search
{
"table": "products",
"query":
{
"match_all": {},
"range": { "c": { "gt": 0 } } }
},
"expressions": { "c": "regex(data.name, 'est')" }
}$index->setName('products')->search('')->expression('idx',"regex(data.name, 'est')")->filter('c','gt',0)->get();searchApi.search({"table":"products","query":{"match_all":{},"range":{"c":{"gt":0}}}},"expressions":{"c":"regex(data.name, 'est')"}})await searchApi.search({"table":"products","query":{"match_all":{},"range":{"c":{"gt":0}}}},"expressions":{"c":"regex(data.name, 'est')"}})res = await searchApi.search({"table":"products","query":{"match_all":{},"range":{"c":{"gt":0}}}},"expressions":{"c":"regex(data.name, 'est')"}}});searchRequest = new SearchRequest();
searchRequest.setIndex("forum");
query = new HashMap<String,Object>();
query.put("match_all",null);
query.put("range", new HashMap<String,Object>(){{
put("c", new HashMap<String,Object>(){{
put("gt",0);
}});
}});
searchRequest.setQuery(query);
Object expressions = new HashMap<String,Object>(){{
put("idx","indexof(x>2 for x in data.intarray)");
}};
searchRequest.setExpressions(expressions);
searchResponse = searchApi.search(searchRequest);object query = new { match_all=null };
var searchRequest = new SearchRequest("forum", query);
var rangeFilter = new RangeFilter("c");
rangeFilter.Gt = 0;
searchRequest.AttrFilter = rangeFilter;
searchRequest.Expressions = new List<Object> {
new Dictionary<string, string> { {"idx", "indexof(x>2 for x in data.intarray)"} }
};
var searchResponse = searchApi.Search(searchRequest);let query = SearchQuery::new();
let mut expr = HashMap::new();
expr.insert("idx".to_string(), serde_json::json!("indexof(x>2 for x in data.intarray)"));
let expressions: [HashMap; 1] = [expr];
let search_request = SearchRequest {
table: "forum".to_string(),
query: Some(Box::new(query)),
expressions: serde_json::json!(expressions),
..Default::default(),
};
let search_res = search_api.search(search_req).await;在处理浮点值的情况下,可能需要强制执行数据类型以确保某些情况下的正确功能。例如,当处理浮点值时,必须使用DOUBLE()以确保正确的排序。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
select * from products order by double(data.myfloat) descPOST /search
{
"table": "products",
"query": { "match_all": {} } },
"sort": [ { "double(data.myfloat)": { "order": "desc"} } ]
}$index->setName('products')->search('')->sort('double(data.myfloat)','desc')->get();searchApi.search({"table":"products","query":{"match_all":{}}},"sort":[{"double(data.myfloat)":{"order":"desc"}}]})await searchApi.search({"table":"products","query":{"match_all":{}}},"sort":[{"double(data.myfloat)":{"order":"desc"}}]})res = await searchApi.search({"table":"products","query":{"match_all":{}}},"sort":[{"double(data.myfloat)":{"order":"desc"}}]});searchRequest = new SearchRequest();
searchRequest.setIndex("forum");
query = new HashMap<String,Object>();
query.put("match_all",null);
searchRequest.setQuery(query);
searchRequest.setSort(new ArrayList<Object>(){{
add(new HashMap<String,String>(){{ put("double(data.myfloat)",new HashMap<String,String>(){{ put("order","desc");}});}});
}});
searchResponse = searchApi.search(searchRequest);object query = new { match_all=null };
var searchRequest = new SearchRequest("forum", query);
searchRequest.Sort = new List<Object> {
new SortOrder("double(data.myfloat)", SortOrder.OrderEnum.Desc)
};
var searchResponse = searchApi.Search(searchRequest);let query = SearchQuery::new();
let mut sort = HashMap::new();
sort.insert("double(data.myfloat)".to_string(), serde_json::json!("desc"));
let search_request = SearchRequest {
table: "forum".to_string(),
query: Some(Box::new(query)),
sort: serde_json::json!(sort)
..Default::default()
};
let search_res = search_api.search(search_req).await;浮点向量属性允许存储可变长度的浮点数列表,主要用于机器学习应用和相似性搜索。此类型与多值整数(MVA)(MVAs)在几个重要方面不同:
- 保留值的确切顺序(与MVAs不同,MVAs可能会重新排序)
- 保留重复值(与MVAs不同,MVAs会去重)
- 插入时无需额外处理(与MVAs不同,MVAs会排序和去重)
重要: float_vector 数据类型与 自动模式 机制不兼容。
- 目前仅支持实时表(不支持普通表)
- 其他函数或表达式中不支持
- 不能在常规过滤器或排序中使用
当你配置 float_vector 属性并设置 KNN 参数时,你将启用向量相似性搜索功能。这允许你执行 k-最近邻搜索,根据向量距离查找相似文档。
你可以做到:
- 执行 KNN(k-最近邻)向量搜索以查找相似文档
- 构建语义搜索、推荐和 AI 驱动的功能
- 使用自动嵌入从文本中自动生成向量
你不能做到:
UPDATEfloat_vector值(必须使用REPLACE代替)- 在常规过滤器或排序中使用浮点向量
- 除了通过向量搜索操作,不能通过
float_vector值进行过滤
当你创建带有 float_vector 属性的表以进行 KNN 搜索时,可以指定以下参数:
必填参数:
KNN_TYPE:目前仅支持'hnsw'KNN_DIMS:向量的维度数(手动插入向量时必需,使用MODEL_NAME时可省略)HNSW_SIMILARITY:距离函数 -'l2'、'ip'(内积)或'cosine'
可选参数:
HNSW_M:图中的最大连接数(默认:16)HNSW_EF_CONSTRUCTION:构建时间/准确性权衡(默认:200)
自动嵌入参数(当使用 MODEL_NAME 时):
MODEL_NAME:使用的嵌入模型(例如,'sentence-transformers/all-MiniLM-L6-v2'、'openai/text-embedding-ada-002')FROM:用于生成嵌入的字段名列表(逗号分隔),或空字符串''表示使用所有文本/字符串字段API_KEY:基于 API 的模型(OpenAI、Voyage、Jina)的 API 密钥
有关设置浮点向量并在搜索中使用它们的详细信息,请参阅KNN搜索。
处理浮点向量最简便的方式是使用自动嵌入。此功能使用机器学习模型自动从您的文本数据生成嵌入,从而消除手动计算和插入向量的需要。
- 简化的工作流程:只需插入文本,嵌入会自动生成
- 无需手动计算向量:无需运行单独的嵌入模型
- 一致的嵌入:相同的模型确保一致的向量表示
- 多模型支持:可选择 sentence-transformers、Qwen 嵌入模型、OpenAI、Voyage 和 Jina 模型
- 灵活的字段选择:控制用于生成嵌入的字段
在创建带有自动嵌入的表时,指定以下附加参数:
MODEL_NAME:用于自动向量生成的嵌入模型FROM:用于生成嵌入的字段(空字符串表示所有文本/字符串字段)
支持的嵌入模型:
- Sentence Transformers:任何 适合的 BERT 基础 Hugging Face 模型(例如,
sentence-transformers/all-MiniLM-L6-v2)——无需 API 密钥。Manticore 在创建表时下载模型。 - Qwen 本地嵌入:Qwen 嵌入模型,如
Qwen/Qwen3-Embedding-0.6B——不需要 API 密钥。Manticore 在您创建表时下载模型。 - OpenAI:OpenAI 嵌入模型,如
openai/text-embedding-ada-002- 需要API_KEY='<OPENAI_API_KEY>'参数 - Voyage:Voyage AI 嵌入模型 - 需要
API_KEY='<VOYAGE_API_KEY>'参数 - Jina:Jina AI 嵌入模型 - 需要
API_KEY='<JINA_API_KEY>'参数
- SQL
使用 sentence-transformers 模型(无需 API 密钥)
CREATE TABLE products (
title TEXT,
description TEXT,
embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
MODEL_NAME='sentence-transformers/all-MiniLM-L6-v2' FROM='title'
);使用 Qwen 本地嵌入(不需要 API 密钥)
CREATE TABLE products_qwen (
title TEXT,
description TEXT,
embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
MODEL_NAME='Qwen/Qwen3-Embedding-0.6B' FROM='title'
);使用 OpenAI 模型(需要 API_KEY 参数)
CREATE TABLE products_openai (
title TEXT,
content TEXT,
embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='cosine'
MODEL_NAME='openai/text-embedding-ada-002' FROM='title,content' API_KEY='<OPENAI_API_KEY>'
);使用所有文本字段进行嵌入(FROM 为空)
CREATE TABLE products_all_fields (
title TEXT,
description TEXT,
tags TEXT,
embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
MODEL_NAME='sentence-transformers/all-MiniLM-L6-v2' FROM=''
);FROM 参数控制用于生成嵌入的字段:
- 特定字段:
FROM='title'- 仅使用标题字段 - 多个字段:
FROM='title,description'- 将标题和描述字段连接并使用 - 所有文本字段:
FROM=''(空)- 表中的所有text(全文字段)和string(字符串属性)字段用于嵌入生成 - 空向量:仍然可以使用
()插入空向量以排除文档从向量搜索
使用自动嵌入时,不要在 INSERT 语句中指定向量字段。嵌入会自动从指定的文本字段生成:
-- Insert text data - embeddings generated automatically
INSERT INTO products (title, description) VALUES
('smartphone', 'latest mobile device with camera'),
('laptop computer', 'portable workstation for developers');
-- Insert with empty vector (excluded from vector search)
INSERT INTO products (title, description, embedding_vector) VALUES
('no-vector item', 'this item has no embedding', ());
或者,你可以手动插入预计算的向量数据。这需要你使用外部工具或模型自行计算向量,然后将其插入到 Manticore 中。
重要提示: 当使用 HNSW_SIMILARITY='cosine' 时,插入时向量会自动归一化为单位向量(数学长度/幅度为 1.0 的向量)。这种归一化保留了向量的方向,同时标准化了其长度,这是高效余弦相似性计算所必需的。这意味着存储的值将与你的原始输入值不同。
- SQL
CREATE TABLE products (
title TEXT,
image_vector FLOAT_VECTOR KNN_TYPE='hnsw' KNN_DIMS='4' HNSW_SIMILARITY='l2'
);
INSERT INTO products VALUES
(1, 'yellow bag', (0.653448,0.192478,0.017971,0.339821)),
(2, 'white bag', (-0.148894,0.748278,0.091892,-0.095406));你也可以创建没有 KNN 配置的 float_vector 属性。在这种模式下,向量被存储但不能用于向量搜索操作。
你可以做到:
- 存储向量数据
UPDATEfloat_vector值(与 KNN 不同,此时可以使用UPDATE)
你不能做到:
- 执行 KNN 搜索或向量相似性搜索
- 使用向量进行任何搜索操作
- 通过
float_vector值进行过滤
- 在配置KNN之前临时存储向量数据
- 用于后续与KNN一起使用的暂存数据
- 存储不需要搜索功能的向量
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text, image_vector float_vector);POST /cli -d "CREATE TABLE products(title text, image_vector float_vector)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'image_vector'=>['type'=>'float_vector']
]);utilsApi.sql('CREATE TABLE products(title text, image_vector float_vector)')await utilsApi.sql('CREATE TABLE products(title text, image_vector float_vector)')res = await utilsApi.sql('CREATE TABLE products(title text, image_vector float_vector)');utilsApi.sql("CREATE TABLE products(title text, image_vector float_vector)");utilsApi.Sql("CREATE TABLE products(title text, image_vector float_vector)");utils_api.sql("CREATE TABLE products(title text, image_vector float_vector)", Some(true)).await;table products
{
type = rt
path = products
rt_field = title
stored_fields = title
rt_attr_float_vector = image_vector
}多值属性允许存储可变长度的32位无符号整数列表。这在存储一对多的数值时非常有用,例如标签、产品类别和属性。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text, product_codes multi);或
CREATE TABLE products(title text, product_codes mva);POST /cli -d "CREATE TABLE products(title text, product_codes multi)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'product_codes'=>['type'=>'multi']
]);utilsApi.sql('CREATE TABLE products(title text, product_codes multi)')await utilsApi.sql('CREATE TABLE products(title text, product_codes multi)')res = await utilsApi.sql('CREATE TABLE products(title text, product_codes multi)');utilsApi.sql("CREATE TABLE products(title text, product_codes multi)");utilsApi.Sql("CREATE TABLE products(title text, product_codes multi)");utils_api.sql("CREATE TABLE products(title text, product_codes multi)", Some(true)).await;table products
{
type = rt
path = products
rt_field = title
stored_fields = title
rt_attr_multi = product_codes
}- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
select * from products where any(product_codes)=3POST /search
{
"table": "products",
"query":
{
"match_all": {},
"equals" : { "any(product_codes)": 3 }
}
}$index->setName('products')->search('')->filter('any(product_codes)','equals',3)->get();searchApi.search({"table":"products","query":{"match_all":{},"equals":{"any(product_codes)":3}}}})await searchApi.search({"table":"products","query":{"match_all":{},"equals":{"any(product_codes)":3}}}})res = await searchApi.search({"table":"products","query":{"match_all":{},"equals":{"any(product_codes)":3}}}})'searchRequest = new SearchRequest();
searchRequest.setIndex("forum");
query = new HashMap<String,Object>();
query.put("match_all",null);
query.put("equals",new HashMap<String,Integer>(){{
put("any(product_codes)",3);
}});
searchRequest.setQuery(query);
searchResponse = searchApi.search(searchRequest);object query = new { match_all=null };
var searchRequest = new SearchRequest("forum", query);
searchRequest.AttrFilter = new EqualsFilter("any(product_codes)", 3);
var searchResponse = searchApi.Search(searchRequest);let query = SearchQuery::new();
let search_request = SearchRequest {
table: "forum".to_string(),
query: Some(Box::new(query)),
..Default::default(),
};
let search_res = search_api.search(search_req).await;- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
select least(product_codes) l from products order by l ascPOST /search
{
"table": "products",
"query":
{
"match_all": {},
"sort": [ { "product_codes":{ "order":"asc", "mode":"min" } } ]
}
}$index->setName('products')->search('')->sort('product_codes','asc','min')->get();searchApi.search({"table":"products","query":{"match_all":{},"sort":[{"product_codes":{"order":"asc","mode":"min"}}]}})await searchApi.search({"table":"products","query":{"match_all":{},"sort":[{"product_codes":{"order":"asc","mode":"min"}}]}})res = await searchApi.search({"table":"products","query":{"match_all":{},"sort":[{"product_codes":{"order":"asc","mode":"min"}}]}});searchRequest = new SearchRequest();
searchRequest.setIndex("forum");
query = new HashMap<String,Object>();
query.put("match_all",null);
searchRequest.setQuery(query);
searchRequest.setSort(new ArrayList<Object>(){{
add(new HashMap<String,String>(){{ put("product_codes",new HashMap<String,String>(){{ put("order","asc");put("mode","min");}});}});
}});
searchResponse = searchApi.search(searchRequest);object query = new { match_all=null };
var searchRequest = new SearchRequest("forum", query);
searchRequest.Sort = new List<Object> {
new SortMVA("product_codes", SortOrder.OrderEnum.Asc, SortMVA.ModeEnum.Min)
};
searchResponse = searchApi.Search(searchRequest);let query = SearchQuery::new();
let mut sort_opts = HashMap::new();
sort_opts.insert("order".to_string(), serde_json::json!("asc"));
sort_opts.insert("mode".to_string(), serde_json::json!("min"));
sort_expr.insert("product_codes".to_string(), serde_json::json!(sort_opts));
let sort: [HashMap; 1] = [sort_expr];
let search_req = SearchRequest {
table: "forum".to_string(),
query: Some(Box::new(query)),
sort: serde_json::json!(sort),
..Default::default(),
};
let search_res = search_api.search(search_req).await;当按多值属性分组时,一个文档将贡献给与该文档关联的不同值数量相同的多个组。例如,如果一个集合恰好包含一个文档,其'product_codes'多值属性的值为5、7和11,那么按'product_codes'分组将产生3个组,COUNT(*)等于1,GROUPBY()键值分别为5、7和11。另外请注意,按多值属性分组可能导致结果集中出现重复文档,因为每个文档可以参与多个组。
- SQL
insert into products values ( 1, 'doc one', (5,7,11) );
select id, count(*), groupby() from products group by product_codes;Query OK, 1 row affected (0.00 sec)
+------+----------+-----------+
| id | count(*) | groupby() |
+------+----------+-----------+
| 1 | 1 | 11 |
| 1 | 1 | 7 |
| 1 | 1 | 5 |
+------+----------+-----------+
3 rows in set (0.00 sec)作为多值属性值插入的数字顺序不会被保留。值在内部以排序集合的形式存储。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
insert into product values (1,'first',(4,2,1,3));
select * from products;POST /insert
{
"table":"products",
"id":1,
"doc":
{
"title":"first",
"product_codes":[4,2,1,3]
}
}
POST /search
{
"table": "products",
"query": { "match_all": {} }
}$index->addDocument([
"title"=>"first",
"product_codes"=>[4,2,1,3]
]);
$index->search('')-get();indexApi.insert({"table":"products","id":1,"doc":{"title":"first","product_codes":[4,2,1,3]}})
searchApi.search({"table":"products","query":{"match_all":{}}})await indexApi.insert({"table":"products","id":1,"doc":{"title":"first","product_codes":[4,2,1,3]}})
await searchApi.search({"table":"products","query":{"match_all":{}}})await indexApi.insert({"table":"products","id":1,"doc":{"title":"first","product_codes":[4,2,1,3]}});
res = await searchApi.search({"table":"products","query":{"match_all":{}}});InsertDocumentRequest newdoc = new InsertDocumentRequest();
HashMap<String,Object> doc = new HashMap<String,Object>(){{
put("title","first");
put("product_codes",new int[] {4,2,1,3});
}};
newdoc.index("products").id(1L).setDoc(doc);
sqlresult = indexApi.insert(newdoc);
Map<String,Object> query = new HashMap<String,Object>();
query.put("match_all",null);
SearchRequest searchRequest = new SearchRequest();
searchRequest.setIndex("products");
searchRequest.setQuery(query);
SearchResponse searchResponse = searchApi.search(searchRequest);
System.out.println(searchResponse.toString() );Dictionary<string, Object> doc = new Dictionary<string, Object>();
doc.Add("title", "first");
doc.Add("product_codes", new List<Object> {4,2,1,3});
InsertDocumentRequest newdoc = new InsertDocumentRequest(index: "products", id: 1, doc: doc);
var sqlresult = indexApi.Insert(newdoc);
object query = new { match_all=null };
var searchRequest = new SearchRequest("products", query);
var searchResponse = searchApi.Search(searchRequest);
Console.WriteLine(searchResponse.ToString())let mut doc = HashMap::new();
doc.insert("title".to_string(), serde_json::json!("first"));
doc.insert("product_codes".to_string(), serde_json::json!([4,2,1,3]));
let insert_req = InsertDocumentRequest::new("products".to_string(), serde_json::json!(doc));
let insert_res = index_api.insert(insert_req).await;
let query = SearchQuery::new();
let search_req = SearchRequest {
table: "forum".to_string(),
query: Some(Box::new(query)),
..Default::default(),
};
let search_res = search_api.search(search_req).await;
println!("{:?}", search_res);Query OK, 1 row affected (0.00 sec)
+------+---------------+-------+
| id | product_codes | title |
+------+---------------+-------+
| 1 | 1,2,3,4 | first |
+------+---------------+-------+
1 row in set (0.01 sec){
"table":"products",
"_id":1,
"created":true,
"result":"created",
"status":201
}
{
"took":0,
"timed_out":false,
"hits":{
"total":1,
"hits":[
{
"_id": 1,
"_score":1,
"_source":{
"product_codes":[
1,
2,
3,
4
],
"title":"first"
}
}
]
}
}Array
(
[_index] => products
[_id] => 1
[created] => 1
[result] => created
[status] => 201
)
Array
(
[took] => 0
[timed_out] =>
[hits] => Array
(
[total] => 1
[hits] => Array
(
[0] => Array
(
[_id] => 1
[_score] => 1
[_source] => Array
(
[product_codes] => Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
)
[title] => first
)
)
)
)
){'created': True,
'found': None,
'id': 1,
'table': 'products',
'result': 'created'}
{'hits': {'hits': [{u'_id': u'1',
u'_score': 1,
u'_source': {u'product_codes': [1, 2, 3, 4],
u'title': u'first'}}],
'total': 1},
'profile': None,
'timed_out': False,
'took': 29}{'created': True,
'found': None,
'id': 1,
'table': 'products',
'result': 'created'}
{'hits': {'hits': [{u'_id': u'1',
u'_score': 1,
u'_source': {u'product_codes': [1, 2, 3, 4],
u'title': u'first'}}],
'total': 1},
'profile': None,
'timed_out': False,
'took': 29}{"took":0,"timed_out":false,"hits":{"total":1,"hits":[{"_id": 1,"_score":1,"_source":{"product_codes":[1,2,3,4],"title":"first"}}]}}class SearchResponse {
took: 0
timedOut: false
hits: class SearchResponseHits {
total: 1
hits: [{_id=1, _score=1, _source={product_codes=[1, 2, 3, 4], title=first}}]
aggregations: null
}
profile: null
}class SearchResponse {
took: 0
timedOut: false
hits: class SearchResponseHits {
total: 1
hits: [{_id=1, _score=1, _source={product_codes=[1, 2, 3, 4], title=first}}]
aggregations: null
}
profile: null
}class SearchResponse {
took: 0
timedOut: false
hits: class SearchResponseHits {
total: 1
hits: [{_id=1, _score=1, _source={product_codes=[1, 2, 3, 4], title=first}}]
aggregations: null
}
profile: null
}一种允许存储可变长度64位有符号整数列表的数据类型。其功能与多值整数相同。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text, values multi64);或
CREATE TABLE products(title text, values mva64);POST /cli -d "CREATE TABLE products(title text, values multi64)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'values'=>['type'=>'multi64']
]);utilsApi.sql('CREATE TABLE products(title text, values multi64))')await utilsApi.sql('CREATE TABLE products(title text, values multi64))')res = await utilsApi.sql('CREATE TABLE products(title text, values multi64))');utilsApi.sql("CREATE TABLE products(title text, values multi64))");utilsApi.Sql("CREATE TABLE products(title text, values multi64))");utils_api.sql("CREATE TABLE products(title text, values multi64))", Some(true)).await;table products
{
type = rt
path = products
rt_field = title
stored_fields = title
rt_attr_multi_64 = values
}当使用列式存储时,可以为属性指定以下特性。
默认情况下,Manticore 列式存储以列式方式存储所有属性,同时也在特殊的文档存储中按行存储。这使得像 SELECT * FROM ... 这样的查询能够快速执行,尤其是在一次获取大量记录时。但是,如果您确定不需要此功能或希望节省磁盘空间,可以在创建表时通过指定 fast_fetch='0' 来禁用它,或者(如果您在配置中定义表)使用 columnar_no_fast_fetch,如下例所示。
- RT mode
- Plain mode
create table t(a int, b int fast_fetch='0') engine='columnar'; desc t;source min {
type = mysql
sql_host = localhost
sql_user = test
sql_pass =
sql_db = test
sql_query = select 1, 1 a, 1 b
sql_attr_uint = a
sql_attr_uint = b
}
table tbl {
path = tbl/col
source = min
columnar_attrs = *
columnar_no_fast_fetch = b
}+-------+--------+---------------------+
| Field | Type | Properties |
+-------+--------+---------------------+
| id | bigint | columnar fast_fetch |
| a | uint | columnar fast_fetch |
| b | uint | columnar |
+-------+--------+---------------------+
3 rows in set (0.00 sec)+-------+--------+---------------------+
| Field | Type | Properties |
+-------+--------+---------------------+
| id | bigint | columnar fast_fetch |
| a | uint | columnar fast_fetch |
| b | uint | columnar |
+-------+--------+---------------------+Manticore 的数据类型可以分为两类:全文字段和属性。
Manticore 中的字段名必须遵循以下规则:
- 可以包含字母(a-z, A-Z)、数字(0-9)和连字符(-)
- 必须以字母开头
- 数字只能出现在字母之后
- 下划线(
_)是唯一允许的特殊字符 - 字段名不区分大小写
例如:
- 有效的字段名:
title、product_id、user_name_2 - 无效的字段名:
2title、-price、user@name
全文字段:
- 可以使用自然语言处理算法进行索引,因此可以搜索关键词
- 不能用于排序或分组
- 可以检索原始文档内容
- 原始文档内容可用于高亮显示
全文字段由数据类型 text 表示。所有其他数据类型都称为“属性”。
属性是与每个文档关联的非全文值,可用于在搜索期间执行非全文过滤、排序和分组。
通常,不仅需要根据匹配的文档 ID 及其排名来处理全文搜索结果,还需要基于许多其他每文档值进行处理。例如,可能需要按日期和相关性对新闻搜索结果进行排序,或在指定价格范围内搜索产品,或将博客搜索限制在选定用户发布的帖子中,或按月份对结果进行分组。为了实现这一点,Manticore 不仅支持全文字段,还允许为每个文档添加额外的属性。这些属性可用于过滤、排序或分组全文匹配项,或仅按属性进行搜索。
与全文字段不同,属性不进行全文索引。它们存储在表中,但无法像全文那样进行搜索。
属性的一个很好的例子是论坛帖子表。假设只有标题和内容字段需要进行全文搜索——但有时还需要将搜索限制在特定作者或子论坛(即,仅搜索那些具有特定 author_id 或 forum_id 值的行);或按 post_date 列对匹配项进行排序;或按 post_date 的月份对匹配的帖子进行分组并计算每组的匹配计数。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- Javascript
- Java
- C#
- Rust
- config
CREATE TABLE forum(title text, content text, author_id int, forum_id int, post_date timestamp);POST /cli -d "CREATE TABLE forum(title text, content text, author_id int, forum_id int, post_date timestamp)"$index = new \Manticoresearch\Index($client);
$index->setName('forum');
$index->create([
'title'=>['type'=>'text'],
'content'=>['type'=>'text'],
'author_id'=>['type'=>'int'],
'forum_id'=>['type'=>'int'],
'post_date'=>['type'=>'timestamp']
]);utilsApi.sql('CREATE TABLE forum(title text, content text, author_id int, forum_id int, post_date timestamp)')await utilsApi.sql('CREATE TABLE forum(title text, content text, author_id int, forum_id int, post_date timestamp)')res = await utilsApi.sql('CREATE TABLE forum(title text, content text, author_id int, forum_id int, post_date timestamp)');utilsApi.sql("CREATE TABLE forum(title text, content text, author_id int, forum_id int, post_date timestamp)");utilsApi.Sql("CREATE TABLE forum(title text, content text, author_id int, forum_id int, post_date timestamp)");utils_api.sql("CREATE TABLE forum(title text, content text, author_id int, forum_id int, post_date timestamp)", Some(true)).await;table forum
{
type = rt
path = forum
# when configuring fields via config, they are indexed (and not stored) by default
rt_field = title
rt_field = content
# this option needs to be specified for the field to be stored
stored_fields = title, content
rt_attr_uint = author_id
rt_attr_uint = forum_id
rt_attr_timestamp = post_date
}此示例展示了按 author_id、forum_id 过滤并按 post_date 排序的全文查询。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
select * from forum where author_id=123 and forum_id in (1,3,7) order by post_date descPOST /search
{
"table": "forum",
"query":
{
"match_all": {},
"bool":
{
"must":
[
{ "equals": { "author_id": 123 } },
{ "in": { "forum_id": [1,3,7] } }
]
}
},
"sort": [ { "post_date": "desc" } ]
}$client->search([
'table' => 'forum',
'query' =>
[
'match_all' => [],
'bool' => [
'must' => [
'equals' => ['author_id' => 123],
'in' => [
'forum_id' => [
1,3,7
]
]
]
]
],
'sort' => [
['post_date' => 'desc']
]
]);searchApi.search({"table":"forum","query":{"match_all":{},"bool":{"must":[{"equals":{"author_id":123}},{"in":{"forum_id":[1,3,7]}}]}},"sort":[{"post_date":"desc"}]})await searchApi.search({"table":"forum","query":{"match_all":{},"bool":{"must":[{"equals":{"author_id":123}},{"in":{"forum_id":[1,3,7]}}]}},"sort":[{"post_date":"desc"}]})res = await searchApi.search({"table":"forum","query":{"match_all":{},"bool":{"must":[{"equals":{"author_id":123}},{"in":{"forum_id":[1,3,7]}}]}},"sort":[{"post_date":"desc"}]});HashMap<String,Object> filters = new HashMap<String,Object>(){{
put("must", new HashMap<String,Object>(){{
put("equals",new HashMap<String,Integer>(){{
put("author_id",123);
}});
put("in",
new HashMap<String,Object>(){{
put("forum_id",new int[] {1,3,7});
}});
}});
}};
Map<String,Object> query = new HashMap<String,Object>();
query.put("match_all",null);
query.put("bool",filters);
SearchRequest searchRequest = new SearchRequest();
searchRequest.setIndex("forum");
searchRequest.setQuery(query);
searchRequest.setSort(new ArrayList<Object>(){{
add(new HashMap<String,String>(){{ put("post_date","desc");}});
}});
SearchResponse searchResponse = searchApi.search(searchRequest);object query = new { match_all=null };
var searchRequest = new SearchRequest("forum", query);
var boolFilter = new BoolFilter();
boolFilter.Must = new List<Object> {
new EqualsFilter("author_id", 123),
new InFilter("forum_id", new List<Object> {1,3,7})
};
searchRequest.AttrFilter = boolFilter;
searchRequest.Sort = new List<Object> { new SortOrder("post_date", SortOrder.OrderEnum.Desc) };
var searchResponse = searchApi.Search(searchRequest);let query = SearchQuery::new();
let mut sort = HashMap::new();
sort.insert("post_date".to_string(), serde_json::json!("desc"));
let search_request = SearchRequest {
table: "forum".to_string(),
query: Some(Box::new(query)),
sort: serde_json::json!(sort)
..Default::default()
};
let search_res = search_api.search(search_req).await;Manticore 支持两种类型的属性存储:
- 行式 - Manticore Search 开箱即用的传统存储
- 列式 - 由 Manticore 列式库 提供
从它们的名称可以理解,它们以不同的方式存储数据。传统的 行式存储:
- 以未压缩的方式存储属性
- 同一文档的所有属性存储在彼此靠近的一行中
- 行按顺序存储
- 访问属性基本上是通过将行 ID 乘以步幅(单个向量的长度)并从计算出的内存位置获取请求的属性来实现。这提供了非常低的随机访问延迟。
- 属性必须位于内存中才能获得可接受的性能,否则由于存储的行式特性,Manticore 可能不得不从磁盘读取太多不需要的数据,这在许多情况下是次优的。
使用 列式存储:
- 每个属性都独立于所有其他属性存储在其单独的“列”中
- 存储被分成 65536 个条目的块
- 块以压缩形式存储。这通常允许仅存储几个不同的值,而不是像行式存储那样存储所有值。高压缩比允许更快地从磁盘读取,并大大降低内存需求
- 当数据被索引时,每个块独立选择存储方案。例如,如果一个块中的所有值都相同,则采用“常量”存储,整个块只存储一个值。如果每个块的唯一值少于 256 个,则采用“表”存储,存储值表的索引而不是值本身
- 如果明确请求的值不在块中,可以提前拒绝在块中的搜索。
列式存储旨在处理无法放入 RAM 的大数据量,因此建议如下:
- 如果您有足够的内存容纳所有属性,您将从行式存储中受益
- 否则,列式存储仍然可以为您提供不错的性能,同时内存占用低得多,这将允许您在表中存储更多的文档
传统的行式存储是默认方式,因此如果您希望所有内容都以行式存储,创建表时无需进行任何操作。
要启用列式存储,您需要:
- 在 CREATE TABLE 中指定
engine='columnar',使表的所有属性变为列式。然后,如果您希望保留特定属性为行式,需要在声明该属性时添加engine='rowwise'。例如:create table tbl(title text, type int, price float engine='rowwise') engine='columnar' - 在
CREATE TABLE中为特定属性指定engine='columnar',使其变为列式。例如:create table tbl(title text, type int, price float engine='columnar');或
create table tbl(title text, type int, price float engine='columnar') engine='rowwise'; - 在 plain 模式 下,您需要在 columnar_attrs 中列出希望为列式的属性。
以下是 Manticore Search 支持的数据类型列表:
文档标识符是一个必需的属性,必须是唯一的 64 位无符号整数。创建表时可以显式指定文档 ID,但即使未指定,文档 ID 也始终会被启用。文档 ID 无法更新。
创建表时,您可以显式指定 ID,但无论您使用何种数据类型,其行为始终如上所述——以无符号 64 位存储,但以有符号 64 位整数形式暴露。
mysql> CREATE TABLE tbl(id bigint, content text);
DESC tbl;
+---------+--------+----------------+
| Field | Type | Properties |
+---------+--------+----------------+
| id | bigint | |
| content | text | indexed stored |
+---------+--------+----------------+
2 rows in set (0.00 sec)
您也可以完全省略指定 ID,它将自动启用。
mysql> CREATE TABLE tbl(content text);
DESC tbl;
+---------+--------+----------------+
| Field | Type | Properties |
+---------+--------+----------------+
| id | bigint | |
| content | text | indexed stored |
+---------+--------+----------------+
2 rows in set (0.00 sec)
处理文档 ID 时,重要的是要知道它们在内部以无符号 64 位整数存储,但根据接口的不同,处理方式也有所不同:
MySQL/SQL 接口:
- 大于 2^63-1 的 ID 将显示为负数。
- 过滤此类大 ID 时,必须使用其有符号表示。
- 使用 UINT64() 函数查看实际的无符号值。
JSON/HTTP 接口:
- ID 始终以其原始无符号值显示,无论大小。
- 过滤时可以使用有符号或无符号表示。
- 插入操作接受完整的无符号 64 位范围。
例如,让我们创建一个表并插入一些接近 2^63 的值:
mysql> create table t(id_text string);
Query OK, 0 rows affected (0.01 sec)
mysql> insert into t values(9223372036854775807, '2 ^ 63 - 1'),(9223372036854775808, '2 ^ 63');
Query OK, 2 rows affected (0.00 sec)
一些 ID 在结果中显示为负数,因为它们超过了 2^63-1。然而,使用 UINT64(id) 可以揭示它们的实际无符号值:
mysql> select *, uint64(id) from t;
+----------------------+------------+---------------------+
| id | id_text | uint64(id) |
+----------------------+------------+---------------------+
| 9223372036854775807 | 2 ^ 63 - 1 | 9223372036854775807 |
| -9223372036854775808 | 2 ^ 63 | 9223372036854775808 |
+----------------------+------------+---------------------+
2 rows in set (0.00 sec)
--- 2 out of 2 results in 0ms ---
对于查询 ID 小于 2^63 的文档,可以直接使用无符号值:
mysql> select * from t where id = 9223372036854775807;
+---------------------+------------+
| id | id_text |
+---------------------+------------+
| 9223372036854775807 | 2 ^ 63 - 1 |
+---------------------+------------+
1 row in set (0.00 sec)
--- 1 out of 1 results in 0ms ---
但是,对于从 2^63 开始的 ID,需要使用有符号值:
mysql> select * from t where id = -9223372036854775808;
+----------------------+---------+
| id | id_text |
+----------------------+---------+
| -9223372036854775808 | 2 ^ 63 |
+----------------------+---------+
1 row in set (0.00 sec)
--- 1 out of 1 results in 0ms ---
如果使用无符号值,将会出错:
mysql> select * from t where id = 9223372036854775808;
ERROR 1064 (42000): number 9223372036854775808 is out of range [-9223372036854775808..9223372036854775807]
超出 64 位范围的值将触发类似错误:
mysql> select * from t where id = -9223372036854775809;
ERROR 1064 (42000): number -9223372036854775809 is out of range [-9223372036854775808..9223372036854775807]
MySQL/SQL 和 JSON/HTTP 接口之间的行为差异在处理非常大的文档 ID 时变得更加明显。以下是一个全面的示例:
MySQL/SQL 接口:
mysql> drop table if exists t; create table t; insert into t values(17581446260360033510);
Query OK, 0 rows affected (0.01 sec)
mysql> select * from t;
+---------------------+
| id |
+---------------------+
| -865297813349518106 |
+---------------------+
mysql> select *, uint64(id) from t;
+---------------------+----------------------+
| id | uint64(id) |
+---------------------+----------------------+
| -865297813349518106 | 17581446260360033510 |
+---------------------+----------------------+
mysql> select * from t where id = -865297813349518106;
+---------------------+
| id |
+---------------------+
| -865297813349518106 |
+---------------------+
mysql> select * from t where id = 17581446260360033510;
ERROR 1064 (42000): number 17581446260360033510 is out of range [-9223372036854775808..9223372036854775807]
JSON/HTTP 接口:
# Search returns the original unsigned value
curl -s 0:9308/search -d '{"table": "t"}'
{
"took": 0,
"timed_out": false,
"hits": {
"total": 1,
"total_relation": "eq",
"hits": [
{
"_id": 17581446260360033510,
"_score": 1,
"_source": {}
}
]
}
}
# Both signed and unsigned values work for filtering
curl -s 0:9308/search -d '{"table": "t", "query": {"equals": {"id": 17581446260360033510}}}'
curl -s 0:9308/search -d '{"table": "t", "query": {"equals": {"id": -865297813349518106}}}'
# Insert with maximum unsigned 64-bit value
curl -s 0:9308/insert -d '{"table": "t", "id": 18446744073709551615, "doc": {}}'
这意味着在处理大文档 ID 时:
- MySQL 接口 要求查询时使用有符号表示,但可以通过
UINT64()显示无符号值 - JSON 接口 始终使用无符号值显示,并接受两种表示进行过滤
通用语法:
string|text [stored|attribute] [indexed]
属性:
indexed- 全文索引(可用于全文查询)stored- 存储在文档存储中(存储在磁盘上,而非 RAM 中,惰性读取)attribute- 使其成为字符串属性(可以按其排序/分组)
指定至少一个属性会覆盖所有默认属性(见下文),即,如果您决定使用自定义属性组合,需要列出所有您想要的属性。
未指定任何属性:
string 和 text 是别名,但如果您不指定任何属性,它们默认表示不同的含义:
文本(仅 text 或 text/string indexed)数据类型构成表的全文部分。文本字段被索引,可以用于搜索关键词。
文本通过分析器管道处理,将文本转换为单词,应用词形变换等。最终,从该文本构建出全文表(一种特殊的数据结构,能够快速搜索关键词)。
全文字段只能在 MATCH() 子句中使用,不能用于排序或聚合。单词存储在倒排索引中,并带有它们所属字段的引用和字段中的位置。这允许在每个字段内搜索单词,并使用邻近等高级运算符。默认情况下,字段的原始文本既被索引,也被存储在文档存储中。这意味着原始文本可以随查询结果返回,并用于 搜索结果高亮。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text);POST /cli -d "CREATE TABLE products(title text)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text']
]);utilsApi.sql('CREATE TABLE products(title text)')await utilsApi.sql('CREATE TABLE products(title text)')res = await utilsApi.sql('CREATE TABLE products(title text)');utilsApi.sql("CREATE TABLE products(title text)");utilsApi.Sql("CREATE TABLE products(title text)");utils_api.sql("CREATE TABLE products(title text)", Some(true)).await;table products
{
type = rt
path = products
# when configuring fields via config, they are indexed (and not stored) by default
rt_field = title
# this option needs to be specified for the field to be stored
stored_fields = title
}此行为可以通过显式指定文本仅被索引来覆盖。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text indexed);POST /cli -d "CREATE TABLE products(title text indexed)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text','options'=>['indexed']]
]);utilsApi.sql('CREATE TABLE products(title text indexed)')await utilsApi.sql('CREATE TABLE products(title text indexed)')res = await utilsApi.sql('CREATE TABLE products(title text indexed)');utilsApi.sql("CREATE TABLE products(title text indexed)");utilsApi.Sql("CREATE TABLE products(title text indexed)");utils_api.sql("CREATE TABLE products(title text indexed)", Some(true)).await;table products
{
type = rt
path = products
# when configuring fields via config, they are indexed (and not stored) by default
rt_field = title
}字段是有名称的,您可以将搜索限制在单个字段(例如仅搜索"title")或字段子集(例如仅"title"和"abstract")。您最多可以拥有256个全文字段。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
select * from products where match('@title first');POST /search
{
"table": "products",
"query":
{
"match": { "title": "first" }
}
}$index->setName('products')->search('@title')->get();searchApi.search({"table":"products","query":{"match":{"title":"first"}}})await searchApi.search({"table":"products","query":{"match":{"title":"first"}}})res = await searchApi.search({"table":"products","query":{"match":{"title":"first"}}});utilsApi.sql("CREATE TABLE products(title text indexed)");utilsApi.Sql("CREATE TABLE products(title text indexed)");utils_api.sql("CREATE TABLE products(title text indexed)", Some(true)).await;与全文字段不同,字符串属性(仅string或string/text attribute)按接收时的原样存储,不能用于全文搜索。相反,它们会在结果中返回,可以在WHERE子句中用于比较过滤或REGEX,并且可以用于排序和聚合。通常,不建议在字符串属性中存储大文本,而是将字符串属性用于元数据,如名称、标题、标签、键。
如果您还想索引字符串属性,可以同时指定为string attribute indexed。这将允许全文搜索并作为属性使用。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text, keys string);POST /cli -d "CREATE TABLE products(title text, keys string)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'keys'=>['type'=>'string']
]);utilsApi.sql('CREATE TABLE products(title text, keys string)')await utilsApi.sql('CREATE TABLE products(title text, keys string)')res = await utilsApi.sql('CREATE TABLE products(title text, keys string)');utilsApi.sql("CREATE TABLE products(title text, keys string)");utilsApi.Sql("CREATE TABLE products(title text, keys string)");utils_api.sql("CREATE TABLE products(title text, keys string)", Some(true)).await;table products
{
type = rt
path = products
rt_field = title
stored_fields = title
rt_attr_string = keys
}Manticore没有专门的二进制数据类型字段,但您可以通过使用base64编码和text stored或string stored字段类型(它们是同义词)来安全地存储它。如果您不对二进制数据进行编码,部分数据可能会丢失——例如,如果遇到空字节,Manticore会修剪字符串的末尾。
这是一个示例,我们使用base64编码ls命令,将其存储在Manticore中,然后解码以验证MD5校验和保持不变:
- Example
# md5sum /bin/ls
43d1b8a7ccda411118e2caba685f4329 /bin/ls
# encoded_data=`base64 -i /bin/ls `
# mysql -P9306 -h0 -e "drop table if exists test; create table test(data text stored); insert into test(data) values('$encoded_data')"
# mysql -P9306 -h0 -NB -e "select data from test" | base64 -d > /tmp/ls | md5sum
43d1b8a7ccda411118e2caba685f4329 -整数类型允许存储32位无符号整数值。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text, price int);POST /cli -d "CREATE TABLE products(title text, price int)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'price'=>['type'=>'int']
]);utilsApi.sql('CREATE TABLE products(title text, price int)')await utilsApi.sql('CREATE TABLE products(title text, price int)')res = await utilsApi.sql('CREATE TABLE products(title text, price int)');utilsApi.sql("CREATE TABLE products(title text, price int)");utilsApi.Sql("CREATE TABLE products(title text, price int)");utils_api.sql("CREATE TABLE products(title text, price int)", Some(true)).await;table products
{
type = rt
path = products
rt_field = title
stored_fields = title
rt_attr_uint = type
}整数可以通过指定位数以比32位更短的尺寸存储。例如,如果我们想存储一个我们知道不会大于8的数值,类型可以定义为bit(3)。位计数整数比全尺寸整数性能更慢,但它们需要更少的RAM。它们以32位块保存,因此为了节省空间,它们应该分组在属性定义的末尾(否则,在两个全尺寸整数之间的位计数整数也将占用32位)。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text, flags bit(3), tags bit(2) );POST /cli -d "CREATE TABLE products(title text, flags bit(3), tags bit(2))"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'flags'=>['type'=>'bit(3)'],
'tags'=>['type'=>'bit(2)']
]);utilsApi.sql('CREATE TABLE products(title text, flags bit(3), tags bit(2) ')await utilsApi.sql('CREATE TABLE products(title text, flags bit(3), tags bit(2) ')res = await utilsApi.sql('CREATE TABLE products(title text, flags bit(3), tags bit(2) ');utilsApi.sql("CREATE TABLE products(title text, flags bit(3), tags bit(2)");utilsApi.Sql("CREATE TABLE products(title text, flags bit(3), tags bit(2)");utils_api.sql("CREATE TABLE products(title text, flags bit(3), tags bit(2)", Some(true)).await;table products
{
type = rt
path = products
rt_field = title
stored_fields = title
rt_attr_uint = flags:3
rt_attr_uint = tags:2
}大整数(bigint)是64位宽的有符号整数。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text, price bigint );POST /cli -d "CREATE TABLE products(title text, price bigint)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'price'=>['type'=>'bigint']
]);utilsApi.sql('CREATE TABLE products(title text, price bigint )')await utilsApi.sql('CREATE TABLE products(title text, price bigint )')res = await utilsApi.sql('CREATE TABLE products(title text, price bigint )');utilsApi.sql("CREATE TABLE products(title text, price bigint )");utilsApi.Sql("CREATE TABLE products(title text, price bigint )");utils_api.sql("CREATE TABLE products(title text, price bigint )", Some(true)).await;table products
{
type = rt
path = products
rt_field = title
stored_fields = title
rt_attr_bigint = type
}声明一个布尔属性。等同于位数为1的整数属性。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text, sold bool );POST /cli -d "CREATE TABLE products(title text, sold bool)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'sold'=>['type'=>'bool']
]);utilsApi.sql('CREATE TABLE products(title text, sold bool )')await utilsApi.sql('CREATE TABLE products(title text, sold bool )')res = await utilsApi.sql('CREATE TABLE products(title text, sold bool )');utilsApi.sql("CREATE TABLE products(title text, sold bool )");utilsApi.Sql("CREATE TABLE products(title text, sold bool )");utils_api.sql("CREATE TABLE products(title text, sold bool )", Some(true)).await;table products
{
type = rt
path = products
rt_field = title
stored_fields = title
rt_attr_bool = sold
}时间戳类型表示Unix时间戳,以32位整数存储。与基本整数不同,时间戳类型允许使用时间和日期函数。从字符串值转换遵循以下规则:
- 不带分隔符的数字,且长度至少为10个字符,按原样转换为时间戳。
%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%Y-%m-%d%Y-%m%Y
这些转换格式的含义详见strptime手册,除%E*S表示毫秒外。
注意,纯表中不支持时间戳的自动转换。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text, date timestamp);POST /cli -d "CREATE TABLE products(title text, date timestamp)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'date'=>['type'=>'timestamp']
]);utilsApi.sql('CREATE TABLE products(title text, date timestamp)')await utilsApi.sql('CREATE TABLE products(title text, date timestamp)')res = await utilsApi.sql('CREATE TABLE products(title text, date timestamp)');utilsApi.sql("CREATE TABLE products(title text, date timestamp)");utilsApi.Sql("CREATE TABLE products(title text, date timestamp)");utils_api.sql("CREATE TABLE products(title text, date timestamp)", Some(true)).await;table products
{
type = rt
path = products
rt_field = title
stored_fields = title
rt_attr_timestamp = date
}实数存储为32位IEEE 754单精度浮点数。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text, coeff float);POST /cli -d "CREATE TABLE products(title text, coeff float)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'coeff'=>['type'=>'float']
]);utilsApi.sql('CREATE TABLE products(title text, coeff float)')await utilsApi.sql('CREATE TABLE products(title text, coeff float)')res = await utilsApi.sql('CREATE TABLE products(title text, coeff float)');utilsApi.sql("CREATE TABLE products(title text, coeff float)");utilsApi.Sql("CREATE TABLE products(title text, coeff float)");utils_api.sql("CREATE TABLE products(title text, coeff float)", Some(true)).await;table products
{
type = rt
path = products
rt_field = title
stored_fields = title
rt_attr_float = coeff
}与整数类型不同,不建议直接比较两个浮点数是否相等,因为可能存在舍入误差。更可靠的做法是使用近似比较,通过检查绝对误差范围。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
select abs(a-b)<=0.00001 from productsPOST /search
{
"table": "products",
"query": { "match_all": {} } },
"expressions": { "eps": "abs(a-b)" }
}$index->setName('products')->search('')->expression('eps','abs(a-b)')->get();searchApi.search({"table":"products","query":{"match_all":{}},"expressions":{"eps":"abs(a-b)"}})await searchApi.search({"table":"products","query":{"match_all":{}},"expressions":{"eps":"abs(a-b)"}})res = await searchApi.search({"table":"products","query":{"match_all":{}},"expressions":{"eps":"abs(a-b)"}});searchRequest = new SearchRequest();
searchRequest.setIndex("forum");
query = new HashMap<String,Object>();
query.put("match_all",null);
searchRequest.setQuery(query);
Object expressions = new HashMap<String,Object>(){{
put("ebs","abs(a-b)");
}};
searchRequest.setExpressions(expressions);
searchResponse = searchApi.search(searchRequest);object query = new { match_all=null };
var searchRequest = new SearchRequest("forum", query);
searchRequest.Expressions = new List<Object>{
new Dictionary<string, string> { {"ebs", "abs(a-b)"} }
};
var searchResponse = searchApi.Search(searchRequest);let query = SearchQuery::new();
let mut expr = HashMap::new();
expr.insert("ebs".to_string(), serde_json::json!("abs(a-b)"));
let expressions: [HashMap; 1] = [expr];
let search_request = SearchRequest {
table: "forum".to_string(),
query: Some(Box::new(query)),
expressions: serde_json::json!(expressions),
..Default::default(),
};
let search_res = search_api.search(search_req).await;另一个替代方案,也可用于执行IN(attr,val1,val2,val3),是通过选择一个乘数因子将浮点数转换为整数进行比较操作。以下例子展示了如何将IN(attr,2.0,2.5,3.5)修改为整型值。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
select in(ceil(attr*100),200,250,350) from productsPOST /search
{
"table": "products",
"query": { "match_all": {} } },
"expressions": { "inc": "in(ceil(attr*100),200,250,350)" }
}$index->setName('products')->search('')->expression('inc','in(ceil(attr*100),200,250,350)')->get();searchApi.search({"table":"products","query":{"match_all":{}}},"expressions":{"inc":"in(ceil(attr*100),200,250,350)"}})await searchApi.search({"table":"products","query":{"match_all":{}}},"expressions":{"inc":"in(ceil(attr*100),200,250,350)"}})res = await searchApi.search({"table":"products","query":{"match_all":{}}},"expressions":{"inc":"in(ceil(attr*100),200,250,350)"}});searchRequest = new SearchRequest();
searchRequest.setIndex("forum");
query = new HashMap<String,Object>();
query.put("match_all",null);
searchRequest.setQuery(query);
Object expressions = new HashMap<String,Object>(){{
put("inc","in(ceil(attr*100),200,250,350)");
}};
searchRequest.setExpressions(expressions);
searchResponse = searchApi.search(searchRequest);object query = new { match_all=null };
var searchRequest = new SearchRequest("forum", query);
searchRequest.Expressions = new List<Object> {
new Dictionary<string, string> { {"ebs", "in(ceil(attr*100),200,250,350)"} }
};
var searchResponse = searchApi.Search(searchRequest);let query = SearchQuery::new();
let mut expr = HashMap::new();
expr.insert("ebs".to_string(), serde_json::json!("in(ceil(attr*100),200,250,350)"));
let expressions: [HashMap; 1] = [expr];
let search_request = SearchRequest {
table: "forum".to_string(),
query: Some(Box::new(query)),
expressions: serde_json::json!(expressions),
..Default::default(),
};
let search_res = search_api.search(search_req).await;浮点值在Manticore中以精确的方式显示,以确保它们反映存储的确切值。这种方法是为了防止精度丢失,特别是在地理坐标等情况下,四舍五入到6位小数会导致不准确。
现在,Manticore首先输出一个带有6位数字的数字,然后解析并将其与原始值进行比较。如果不匹配,则会添加更多位数,直到匹配为止。
例如,如果浮点值插入为19.45,Manticore将显示为19.450001以准确表示存储的值。
- Example
insert into t(id, f) values(1, 19.45)
--------------
Query OK, 1 row affected (0.02 sec)
--------------
select * from t
--------------
+------+-----------+
| id | f |
+------+-----------+
| 1 | 19.450001 |
+------+-----------+
1 row in set (0.00 sec)
--- 1 out of 1 results in 0ms ---此数据类型允许存储JSON对象,特别适用于处理无模式数据。在定义JSON值时,请确保对象的开闭花括号{和}包含,或数组的开闭方括号[和]包含。虽然JSON不支持列存储,但它可以存储在传统的行存储中。值得注意的是,这两种存储类型可以在同一表中结合使用。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text, data json);POST /cli -d "CREATE TABLE products(title text, data json)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'data'=>['type'=>'json']
]);utilsApi.sql('CREATE TABLE products(title text, data json)')await utilsApi.sql('CREATE TABLE products(title text, data json)')res = await utilsApi.sql('CREATE TABLE products(title text, data json)');utilsApi.sql("CREATE TABLE products(title text, data json)");utilsApi.Sql("CREATE TABLE products(title text, data json)");utils_api.sql("CREATE TABLE products(title text, data json)", Some(true)).await;table products
{
type = rt
path = products
rt_field = title
stored_fields = title
rt_attr_json = data
}JSON属性可以在大多数操作中使用。还有一些特殊函数,如ALL()、ANY()、GREATEST()、LEAST()和INDEXOF(),允许遍历属性数组。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
select indexof(x>2 for x in data.intarray) from productsPOST /search
{
"table": "products",
"query": { "match_all": {} } },
"expressions": { "idx": "indexof(x>2 for x in data.intarray)" }
}$index->setName('products')->search('')->expression('idx','indexof(x>2 for x in data.intarray)')->get();searchApi.search({"table":"products","query":{"match_all":{}},"expressions":{"idx":"indexof(x>2 for x in data.intarray)"}})await searchApi.search({"table":"products","query":{"match_all":{}},"expressions":{"idx":"indexof(x>2 for x in data.intarray)"}})res = await searchApi.search({"table":"products","query":{"match_all":{}},"expressions":{"idx":"indexof(x>2 for x in data.intarray)"}});searchRequest = new SearchRequest();
searchRequest.setIndex("forum");
query = new HashMap<String,Object>();
query.put("match_all",null);
searchRequest.setQuery(query);
Object expressions = new HashMap<String,Object>(){{
put("idx","indexof(x>2 for x in data.intarray)");
}};
searchRequest.setExpressions(expressions);
searchResponse = searchApi.search(searchRequest);object query = new { match_all=null };
var searchRequest = new SearchRequest("forum", query);
searchRequest.Expressions = new List<Object> {
new Dictionary<string, string> { {"idx", "indexof(x>2 for x in data.intarray)"} }
};
var searchResponse = searchApi.Search(searchRequest);let query = SearchQuery::new();
let mut expr = HashMap::new();
expr.insert("idx".to_string(), serde_json::json!("indexof(x>2 for x in data.intarray)"));
let expressions: [HashMap; 1] = [expr];
let search_request = SearchRequest {
table: "forum".to_string(),
query: Some(Box::new(query)),
expressions: serde_json::json!(expressions),
..Default::default(),
};
let search_res = search_api.search(search_req).await;文本属性与字符串相同,因此无法在全文匹配表达式中使用。但是,可以使用字符串函数,如REGEX()。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
select regex(data.name, 'est') as c from products where c>0POST /search
{
"table": "products",
"query":
{
"match_all": {},
"range": { "c": { "gt": 0 } } }
},
"expressions": { "c": "regex(data.name, 'est')" }
}$index->setName('products')->search('')->expression('idx',"regex(data.name, 'est')")->filter('c','gt',0)->get();searchApi.search({"table":"products","query":{"match_all":{},"range":{"c":{"gt":0}}}},"expressions":{"c":"regex(data.name, 'est')"}})await searchApi.search({"table":"products","query":{"match_all":{},"range":{"c":{"gt":0}}}},"expressions":{"c":"regex(data.name, 'est')"}})res = await searchApi.search({"table":"products","query":{"match_all":{},"range":{"c":{"gt":0}}}},"expressions":{"c":"regex(data.name, 'est')"}}});searchRequest = new SearchRequest();
searchRequest.setIndex("forum");
query = new HashMap<String,Object>();
query.put("match_all",null);
query.put("range", new HashMap<String,Object>(){{
put("c", new HashMap<String,Object>(){{
put("gt",0);
}});
}});
searchRequest.setQuery(query);
Object expressions = new HashMap<String,Object>(){{
put("idx","indexof(x>2 for x in data.intarray)");
}};
searchRequest.setExpressions(expressions);
searchResponse = searchApi.search(searchRequest);object query = new { match_all=null };
var searchRequest = new SearchRequest("forum", query);
var rangeFilter = new RangeFilter("c");
rangeFilter.Gt = 0;
searchRequest.AttrFilter = rangeFilter;
searchRequest.Expressions = new List<Object> {
new Dictionary<string, string> { {"idx", "indexof(x>2 for x in data.intarray)"} }
};
var searchResponse = searchApi.Search(searchRequest);let query = SearchQuery::new();
let mut expr = HashMap::new();
expr.insert("idx".to_string(), serde_json::json!("indexof(x>2 for x in data.intarray)"));
let expressions: [HashMap; 1] = [expr];
let search_request = SearchRequest {
table: "forum".to_string(),
query: Some(Box::new(query)),
expressions: serde_json::json!(expressions),
..Default::default(),
};
let search_res = search_api.search(search_req).await;在处理浮点值的情况下,可能需要强制执行数据类型以确保某些情况下的正确功能。例如,当处理浮点值时,必须使用DOUBLE()以确保正确的排序。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
select * from products order by double(data.myfloat) descPOST /search
{
"table": "products",
"query": { "match_all": {} } },
"sort": [ { "double(data.myfloat)": { "order": "desc"} } ]
}$index->setName('products')->search('')->sort('double(data.myfloat)','desc')->get();searchApi.search({"table":"products","query":{"match_all":{}}},"sort":[{"double(data.myfloat)":{"order":"desc"}}]})await searchApi.search({"table":"products","query":{"match_all":{}}},"sort":[{"double(data.myfloat)":{"order":"desc"}}]})res = await searchApi.search({"table":"products","query":{"match_all":{}}},"sort":[{"double(data.myfloat)":{"order":"desc"}}]});searchRequest = new SearchRequest();
searchRequest.setIndex("forum");
query = new HashMap<String,Object>();
query.put("match_all",null);
searchRequest.setQuery(query);
searchRequest.setSort(new ArrayList<Object>(){{
add(new HashMap<String,String>(){{ put("double(data.myfloat)",new HashMap<String,String>(){{ put("order","desc");}});}});
}});
searchResponse = searchApi.search(searchRequest);object query = new { match_all=null };
var searchRequest = new SearchRequest("forum", query);
searchRequest.Sort = new List<Object> {
new SortOrder("double(data.myfloat)", SortOrder.OrderEnum.Desc)
};
var searchResponse = searchApi.Search(searchRequest);let query = SearchQuery::new();
let mut sort = HashMap::new();
sort.insert("double(data.myfloat)".to_string(), serde_json::json!("desc"));
let search_request = SearchRequest {
table: "forum".to_string(),
query: Some(Box::new(query)),
sort: serde_json::json!(sort)
..Default::default()
};
let search_res = search_api.search(search_req).await;浮点向量属性允许存储可变长度的浮点数列表,主要用于机器学习应用和相似性搜索。此类型与多值整数(MVA)(MVAs)在几个重要方面不同:
- 保留值的确切顺序(与MVAs不同,MVAs可能会重新排序)
- 保留重复值(与MVAs不同,MVAs会去重)
- 插入时无需额外处理(与MVAs不同,MVAs会排序和去重)
重要: float_vector 数据类型与 自动模式 机制不兼容。
- 目前仅支持实时表(不支持普通表)
- 其他函数或表达式中不支持
- 不能在常规过滤器或排序中使用
当你配置 float_vector 属性并设置 KNN 参数时,你将启用向量相似性搜索功能。这允许你执行 k-最近邻搜索,根据向量距离查找相似文档。
你可以做到:
- 执行 KNN(k-最近邻)向量搜索以查找相似文档
- 构建语义搜索、推荐和 AI 驱动的功能
- 使用自动嵌入从文本中自动生成向量
你不能做到:
UPDATEfloat_vector值(必须使用REPLACE代替)- 在常规过滤器或排序中使用浮点向量
- 除了通过向量搜索操作,不能通过
float_vector值进行过滤
当你创建带有 float_vector 属性的表以进行 KNN 搜索时,可以指定以下参数:
必填参数:
KNN_TYPE:目前仅支持'hnsw'KNN_DIMS:向量的维度数(手动插入向量时必需,使用MODEL_NAME时可省略)HNSW_SIMILARITY:距离函数 -'l2'、'ip'(内积)或'cosine'
可选参数:
HNSW_M:图中的最大连接数(默认:16)HNSW_EF_CONSTRUCTION:构建时间/准确性权衡(默认:200)
自动嵌入参数(当使用 MODEL_NAME 时):
MODEL_NAME:使用的嵌入模型(例如,'sentence-transformers/all-MiniLM-L6-v2'、'openai/text-embedding-ada-002')FROM:用于生成嵌入的字段名列表(逗号分隔),或空字符串''表示使用所有文本/字符串字段API_KEY:基于 API 的模型(OpenAI、Voyage、Jina)的 API 密钥
有关设置浮点向量并在搜索中使用它们的详细信息,请参阅KNN搜索。
处理浮点向量最简便的方式是使用自动嵌入。此功能使用机器学习模型自动从您的文本数据生成嵌入,从而消除手动计算和插入向量的需要。
- 简化的工作流程:只需插入文本,嵌入会自动生成
- 无需手动计算向量:无需运行单独的嵌入模型
- 一致的嵌入:相同的模型确保一致的向量表示
- 多模型支持:可选择 sentence-transformers、Qwen 嵌入模型、OpenAI、Voyage 和 Jina 模型
- 灵活的字段选择:控制用于生成嵌入的字段
在创建带有自动嵌入的表时,指定以下附加参数:
MODEL_NAME:用于自动向量生成的嵌入模型FROM:用于生成嵌入的字段(空字符串表示所有文本/字符串字段)
支持的嵌入模型:
- Sentence Transformers:任何 适合的 BERT 基础 Hugging Face 模型(例如,
sentence-transformers/all-MiniLM-L6-v2)——无需 API 密钥。Manticore 在创建表时下载模型。 - Qwen 本地嵌入:Qwen 嵌入模型,如
Qwen/Qwen3-Embedding-0.6B——不需要 API 密钥。Manticore 在您创建表时下载模型。 - OpenAI:OpenAI 嵌入模型,如
openai/text-embedding-ada-002- 需要API_KEY='<OPENAI_API_KEY>'参数 - Voyage:Voyage AI 嵌入模型 - 需要
API_KEY='<VOYAGE_API_KEY>'参数 - Jina:Jina AI 嵌入模型 - 需要
API_KEY='<JINA_API_KEY>'参数
- SQL
使用 sentence-transformers 模型(无需 API 密钥)
CREATE TABLE products (
title TEXT,
description TEXT,
embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
MODEL_NAME='sentence-transformers/all-MiniLM-L6-v2' FROM='title'
);使用 Qwen 本地嵌入(不需要 API 密钥)
CREATE TABLE products_qwen (
title TEXT,
description TEXT,
embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
MODEL_NAME='Qwen/Qwen3-Embedding-0.6B' FROM='title'
);使用 OpenAI 模型(需要 API_KEY 参数)
CREATE TABLE products_openai (
title TEXT,
content TEXT,
embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='cosine'
MODEL_NAME='openai/text-embedding-ada-002' FROM='title,content' API_KEY='<OPENAI_API_KEY>'
);使用所有文本字段进行嵌入(FROM 为空)
CREATE TABLE products_all_fields (
title TEXT,
description TEXT,
tags TEXT,
embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
MODEL_NAME='sentence-transformers/all-MiniLM-L6-v2' FROM=''
);FROM 参数控制用于生成嵌入的字段:
- 特定字段:
FROM='title'- 仅使用标题字段 - 多个字段:
FROM='title,description'- 将标题和描述字段连接并使用 - 所有文本字段:
FROM=''(空)- 表中的所有text(全文字段)和string(字符串属性)字段用于嵌入生成 - 空向量:仍然可以使用
()插入空向量以排除文档从向量搜索
使用自动嵌入时,不要在 INSERT 语句中指定向量字段。嵌入会自动从指定的文本字段生成:
-- Insert text data - embeddings generated automatically
INSERT INTO products (title, description) VALUES
('smartphone', 'latest mobile device with camera'),
('laptop computer', 'portable workstation for developers');
-- Insert with empty vector (excluded from vector search)
INSERT INTO products (title, description, embedding_vector) VALUES
('no-vector item', 'this item has no embedding', ());
或者,你可以手动插入预计算的向量数据。这需要你使用外部工具或模型自行计算向量,然后将其插入到 Manticore 中。
重要提示: 当使用 HNSW_SIMILARITY='cosine' 时,插入时向量会自动归一化为单位向量(数学长度/幅度为 1.0 的向量)。这种归一化保留了向量的方向,同时标准化了其长度,这是高效余弦相似性计算所必需的。这意味着存储的值将与你的原始输入值不同。
- SQL
CREATE TABLE products (
title TEXT,
image_vector FLOAT_VECTOR KNN_TYPE='hnsw' KNN_DIMS='4' HNSW_SIMILARITY='l2'
);
INSERT INTO products VALUES
(1, 'yellow bag', (0.653448,0.192478,0.017971,0.339821)),
(2, 'white bag', (-0.148894,0.748278,0.091892,-0.095406));你也可以创建没有 KNN 配置的 float_vector 属性。在这种模式下,向量被存储但不能用于向量搜索操作。
你可以做到:
- 存储向量数据
UPDATEfloat_vector值(与 KNN 不同,此时可以使用UPDATE)
你不能做到:
- 执行 KNN 搜索或向量相似性搜索
- 使用向量进行任何搜索操作
- 通过
float_vector值进行过滤
- 在配置KNN之前临时存储向量数据
- 用于后续与KNN一起使用的暂存数据
- 存储不需要搜索功能的向量
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text, image_vector float_vector);POST /cli -d "CREATE TABLE products(title text, image_vector float_vector)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'image_vector'=>['type'=>'float_vector']
]);utilsApi.sql('CREATE TABLE products(title text, image_vector float_vector)')await utilsApi.sql('CREATE TABLE products(title text, image_vector float_vector)')res = await utilsApi.sql('CREATE TABLE products(title text, image_vector float_vector)');utilsApi.sql("CREATE TABLE products(title text, image_vector float_vector)");utilsApi.Sql("CREATE TABLE products(title text, image_vector float_vector)");utils_api.sql("CREATE TABLE products(title text, image_vector float_vector)", Some(true)).await;table products
{
type = rt
path = products
rt_field = title
stored_fields = title
rt_attr_float_vector = image_vector
}多值属性允许存储可变长度的32位无符号整数列表。这在存储一对多的数值时非常有用,例如标签、产品类别和属性。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text, product_codes multi);或
CREATE TABLE products(title text, product_codes mva);POST /cli -d "CREATE TABLE products(title text, product_codes multi)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'product_codes'=>['type'=>'multi']
]);utilsApi.sql('CREATE TABLE products(title text, product_codes multi)')await utilsApi.sql('CREATE TABLE products(title text, product_codes multi)')res = await utilsApi.sql('CREATE TABLE products(title text, product_codes multi)');utilsApi.sql("CREATE TABLE products(title text, product_codes multi)");utilsApi.Sql("CREATE TABLE products(title text, product_codes multi)");utils_api.sql("CREATE TABLE products(title text, product_codes multi)", Some(true)).await;table products
{
type = rt
path = products
rt_field = title
stored_fields = title
rt_attr_multi = product_codes
}- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
select * from products where any(product_codes)=3POST /search
{
"table": "products",
"query":
{
"match_all": {},
"equals" : { "any(product_codes)": 3 }
}
}$index->setName('products')->search('')->filter('any(product_codes)','equals',3)->get();searchApi.search({"table":"products","query":{"match_all":{},"equals":{"any(product_codes)":3}}}})await searchApi.search({"table":"products","query":{"match_all":{},"equals":{"any(product_codes)":3}}}})res = await searchApi.search({"table":"products","query":{"match_all":{},"equals":{"any(product_codes)":3}}}})'searchRequest = new SearchRequest();
searchRequest.setIndex("forum");
query = new HashMap<String,Object>();
query.put("match_all",null);
query.put("equals",new HashMap<String,Integer>(){{
put("any(product_codes)",3);
}});
searchRequest.setQuery(query);
searchResponse = searchApi.search(searchRequest);object query = new { match_all=null };
var searchRequest = new SearchRequest("forum", query);
searchRequest.AttrFilter = new EqualsFilter("any(product_codes)", 3);
var searchResponse = searchApi.Search(searchRequest);let query = SearchQuery::new();
let search_request = SearchRequest {
table: "forum".to_string(),
query: Some(Box::new(query)),
..Default::default(),
};
let search_res = search_api.search(search_req).await;- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
select least(product_codes) l from products order by l ascPOST /search
{
"table": "products",
"query":
{
"match_all": {},
"sort": [ { "product_codes":{ "order":"asc", "mode":"min" } } ]
}
}$index->setName('products')->search('')->sort('product_codes','asc','min')->get();searchApi.search({"table":"products","query":{"match_all":{},"sort":[{"product_codes":{"order":"asc","mode":"min"}}]}})await searchApi.search({"table":"products","query":{"match_all":{},"sort":[{"product_codes":{"order":"asc","mode":"min"}}]}})res = await searchApi.search({"table":"products","query":{"match_all":{},"sort":[{"product_codes":{"order":"asc","mode":"min"}}]}});searchRequest = new SearchRequest();
searchRequest.setIndex("forum");
query = new HashMap<String,Object>();
query.put("match_all",null);
searchRequest.setQuery(query);
searchRequest.setSort(new ArrayList<Object>(){{
add(new HashMap<String,String>(){{ put("product_codes",new HashMap<String,String>(){{ put("order","asc");put("mode","min");}});}});
}});
searchResponse = searchApi.search(searchRequest);object query = new { match_all=null };
var searchRequest = new SearchRequest("forum", query);
searchRequest.Sort = new List<Object> {
new SortMVA("product_codes", SortOrder.OrderEnum.Asc, SortMVA.ModeEnum.Min)
};
searchResponse = searchApi.Search(searchRequest);let query = SearchQuery::new();
let mut sort_opts = HashMap::new();
sort_opts.insert("order".to_string(), serde_json::json!("asc"));
sort_opts.insert("mode".to_string(), serde_json::json!("min"));
sort_expr.insert("product_codes".to_string(), serde_json::json!(sort_opts));
let sort: [HashMap; 1] = [sort_expr];
let search_req = SearchRequest {
table: "forum".to_string(),
query: Some(Box::new(query)),
sort: serde_json::json!(sort),
..Default::default(),
};
let search_res = search_api.search(search_req).await;当按多值属性分组时,一个文档将贡献给与该文档关联的不同值数量相同的多个组。例如,如果一个集合恰好包含一个文档,其'product_codes'多值属性的值为5、7和11,那么按'product_codes'分组将产生3个组,COUNT(*)等于1,GROUPBY()键值分别为5、7和11。另外请注意,按多值属性分组可能导致结果集中出现重复文档,因为每个文档可以参与多个组。
- SQL
insert into products values ( 1, 'doc one', (5,7,11) );
select id, count(*), groupby() from products group by product_codes;Query OK, 1 row affected (0.00 sec)
+------+----------+-----------+
| id | count(*) | groupby() |
+------+----------+-----------+
| 1 | 1 | 11 |
| 1 | 1 | 7 |
| 1 | 1 | 5 |
+------+----------+-----------+
3 rows in set (0.00 sec)作为多值属性值插入的数字顺序不会被保留。值在内部以排序集合的形式存储。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
insert into product values (1,'first',(4,2,1,3));
select * from products;POST /insert
{
"table":"products",
"id":1,
"doc":
{
"title":"first",
"product_codes":[4,2,1,3]
}
}
POST /search
{
"table": "products",
"query": { "match_all": {} }
}$index->addDocument([
"title"=>"first",
"product_codes"=>[4,2,1,3]
]);
$index->search('')-get();indexApi.insert({"table":"products","id":1,"doc":{"title":"first","product_codes":[4,2,1,3]}})
searchApi.search({"table":"products","query":{"match_all":{}}})await indexApi.insert({"table":"products","id":1,"doc":{"title":"first","product_codes":[4,2,1,3]}})
await searchApi.search({"table":"products","query":{"match_all":{}}})await indexApi.insert({"table":"products","id":1,"doc":{"title":"first","product_codes":[4,2,1,3]}});
res = await searchApi.search({"table":"products","query":{"match_all":{}}});InsertDocumentRequest newdoc = new InsertDocumentRequest();
HashMap<String,Object> doc = new HashMap<String,Object>(){{
put("title","first");
put("product_codes",new int[] {4,2,1,3});
}};
newdoc.index("products").id(1L).setDoc(doc);
sqlresult = indexApi.insert(newdoc);
Map<String,Object> query = new HashMap<String,Object>();
query.put("match_all",null);
SearchRequest searchRequest = new SearchRequest();
searchRequest.setIndex("products");
searchRequest.setQuery(query);
SearchResponse searchResponse = searchApi.search(searchRequest);
System.out.println(searchResponse.toString() );Dictionary<string, Object> doc = new Dictionary<string, Object>();
doc.Add("title", "first");
doc.Add("product_codes", new List<Object> {4,2,1,3});
InsertDocumentRequest newdoc = new InsertDocumentRequest(index: "products", id: 1, doc: doc);
var sqlresult = indexApi.Insert(newdoc);
object query = new { match_all=null };
var searchRequest = new SearchRequest("products", query);
var searchResponse = searchApi.Search(searchRequest);
Console.WriteLine(searchResponse.ToString())let mut doc = HashMap::new();
doc.insert("title".to_string(), serde_json::json!("first"));
doc.insert("product_codes".to_string(), serde_json::json!([4,2,1,3]));
let insert_req = InsertDocumentRequest::new("products".to_string(), serde_json::json!(doc));
let insert_res = index_api.insert(insert_req).await;
let query = SearchQuery::new();
let search_req = SearchRequest {
table: "forum".to_string(),
query: Some(Box::new(query)),
..Default::default(),
};
let search_res = search_api.search(search_req).await;
println!("{:?}", search_res);Query OK, 1 row affected (0.00 sec)
+------+---------------+-------+
| id | product_codes | title |
+------+---------------+-------+
| 1 | 1,2,3,4 | first |
+------+---------------+-------+
1 row in set (0.01 sec){
"table":"products",
"_id":1,
"created":true,
"result":"created",
"status":201
}
{
"took":0,
"timed_out":false,
"hits":{
"total":1,
"hits":[
{
"_id": 1,
"_score":1,
"_source":{
"product_codes":[
1,
2,
3,
4
],
"title":"first"
}
}
]
}
}Array
(
[_index] => products
[_id] => 1
[created] => 1
[result] => created
[status] => 201
)
Array
(
[took] => 0
[timed_out] =>
[hits] => Array
(
[total] => 1
[hits] => Array
(
[0] => Array
(
[_id] => 1
[_score] => 1
[_source] => Array
(
[product_codes] => Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
)
[title] => first
)
)
)
)
){'created': True,
'found': None,
'id': 1,
'table': 'products',
'result': 'created'}
{'hits': {'hits': [{u'_id': u'1',
u'_score': 1,
u'_source': {u'product_codes': [1, 2, 3, 4],
u'title': u'first'}}],
'total': 1},
'profile': None,
'timed_out': False,
'took': 29}{'created': True,
'found': None,
'id': 1,
'table': 'products',
'result': 'created'}
{'hits': {'hits': [{u'_id': u'1',
u'_score': 1,
u'_source': {u'product_codes': [1, 2, 3, 4],
u'title': u'first'}}],
'total': 1},
'profile': None,
'timed_out': False,
'took': 29}{"took":0,"timed_out":false,"hits":{"total":1,"hits":[{"_id": 1,"_score":1,"_source":{"product_codes":[1,2,3,4],"title":"first"}}]}}class SearchResponse {
took: 0
timedOut: false
hits: class SearchResponseHits {
total: 1
hits: [{_id=1, _score=1, _source={product_codes=[1, 2, 3, 4], title=first}}]
aggregations: null
}
profile: null
}class SearchResponse {
took: 0
timedOut: false
hits: class SearchResponseHits {
total: 1
hits: [{_id=1, _score=1, _source={product_codes=[1, 2, 3, 4], title=first}}]
aggregations: null
}
profile: null
}class SearchResponse {
took: 0
timedOut: false
hits: class SearchResponseHits {
total: 1
hits: [{_id=1, _score=1, _source={product_codes=[1, 2, 3, 4], title=first}}]
aggregations: null
}
profile: null
}一种允许存储可变长度64位有符号整数列表的数据类型。其功能与多值整数相同。
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- java
- C#
- Rust
- config
CREATE TABLE products(title text, values multi64);或
CREATE TABLE products(title text, values mva64);POST /cli -d "CREATE TABLE products(title text, values multi64)"$index = new \Manticoresearch\Index($client);
$index->setName('products');
$index->create([
'title'=>['type'=>'text'],
'values'=>['type'=>'multi64']
]);utilsApi.sql('CREATE TABLE products(title text, values multi64))')await utilsApi.sql('CREATE TABLE products(title text, values multi64))')res = await utilsApi.sql('CREATE TABLE products(title text, values multi64))');utilsApi.sql("CREATE TABLE products(title text, values multi64))");utilsApi.Sql("CREATE TABLE products(title text, values multi64))");utils_api.sql("CREATE TABLE products(title text, values multi64))", Some(true)).await;table products
{
type = rt
path = products
rt_field = title
stored_fields = title
rt_attr_multi_64 = values
}当使用列式存储时,可以为属性指定以下特性。
默认情况下,Manticore 列式存储以列式方式存储所有属性,同时也在特殊的文档存储中按行存储。这使得像 SELECT * FROM ... 这样的查询能够快速执行,尤其是在一次获取大量记录时。但是,如果您确定不需要此功能或希望节省磁盘空间,可以在创建表时通过指定 fast_fetch='0' 来禁用它,或者(如果您在配置中定义表)使用 columnar_no_fast_fetch,如下例所示。
- RT mode
- Plain mode
create table t(a int, b int fast_fetch='0') engine='columnar'; desc t;source min {
type = mysql
sql_host = localhost
sql_user = test
sql_pass =
sql_db = test
sql_query = select 1, 1 a, 1 b
sql_attr_uint = a
sql_attr_uint = b
}
table tbl {
path = tbl/col
source = min
columnar_attrs = *
columnar_no_fast_fetch = b
}+-------+--------+---------------------+
| Field | Type | Properties |
+-------+--------+---------------------+
| id | bigint | columnar fast_fetch |
| a | uint | columnar fast_fetch |
| b | uint | columnar |
+-------+--------+---------------------+
3 rows in set (0.00 sec)+-------+--------+---------------------+
| Field | Type | Properties |
+-------+--------+---------------------+
| id | bigint | columnar fast_fetch |
| a | uint | columnar fast_fetch |
| b | uint | columnar |
+-------+--------+---------------------+在 Manticore Search 中,有以下两种方式来管理表:
实时模式不需要在配置文件中定义表。但是,searchd 部分中的 data_dir 指令是必需的。表文件存储在 data_dir 内。
仅在此模式下可用复制功能。
您可以使用 SQL 命令如 CREATE TABLE、ALTER TABLE 和 DROP TABLE 来创建和修改表结构,并删除它。此模式特别适用于 实时 和 过滤查询 表。
创建表时,表名会被转换为小写。
在这种模式下,您可以在配置文件中指定表结构。Manticore 在启动时读取此结构并在表不存在时创建它。此模式特别适用于 纯文本 表,这些表使用外部存储的数据。
要删除一个表,请从配置文件中移除它或移除路径设置并发送 SIGHUP 信号给服务器或重启它。
此模式下表名是区分大小写的。
此模式支持所有类型的表。
| 表类型 | 实时模式 | 纯文本模式 |
|---|---|---|
| 实时 | 支持 | 支持 |
| 纯文本 | 不支持 | 支持 |
| 过滤查询 | 支持 | 支持 |
| 分布式 | 支持 | 支持 |
| 模板 | 不支持 | 支持 |
一个 实时表 是 Manticore 中的主要表类型。它允许您添加、更新和删除文档,并且您可以立即看到这些更改。您可以在配置文件中设置实时表,或者使用 CREATE、UPDATE、DELETE 或 ALTER 命令。
内部,一个实时表由一个或多个称为 碎片 的 普通表 组成。有两类碎片:
- 多个 磁盘碎片 - 这些保存在磁盘上,并且结构类似于 普通表。
- 单个 内存碎片 - 这些保存在内存中并收集所有更改。
内存碎片的大小由 rt_mem_limit 设置控制。一旦达到此限制,内存碎片将作为磁盘碎片转移到磁盘上。如果磁盘碎片过多,Manticore 将 组合一些碎片 以提高性能。
您可以使用两种方式创建新的实时表:通过 CREATE TABLE 命令或通过 HTTP JSON API 的 _mapping 端点。
您可以使用此命令通过 SQL 和 HTTP 协议进行:
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- Javascript
- Java
- C#
- Rust
- Typescript
- Go
- CONFIG
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'],
]);utilsApi.sql('CREATE TABLE forum(title text, price float)')await utilsApi.sql('CREATE TABLE forum(title text, price float)')res = await utilsApi.sql('CREATE TABLE forum(title text, price float)');utilsApi.sql("CREATE TABLE forum(title text, price float)");utilsApi.Sql("CREATE TABLE forum(title text, price float)");utils_api.sql("CREATE TABLE forum(title text, price float)", Some(true)).await;res = await utilsApi.sql('CREATE TABLE forum(title text, price float)');utilsAPI.Sql(context.Background()).Body("CREATE TABLE forum(title text, price float)").Execute()table products {
type = rt
path = tbl
rt_field = title
rt_attr_uint = price
stored_fields = title
}Query OK, 0 rows affected (0.00 sec){
"total":0,
"error":"",
"warning":""
}注意:_mapping API 需要 Manticore Buddy。如果不起作用,请确保已安装 Buddy。
另外,您还可以通过 _mapping 端点创建新表。此端点允许您定义类似 Elasticsearch 的表结构并将其转换为 Manticore 表。
请求体必须具有以下结构:
"properties"
{
"FIELD_NAME_1":
{
"type": "FIELD_TYPE_1"
},
"FIELD_NAME_2":
{
"type": "FIELD_TYPE_2"
},
...
"FIELD_NAME_N":
{
"type": "FIELD_TYPE_M"
}
}
在创建表时,Elasticsearch 数据类型将根据以下规则映射到 Manticore 类型:
- aggregate_metric => json
- binary => string
- boolean => bool
- byte => int
- completion => string
- date => timestamp
- date_nanos => bigint
- date_range => json
- dense_vector => json
- flattened => json
- flat_object => json
- float => float
- float_range => json
- geo_point => json
- geo_shape => json
- half_float => float
- histogram => json
- integer => int
- integer_range => json
- ip => string
- ip_range => json
- keyword => string
- knn_vector => float_vector
- long => bigint
- long_range => json
- match_only_text => text
- object => json
- point => json
- scaled_float => float
- search_as_you_type => text
- shape => json
- short => int
- text => text
- unsigned_long => int
- version => string
- JSON
POST /your_table_name/_mapping -d '
{
"properties": {
"price": {
"type": "float"
},
"title": {
"type": "text"
}
}
}
'{
"total":0,
"error":"",
"warning":""
}您可以创建实时表的一个副本,可以包含或不包含其数据。请注意,如果表很大,复制带数据的表可能需要一些时间。复制工作在同步模式下进行,但如果连接中断,它将在后台继续。
CREATE TABLE [IF NOT EXISTS] table_name LIKE old_table_name [WITH DATA]
注意:复制表需要 Manticore Buddy。如果不起作用,请确保已安装 Buddy。
- SQL
- Example (WITH DATA)
create table products LIKE old_products;create table products LIKE old_products WITH DATA;- 添加文档。
- 使用 更新 过程更新属性和全文字段。
- 删除文档。
- 清空表。
- 使用
ALTER命令在线更改架构,如 在线更改架构 所述。 - 在配置文件中定义表,如 定义表 所述。
- 使用 UUID 功能自动提供 ID。
- 不能使用 indexer 功能进行数据摄取。
- 不能连接到 sources 以便轻松从外部存储索引数据。
- 不能更新 killlist_target,因为它由实时表自动管理。
下表列出了实时表中不同文件扩展名及其对应说明:
| Extension | Description |
|---|---|
.lock |
一个锁文件,确保一次只有一个进程可以访问该表。 |
.ram |
表的RAM部分,存储在内存中,用作变更的累积器。 |
.meta |
定义实时表结构和设置的表头文件。 |
.*.sp* |
存储在磁盘上的磁盘块,格式与普通表相同。当RAM块大小超过 rt_mem_limit 时创建。 |
详情请参阅有关磁盘块结构的普通表文件结构。