WHERE 是一个 SQL 子句,适用于全文匹配和额外的过滤。以下操作符可用:
MATCH('query') 受支持,并映射到一个 全文查询。
{col_name | expr_alias} [NOT] IN @uservar 条件语法受支持。请参阅 SET 语法以了解全局用户变量的描述。
如果您更喜欢 HTTP JSON 接口,也可以应用过滤。它可能看起来比 SQL 更复杂,但在需要程序化准备查询的情况下(例如,当用户在您的应用程序中填写表单时),它被推荐使用。
以下是一个 bool 查询中几个过滤器的示例。
此全文查询匹配所有包含 product 的字段的文档。这些文档必须具有价格大于或等于 500(gte)且小于或等于 1000(lte)。所有这些文档都必须没有小于 15 的修订版本(lt)。
- JSON
POST /search
{
"table": "test1",
"query": {
"bool": {
"must": [
{ "match" : { "_all" : "product" } },
{ "range": { "price": { "gte": 500, "lte": 1000 } } }
],
"must_not": {
"range": { "revision": { "lt": 15 } }
}
}
}
}bool 查询根据其他查询和/或过滤器的布尔组合匹配文档。查询和过滤器必须在 must、should 或 must_not 部分指定,并且可以 嵌套。
- JSON
POST /search
{
"table":"test1",
"query": {
"bool": {
"must": [
{ "match": {"_all":"keyword"} },
{ "range": { "revision": { "gte": 14 } } }
]
}
}
}must 部分中指定的查询和过滤器必须匹配文档。如果指定了多个全文查询或过滤器,则所有这些查询都必须匹配。这相当于 SQL 中的 AND 查询。请注意,如果您要匹配数组(多值属性),可以多次指定该属性。结果仅在数组中找到所有查询值时为正,例如:
"must": [
{"equals" : { "product_codes": 5 }},
{"equals" : { "product_codes": 6 }}
]
另外,从性能角度来看,使用:
{"in" : { "all(product_codes)": [5,6] }}
(请参阅下方的详细信息)可能更好。
should 部分中指定的查询和过滤器应该匹配文档。如果在 must 或 must_not 中指定了某些查询,则 should 查询将被忽略。另一方面,如果除了 should 之外没有其他查询,则至少有一个这些查询必须匹配一个文档,该文档才能匹配 bool 查询。这相当于 OR 查询。请注意,如果您要匹配数组(多值属性),可以多次指定该属性,例如:
"should": [
{"equals" : { "product_codes": 7 }},
{"equals" : { "product_codes": 8 }}
]
另外,从性能角度来看,使用:
{"in" : { "any(product_codes)": [7,8] }}
(请参阅下方的详细信息)可能更好。
must_not 部分中指定的查询和过滤器必须不匹配文档。如果在 must_not 下指定了多个查询,则文档匹配如果它们中的任何一个都不匹配。
- JSON
POST /search
{
"table":"t",
"query": {
"bool": {
"should": [
{
"equals": {
"b": 1
}
},
{
"equals": {
"b": 3
}
}
],
"must": [
{
"equals": {
"a": 1
}
}
],
"must_not": {
"equals": {
"b": 2
}
}
}
}
}bool 查询可以嵌套在另一个 bool 中,因此您可以创建更复杂的查询。要创建嵌套布尔查询,请使用另一个 bool 而不是 must、should 或 must_not。以下是如何表示此查询:
a = 2 and (a = 10 or b = 0)
以 JSON 格式呈现。
- JSON
a = 2 and (a = 10 or b = 0)
POST /search
{
"table":"t",
"query": {
"bool": {
"must": [
{
"equals": {
"a": 2
}
},
{
"bool": {
"should": [
{
"equals": {
"a": 10
}
},
{
"equals": {
"b": 0
}
}
]
}
}
]
}
}
}更复杂的查询:
(a = 1 and b = 1) or (a = 10 and b = 2) or (b = 0)
- JSON
(a = 1 and b = 1) or (a = 10 and b = 2) or (b = 0)
POST /search
{
"table":"t",
"query": {
"bool": {
"should": [
{
"bool": {
"must": [
{
"equals": {
"a": 1
}
},
{
"equals": {
"b": 1
}
}
]
}
},
{
"bool": {
"must": [
{
"equals": {
"a": 10
}
},
{
"equals": {
"b": 2
}
}
]
}
},
{
"bool": {
"must": [
{
"equals": {
"b": 0
}
}
]
}
}
]
}
}
}SQL 格式的查询(query_string)也可以在 bool 查询中使用。
- JSON
POST /search
{
"table": "test1",
"query": {
"bool": {
"must": [
{ "query_string" : "product" },
{ "query_string" : "good" }
]
}
}
}等值过滤器是最简单的过滤器,适用于整数、浮点数和字符串属性。
- JSON
POST /search
{
"table":"test1",
"query": {
"equals": { "price": 500 }
}
}equals 过滤器可以应用于 多值属性,您可以使用:
any(),如果属性至少有一个值等于查询值,则为正;all(),如果属性只有一个值且等于查询值,则为正
- JSON
POST /search
{
"table":"test1",
"query": {
"equals": { "any(price)": 100 }
}
}集合过滤器检查属性值是否等于指定集合中的任何值。
集合过滤器支持整数、字符串和多值属性。
- JSON
POST /search
{
"table":"test1",
"query": {
"in": {
"price": [1,10,100]
}
}
}当应用于 多值属性 时,您可以使用:
any()(等同于无函数),如果属性值与查询值之间至少有一个匹配,则为正;all(),如果所有属性值都在查询集中,则为正
- JSON
POST /search
{
"table":"test1",
"query": {
"in": {
"all(price)": [1,10]
}
}
}范围过滤器匹配具有属性值在指定范围内的文档。
范围过滤器支持以下属性:
gte:大于或等于gt:大于lte:小于或等于lt:小于
- JSON
POST /search
{
"table":"test1",
"query": {
"range": {
"price": {
"gte": 500,
"lte": 1000
}
}
}
}geo_distance 过滤器用于过滤距离特定地理位置特定距离内的文档。
指定针的位置,以度为单位。距离从这一点计算。
指定包含纬度和经度的属性。
指定距离计算函数。可以是 adaptive 或 haversine。adaptive 更快且更精确,有关详细信息,请参阅 GEODIST()。可选,默认为 adaptive。
指定从针位置的最大距离。在此距离内的所有文档都匹配。距离可以使用多种单位指定。如果没有指定单位,则距离默认为米。以下是支持的距离单位列表:
- 米:
m或meters - 公里:
km或kilometers - 厘米:
cm或centimeters - 毫米:
mm或millimeters - 英里:
mi或miles - 码:
yd或yards - 英尺:
ft或feet - 英寸:
in或inch - 海里:
NM,nmi或nauticalmiles
location_anchor 和 location_source 属性接受以下纬度/经度格式:
- 具有 lat 和 lon 键的对象:
{ "lat": "attr_lat", "lon": "attr_lon" } - 具有以下结构的字符串:
"attr_lat, attr_lon" - 按以下顺序排列的数组:
[attr_lon, attr_lat]
纬度和经度以度为单位指定。
- Basic example
- Advanced example
POST /search
{
"table":"test",
"query": {
"geo_distance": {
"location_anchor": {"lat":49, "lon":15},
"location_source": {"attr_lat, attr_lon"},
"distance_type": "adaptive",
"distance":"100 km"
}
}
}geo_distance 可以作为 bool 查询中的过滤器与其他匹配项或其它属性过滤器一起使用。
POST /search
{
"table": "geodemo",
"query": {
"bool": {
"must": [
{
"match": {
"*": "station"
}
},
{
"equals": {
"state_code": "ENG"
}
},
{
"geo_distance": {
"distance_type": "adaptive",
"location_anchor": {
"lat": 52.396,
"lon": -1.774
},
"location_source": "latitude_deg,longitude_deg",
"distance": "10000 m"
}
}
]
}
}
}表连接在Manticore Search中允许您通过匹配相关列将两个表中的文档合并。此功能允许执行更复杂的查询并增强跨多个表的数据检索。
SELECT
select_expr [, select_expr] ...
FROM tbl_name
{INNER | LEFT} JOIN tbl2_name
ON join_condition
[...other select options]
join_condition: {
left_table.attr = right_table.attr
| left_table.json_attr.string_id = string(right_table.json_attr.string_id)
| left_table.json_attr.int_id = int(right_table.json_attr.int_id)
| [..filters on right table attributes]
}
有关选择选项的更多信息,请参阅SELECT部分。
当通过JSON属性的值进行连接时,您需要显式指定该值的类型,使用int()或string()函数。
SELECT ... ON left_table.json_attr.string_id = string(right_table.json_attr.string_id)
SELECT ... ON left_table.json_attr.int_id = int(right_table.json_attr.int_id)
POST /search
{
"table": "table_name",
"query": {
<optional full-text query against the left table>
},
"join": [
{
"type": "inner" | "left",
"table": "joined_table_name",
"query": {
<optional full-text query against the right table>
},
"on": [
{
"left": {
"table": "left_table_name",
"field": "field_name",
"type": "<common field's type when joining using json attributes>"
},
"operator": "eq",
"right": {
"table": "right_table_name",
"field": "field_name"
}
}
]
}
],
"options": {
...
}
}
on.type: {
int
| string
}
注意,在left操作数部分有type字段,您应该在使用json属性连接两个表时使用它。允许的值为string和int。
Manticore Search 支持两种类型的连接:
- INNER JOIN:仅返回在两个表中都有匹配的行。例如,查询在
orders和customers表之间执行INNER JOIN,仅包括匹配的订单。
- SQL
- JSON
SELECT product, customers.email, customers.name, customers.address
FROM orders
INNER JOIN customers
ON customers.id = orders.customer_id
WHERE MATCH('maple', customers)
ORDER BY customers.email ASC;POST /search
{
"table": "orders",
"join": [
{
"type": "inner",
"table": "customers",
"query": {
"query_string": "maple"
},
"on": [
{
"left": {
"table": "orders",
"field": "customer_id"
},
"operator": "eq",
"right": {
"table": "customers",
"field": "id"
}
}
]
}
],
"_source": ["product", "customers.email", "customers.name", "customers.address"],
"sort": [{"customers.email": "asc"}]
}+---------+-------------------+----------------+-------------------+
| product | customers.email | customers.name | customers.address |
+---------+-------------------+----------------+-------------------+
| Laptop | alice@example.com | Alice Johnson | 123 Maple St |
| Tablet | alice@example.com | Alice Johnson | 123 Maple St |
+---------+-------------------+----------------+-------------------+
2 rows in set (0.00 sec){
"took": 0,
"timed_out": false,
"hits": {
"total": 2,
"total_relation": "eq",
"hits": [
{
"_id": 1,
"_score": 1,
"_source": {
"product": "Laptop",
"customers.email": "alice@example.com",
"customers.name": "Alice Johnson",
"customers.address": "123 Maple St"
}
},
{
"_id": 3,
"_score": 1,
"_source": {
"product": "Tablet",
"customers.email": "alice@example.com",
"customers.name": "Alice Johnson",
"customers.address": "123 Maple St"
}
}
]
}
}- LEFT JOIN:返回左表的所有行以及右表的匹配行。如果没有匹配,则右表的列返回NULL值。例如,此查询使用LEFT JOIN检索所有客户及其相应的订单。如果没有相应的订单,则会出现NULL值。结果按客户的电子邮件排序,并仅选择客户的姓名和订单数量。
- SQL
- JSON
SELECT
name, orders.quantity
FROM customers
LEFT JOIN orders
ON orders.customer_id = customers.id
ORDER BY email ASC;POST /search
{
"table": "customers",
"_source": ["name", "orders.quantity"],
"join": [
{
"type": "left",
"table": "orders",
"on": [
{
"left": {
"table": "orders",
"field": "customer_id"
},
"operator": "eq",
"right": {
"table": "customers",
"field": "id"
}
}
]
}
],
"sort": [{"email": "asc"}]
}+---------------+-----------------+-------------------+
| name | orders.quantity | @int_attr_email |
+---------------+-----------------+-------------------+
| Alice Johnson | 1 | alice@example.com |
| Alice Johnson | 1 | alice@example.com |
| Bob Smith | 2 | bob@example.com |
| Carol White | 1 | carol@example.com |
| John Smith | NULL | john@example.com |
+---------------+-----------------+-------------------+
5 rows in set (0.00 sec){
"took": 0,
"timed_out": false,
"hits": {
"total": 5,
"total_relation": "eq",
"hits": [
{
"_id": 1,
"_score": 1,
"_source": {
"name": "Alice Johnson",
"address": "123 Maple St",
"email": "alice@example.com",
"orders.id": 3,
"orders.customer_id": 1,
"orders.quantity": 1,
"orders.order_date": "2023-01-03",
"orders.tags": [
101,
104
],
"orders.details": {
"price": 450,
"warranty": "1 year"
},
"orders.product": "Tablet"
}
},
{
"_id": 1,
"_score": 1,
"_source": {
"name": "Alice Johnson",
"address": "123 Maple St",
"email": "alice@example.com",
"orders.id": 1,
"orders.customer_id": 1,
"orders.quantity": 1,
"orders.order_date": "2023-01-01",
"orders.tags": [
101,
102
],
"orders.details": {
"price": 1200,
"warranty": "2 years"
},
"orders.product": "Laptop"
}
},
{
"_id": 2,
"_score": 1,
"_source": {
"name": "Bob Smith",
"address": "456 Oak St",
"email": "bob@example.com",
"orders.id": 2,
"orders.customer_id": 2,
"orders.quantity": 2,
"orders.order_date": "2023-01-02",
"orders.tags": [
103
],
"orders.details": {
"price": 800,
"warranty": "1 year"
},
"orders.product": "Phone"
}
},
{
"_id": 3,
"_score": 1,
"_source": {
"name": "Carol White",
"address": "789 Pine St",
"email": "carol@example.com",
"orders.id": 4,
"orders.customer_id": 3,
"orders.quantity": 1,
"orders.order_date": "2023-01-04",
"orders.tags": [
105
],
"orders.details": {
"price": 300,
"warranty": "1 year"
},
"orders.product": "Monitor"
}
},
{
"_id": 4,
"_score": 1,
"_source": {
"name": "John Smith",
"address": "15 Barclays St",
"email": "john@example.com",
"orders.id": 0,
"orders.customer_id": 0,
"orders.quantity": 0,
"orders.order_date": "",
"orders.tags": [],
"orders.details": null,
"orders.product": ""
}
}
]
}
}Manticore Search 中表连接的一个强大功能是能够在连接的两个表中同时执行全文搜索。这允许您创建基于多个表中的文本内容的复杂查询。
您可以为 JOIN 查询中的每个表单独使用 MATCH() 函数。查询根据两个表中的文本内容过滤结果。
- SQL
- JSON
SELECT t1.f, t2.f
FROM t1
LEFT JOIN t2 ON t1.id = t2.id
WHERE MATCH('hello', t1) AND MATCH('goodbye', t2);POST /search
{
"table": "t1",
"query": {
"query_string": "hello"
},
"join": [
{
"type": "left",
"table": "t2",
"query": {
"query_string": "goodbye"
},
"on": [
{
"left": {
"table": "t1",
"field": "id"
},
"operator": "eq",
"right": {
"table": "t2",
"field": "id"
}
}
]
}
],
"_source": ["f", "t2.f"]
}+-------------+---------------+
| f | t2.f |
+-------------+---------------+
| hello world | goodbye world |
+-------------+---------------+
1 row in set (0.00 sec){
"took": 1,
"timed_out": false,
"hits": {
"total": 1,
"total_relation": "eq",
"hits": [
{
"_id": 2,
"_score": 1680,
"t2._score": 1680,
"_source": {
"f": "hello world",
"t2.f": "goodbye world"
}
}
]
}
}在JSON API查询中,表特定的全文匹配与SQL不同:
主表查询:根级别上的 "query" 字段应用于主表(在 "table" 中指定)。
连接表查询:每个连接定义可以包括其自己的 "query" 字段,专门应用于该连接表。
- JSON
POST /search
{
"table": "t1",
"query": {
"query_string": "hello"
},
"join": [
{
"type": "left",
"table": "t2",
"query": {
"match": {
"*": "goodbye"
}
},
"on": [
{
"left": {
"table": "t1",
"field": "id"
},
"operator": "eq",
"right": {
"table": "t2",
"field": "id"
}
}
]
}
]
}{
"took": 1,
"timed_out": false,
"hits": {
"total": 1,
"total_relation": "eq",
"hits": [
{
"_id": 1,
"_score": 1680,
"t2._score": 1680,
"_source": {
"f": "hello world",
"t2.id": 1,
"t2.f": "goodbye world"
}
}
]
}
}1. 仅在主表上查询:返回主表中所有匹配的行。对于未匹配的连接记录(LEFT JOIN),SQL返回NULL值,而JSON API返回默认值(数字为0,文本为空字符串)。
- SQL
- JSON
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
WHERE MATCH('database', t1);POST /search
{
"table": "t1",
"query": {
"query_string": "database"
},
"join": [
{
"type": "left",
"table": "t2",
"on": [
{
"left": {
"table": "t1",
"field": "id"
},
"operator": "eq",
"right": {
"table": "t2",
"field": "id"
}
}
]
}
]
}+------+-----------------+-------+------+
| id | f | t2.id | t2.f |
+------+-----------------+-------+------+
| 3 | database search | NULL | NULL |
+------+-----------------+-------+------+
1 row in set (0.00 sec){
"took": 0,
"timed_out": false,
"hits": {
"total": 1,
"total_relation": "eq",
"hits": [
{
"_id": 3,
"_score": 1680,
"t2._score": 0,
"_source": {
"f": "database search",
"t2.id": 0,
"t2.f": ""
}
}
]
}
}2. 连接表上的查询作为过滤器:当连接表有查询时,仅返回同时满足连接条件和查询条件的记录。
- JSON
POST /search
{
"table": "t1",
"query": {
"query_string": "database"
},
"join": [
{
"type": "left",
"table": "t2",
"query": {
"query_string": "nonexistent"
},
"on": [
{
"left": {
"table": "t1",
"field": "id"
},
"operator": "eq",
"right": {
"table": "t2",
"field": "id"
}
}
]
}
]
}{
"took": 0,
"timed_out": false,
"hits": {
"total": 0,
"total_relation": "eq",
"hits": []
}
}3. 连接类型影响过滤:INNER JOIN 要求同时满足连接和查询条件,而LEFT JOIN即使右表条件失败也会返回匹配的左表行。
在使用连接中的全文匹配时,请注意以下几点:
-
表特定匹配:
- SQL:每个
MATCH()函数应指定要搜索的表:MATCH('term', table_name) - JSON:使用根级的
"query"用于主表,并在每个连接定义中的"query"用于连接表
- SQL:每个
-
查询语法灵活性:JSON API 支持
"query_string"和"match"两种全文查询语法 -
性能影响:在两个表上执行全文匹配可能会影响查询性能,特别是在大数据集上。考虑使用适当的索引和批次大小。
-
NULL/默认值处理:使用LEFT JOIN时,如果右表中没有匹配记录,查询优化器将根据性能决定是否先评估全文条件还是过滤条件。SQL返回NULL值,而JSON API返回默认值(数字为0,文本为空字符串)。
-
过滤行为:连接表上的查询作为过滤器 - 它们限制结果为同时满足连接和查询条件的记录。
-
全文运算符支持:所有全文运算符都支持在连接查询中使用,包括短语、接近、字段搜索、NEAR、共识匹配和高级运算符。
-
评分计算:每个表都维护自己的相关性评分,可通过SQL中的
table_name.weight()或JSON响应中的table_name._score访问。
在前面的示例基础上,让我们探索一个更高级的场景,其中我们将表连接与跨多个表的全文匹配和分面结合。这展示了Manticore连接功能的全部力量,包括复杂的过滤和聚合。
该查询演示了在 customers 和 orders 表上进行全文匹配,并结合范围过滤和分面功能。它搜索名为 "Alice" 或 "Bob" 的客户及其包含 "laptop"、"phone" 或 "tablet" 且价格高于 $500 的订单。结果按订单 ID 排序,并按保修条款进行分面。
- SQL
- JSON
SELECT orders.product, name, orders.details.price, orders.tags
FROM customers
LEFT JOIN orders ON customers.id = orders.customer_id
WHERE orders.details.price > 500
AND MATCH('laptop | phone | tablet', orders)
AND MATCH('alice | bob', customers)
ORDER BY orders.id ASC
FACET orders.details.warranty;POST /search
{
"table": "customers",
"query": {
"bool": {
"must": [
{
"range": {
"orders.details.price": {
"gt": 500
}
},
"query_string": "alice | bob"
]
}
},
"join": [
{
"type": "left",
"table": "orders",
"query": {
"query_string": "laptop | phone | tablet"
},
"on": [
{
"left": {
"table": "customers",
"field": "id"
},
"operator": "eq",
"right": {
"table": "orders",
"field": "customer_id"
}
}
]
}
],
"_source": ["orders.product", "name", "orders.details.price", "orders.tags"],
"sort": [{"orders.id": "asc"}],
"aggs": {
"warranty_facet": {
"terms": {
"field": "orders.details.warranty"
}
}
}
}+-----------------+---------------+----------------------+-------------+
| orders.product | name | orders.details.price | orders.tags |
+-----------------+---------------+----------------------+-------------+
| Laptop Computer | Alice Johnson | 1200 | 101,102 |
| Smart Phone | Bob Smith | 800 | 103 |
+-----------------+---------------+----------------------+-------------+
2 rows in set (0.00 sec)
+-------------------------+----------+
| orders.details.warranty | count(*) |
+-------------------------+----------+
| 2 years | 1 |
| 1 year | 1 |
+-------------------------+----------+
2 rows in set (0.00 sec){
"took": 0,
"timed_out": false,
"hits": {
"total": 3,
"total_relation": "eq",
"hits": [
{
"_id": 1,
"_score": 1,
"orders._score": 1565,
"_source": {
"name": "Alice Johnson",
"orders.tags": [
101,
102
],
"orders.product": "Laptop Computer"
}
},
{
"_id": 2,
"_score": 1,
"orders._score": 1565,
"_source": {
"name": "Bob Smith",
"orders.tags": [
103
],
"orders.product": "Smart Phone"
}
},
{
"_id": 1,
"_score": 1,
"orders._score": 1565,
"_source": {
"name": "Alice Johnson",
"orders.tags": [
101,
104
],
"orders.product": "Tablet Device"
}
}
]
},
"aggregations": {
"warranty_facet": {
"buckets": [
{
"key": "2 years",
"doc_count": 1
},
{
"key": "1 year",
"doc_count": 2
}
]
}
}
}可以为连接查询中的左表和右表分别指定不同的选项。SQL 查询的语法是 OPTION(<table_name>),JSON 查询则是在 "options" 下使用一个或多个子对象。
以下示例展示了如何为右表的全文查询指定不同的字段权重。要通过 SQL 获取匹配权重,请使用 <table_name>.weight() 表达式。
在 JSON 查询中,此权重表示为 <table_name>._score。
- SQL
- JSON
SELECT product, customers.email, customers.name, customers.address, customers.weight()
FROM orders
INNER JOIN customers
ON customers.id = orders.customer_id
WHERE MATCH('maple', customers)
OPTION(customers) field_weights=(address=1500);POST /search
{
"table": "orders",
"options": {
"customers": {
"field_weights": {
"address": 1500
}
}
},
"join": [
{
"type": "inner",
"table": "customers",
"query": {
"query_string": "maple"
},
"on": [
{
"left": {
"table": "orders",
"field": "customer_id"
},
"operator": "eq",
"right": {
"table": "customers",
"field": "id"
}
}
]
}
],
"_source": ["product", "customers.email", "customers.name", "customers.address"]
}+---------+-------------------+----------------+-------------------+--------------------+
| product | customers.email | customers.name | customers.address | customers.weight() |
+---------+-------------------+----------------+-------------------+--------------------+
| Laptop | alice@example.com | Alice Johnson | 123 Maple St | 1500680 |
| Tablet | alice@example.com | Alice Johnson | 123 Maple St | 1500680 |
+---------+-------------------+----------------+-------------------+--------------------+
2 rows in set (0.00 sec){
"took": 0,
"timed_out": false,
"hits": {
"total": 2,
"total_relation": "eq",
"hits": [
{
"_id": 1,
"_score": 1,
"customers._score": 15000680,
"_source": {
"product": "Laptop",
"customers.email": "alice@example.com",
"customers.name": "Alice Johnson",
"customers.address": "123 Maple St"
}
},
{
"_id": 3,
"_score": 1,
"customers._score": 15000680,
"_source": {
"product": "Tablet",
"customers.email": "alice@example.com",
"customers.name": "Alice Johnson",
"customers.address": "123 Maple St"
}
}
]
}
}在执行表连接时,Manticore Search 会以批处理方式处理结果,以优化性能和资源使用。其工作原理如下:
-
批处理如何工作:
- 首先执行左表的查询,并将结果累积到一个批次中。
- 然后,该批次作为右表查询的输入,右表查询作为单个操作执行。
- 这种方法最大限度地减少了发送到右表的查询次数,提高了效率。
-
配置批次大小:
- 可以使用
join_batch_size搜索选项调整批次的大小。 - 也可以在配置文件的
searchd部分中配置此选项。 - 默认批次大小为
1000,但您可以根据您的用例增加或减少它。 - 设置
join_batch_size=0将完全禁用批处理,这可能对调试或特定场景有用。
- 可以使用
-
性能考虑:
- 较大的批次大小可以通过减少在右表上执行的查询次数来提高性能。
- 但是,较大的批次可能会消耗更多内存,特别是对于复杂查询或大型数据集。
- 尝试不同的批次大小,以找到性能和资源使用之间的最佳平衡点。
为了进一步优化连接操作,Manticore Search 对右表执行的查询采用了缓存机制。以下是您需要了解的内容:
-
缓存如何工作:
- 右表的每个查询都由
JOIN ON条件定义。 - 如果相同的
JOIN ON条件在多个查询中重复出现,结果将被缓存并重复使用。 - 这避免了冗余查询,并加快了后续连接操作的速度。
- 右表的每个查询都由
-
配置缓存大小:
- 连接缓存的大小可以通过配置文件
searchd部分中的 join_cache_size 选项进行配置。 - 默认缓存大小为
20MB,但您可以根据工作负载和可用内存进行调整。 - 设置
join_cache_size=0将完全禁用缓存。
- 连接缓存的大小可以通过配置文件
-
内存考虑:
- 每个线程维护自己的缓存,因此总内存使用量取决于线程数量和缓存大小。
- 请确保您的服务器有足够的内存来容纳缓存,特别是在高并发环境中。
仅由本地表组成的分布式表在连接查询的左侧和右侧都受支持。但是,包含远程表的分布式表不受支持。
在 Manticore Search 中使用 JOIN 时,请记住以下几点:
-
字段选择:在 JOIN 中选择两个表的字段时,不要为左表的字段添加前缀,但需要为右表的字段添加前缀。例如:
SELECT field_name, right_table.field_name FROM ... -
JOIN 条件:始终在 JOIN 条件中明确指定表名:
JOIN ON table_name.some_field = another_table_name.some_field -
使用 JOIN 的表达式:当使用组合了连接表中两个字段的表达式时,请为表达式的结果设置别名:
SELECT *, (nums2.n + 3) AS x, x * n FROM nums LEFT JOIN nums2 ON nums2.id = nums.num2_id -
对带别名的表达式进行过滤:不能在 WHERE 子句中使用涉及两个表字段的表达式的别名。
-
JSON 属性:在 JSON 属性上进行连接时,必须将值显式转换为适当的类型:
-- Correct: SELECT * FROM t1 LEFT JOIN t2 ON int(t1.json_attr.id) = t2.json_attr.id -- Incorrect: SELECT * FROM t1 LEFT JOIN t2 ON t1.json_attr.id = t2.json_attr.id -
NULL 处理:可以在连接字段上使用 IS NULL 和 IS NOT NULL 条件:
SELECT * FROM t1 LEFT JOIN t2 ON t1.id = t2.id WHERE t2.name IS NULL SELECT * FROM t1 LEFT JOIN t2 ON t1.id = t2.id WHERE t2.name IS NOT NULL -
在 MVA 中使用 ANY:在 JOIN 中对多值属性使用
ANY()函数时,请为连接表中的多值属性设置别名:SELECT *, t2.m AS alias FROM t LEFT JOIN t2 ON t.id = t2.t_id WHERE ANY(alias) IN (3, 5)
遵循这些指南,您可以有效地在 Manticore Search 中使用 JOIN 来组合来自多个索引的数据并执行复杂查询。
Manticore 通过 SQL 和 HTTP 支持使用任意的算术表达式,结合属性值、内部属性(文档 ID 和相关度权重)、算术运算、多种内建函数以及用户自定义函数。以下是完整的参考列表,方便快速查阅。
+, -, *, /, %, DIV, MOD
提供标准算术运算符。使用这些运算符的算术计算可以以三种不同模式执行:
- 使用单精度、32 位 IEEE 754 浮点值(默认),
- 使用有符号 32 位整数,
- 使用有符号 64 位整数。
表达式解析器会自动切换到整数模式,如果没有任何运算结果为浮点值。否则,使用默认的浮点模式。例如,当两个参数都是 32 位整数时,a+b 会以 32 位整数计算;当两个参数都是整数但其中一个为 64 位时,使用 64 位整数;否则以浮点计算。然而,a/b 或 sqrt(a) 始终以浮点计算,因为这些运算返回非整数结果。为避免这种情况,可以使用 IDIV(a,b) 或 DIV b 形式。另外,当参数是 32 位时,a*b 不会自动提升到 64 位。若要强制使用 64 位结果,请使用 BIGINT(),但注意如果存在非整数运算,BIGINT() 将被忽略。
<, > <=, >=, =, <>
比较运算符返回 1.0 时条件为真,返回 0.0 时条件为假。例如,当属性 a 等于属性 b 时,表达式 (a=b)+3 计算值为 4;当 a 不等于 b 时,计算值为 3。与 MySQL 不同,等值比较(即 = 和 <> 运算符)包含一个小的相等阈值(默认是 1e-6)。如果比较值的差距在阈值内,则认为它们相等。
对于多值属性,BETWEEN 和 IN 运算符只要至少一个值满足条件就返回真(类似于 ANY())。IN 运算符不支持 JSON 属性。IS (NOT) NULL 运算符仅支持 JSON 属性。
AND, OR, NOT
布尔运算符(AND、OR、NOT)的行为符合预期。它们是左结合的,并且优先级最低。NOT 的优先级高于 AND 和 OR,但仍低于其他运算符。AND 和 OR 具有相同优先级,建议在复杂表达式中使用括号以避免混淆。
&, |
这些运算符分别执行按位与和按位或。操作数必须是整数类型。
- ABS()
- ALL()
- ANY()
- ATAN2()
- BIGINT()
- BITDOT()
- BM25F()
- CEIL()
- CONCAT()
- CONTAINS()
- COS()
- CRC32()
- DATE_HISTOGRAM()
- DATE_RANGE()
- DAY()
- DOUBLE()
- EXP()
- FIBONACCI()
- FLOOR()
- GEODIST()
- GEOPOLY2D()
- GREATEST()
- HOUR()
- HISTOGRAM()
- IDIV()
- IF()
- IN()
- INDEXOF()
- INTEGER()
- INTERVAL()
- LAST_INSERT_ID()
- LEAST()
- LENGTH()
- LN()
- LOG10()
- LOG2()
- MAX()
- MIN()
- MINUTE()
- MIN_TOP_SORTVAL()
- MIN_TOP_WEIGHT()
- MONTH()
- NOW()
- PACKEDFACTORS()
- POLY2D()
- POW()
- RAND()
- RANGE()
- REGEX()
- REMAP()
- SECOND()
- SIN()
- SINT()
- SQRT()
- SUBSTRING_INDEX()
- TO_STRING()
- UINT()
- YEAR()
- YEARMONTH()
- YEARMONTHDAY()
- WEIGHT()
在 HTTP JSON 接口中,通过 script_fields 和 expressions 支持表达式。
{
"table": "test",
"query": {
"match_all": {}
}, "script_fields": {
"add_all": {
"script": {
"inline": "( gid * 10 ) | crc32(title)"
}
},
"title_len": {
"script": {
"inline": "crc32(title)"
}
}
}
}
在这个示例中,创建了两个表达式:add_all 和 title_len。第一个表达式计算 ( gid * 10 ) | crc32(title) 并将结果存储在 add_all 属性中。第二个表达式计算 crc32(title) 并将结果存储在 title_len 属性中。
目前,仅支持 inline 表达式。inline 属性(要计算的表达式)的值具有与 SQL 表达式相同的语法。
表达式名称可以在过滤或排序中使用。
- script_fields
{
"table":"movies_rt",
"script_fields":{
"cond1":{
"script":{
"inline":"actor_2_facebook_likes =296 OR movie_facebook_likes =37000"
}
},
"cond2":{
"script":{
"inline":"IF (IN (content_rating,'TV-PG','PG'),2, IF(IN(content_rating,'TV-14','PG-13'),1,0))"
}
}
},
"limit":10,
"sort":[
{
"cond2":"desc"
},
{
"actor_1_name":"asc"
},
{
"actor_2_name":"desc"
}
],
"profile":true,
"query":{
"bool":{
"must":[
{
"match":{
"*":"star"
}
},
{
"equals":{
"cond1":1
}
}
],
"must_not":[
{
"equals":{
"content_rating":"R"
}
}
]
}
}
}默认情况下,表达式值包含在结果集的 _source 数组中。如果源是选择性的(参见 源选择),可以在请求中将表达式名称添加到 _source 参数中。注意,表达式的名称必须是小写的。
expressions 是 script_fields 的替代方案,语法更简单。示例请求添加了两个表达式并将结果存储在 add_all 和 title_len 属性中。注意,表达式的名称必须是小写的。
- expressions
{
"table": "test",
"query": { "match_all": {} },
"expressions":
{
"add_all": "( gid * 10 ) | crc32(title)",
"title_len": "crc32(title)"
}
}SQL SELECT 子句和 HTTP /search 端点支持许多选项,可用于微调搜索行为。
SQL:
SELECT ... [OPTION <optionname>=<value> [ , ... ]] [/*+ [NO_][ColumnarScan|DocidIndex|SecondaryIndex(<attribute>[,...])]] /*]
HTTP:
POST /search
{
"table" : "table_name",
"options":
{
"optionname": "value",
"optionname2": <value2>
}
}
- SQL
- JSON
SELECT * FROM test WHERE MATCH('@title hello @body world')
OPTION ranker=bm25, max_matches=3000,
field_weights=(title=10, body=3), agent_query_timeout=10000POST /search
{
"table" : "test",
"query": {
"match": {
"title": "hello"
},
"match": {
"body": "world"
}
},
"options":
{
"ranker": "bm25",
"max_matches": 3000,
"field_weights": {
"title": 10,
"body": 3
},
"agent_query_timeout": 10000
}
}+------+-------+-------+
| id | title | body |
+------+-------+-------+
| 1 | hello | world |
+------+-------+-------+
1 row in set (0.00 sec){
"took": 0,
"timed_out": false,
"hits": {
"total": 1,
"total_relation": "eq",
"hits": [
{
"_id": 1,
"_score": 10500,
"_source": {
"title": "hello",
"body": "world"
}
}
]
}
}支持的选项包括:
整数。启用或禁用在多线程运行 groupby 查询时的保证聚合准确性。默认值为 0。
当运行 groupby 查询时,可以在普通表上使用多个伪分片并行运行(如果 pseudo_sharding 开启)。类似的方法适用于 RT 表。每个分片/块执行查询,但组的数量受 max_matches 限制。如果不同分片/块的结果集包含不同的组,组计数和聚合可能会不准确。注意 Manticore 会根据 groupby 属性的唯一值数量(从二级索引中检索)将 max_matches 增加到 max_matches_increase_threshold。如果成功,将不会出现准确性损失。
然而,如果 groupby 属性的唯一值数量很高,进一步增加 max_matches 可能不是一个好策略,因为它可能导致性能下降和内存使用增加。将 accurate_aggregation 设置为 1 会强制 groupby 搜索在单线程中运行,从而解决准确性问题。注意,仅当 max_matches 无法设置得足够高时,才会强制单线程运行;否则,带有 accurate_aggregation=1 的搜索仍会在多线程中运行。
总体而言,将 accurate_aggregation 设置为 1 可确保 RT 表和 pseudo_sharding=1 的普通表中的组计数和聚合准确性。缺点是搜索会变慢,因为它们将被强制在单线程中运行。
然而,如果我们有一个 RT 表和一个包含相同数据的普通表,并运行带有 accurate_aggregation=1 的查询,我们仍可能收到不同的结果。这是因为守护进程可能会根据 max_matches_increase_threshold 设置为 RT 表和普通表选择不同的 max_matches 值。
整数。等待远程查询完成的最大时间(以毫秒为单位),请参见 此部分。
0 或 1(默认为 1)。boolean_simplify=1 启用 查询简化 以加快速度。
此选项也可以在 searchd 配置 中全局设置,以更改所有查询的默认行为。每个查询的选项将覆盖全局设置。
字符串,用户评论会被复制到查询日志文件中。
整数。指定要处理的最大匹配数。如果未设置,Manticore 将自动选择一个适当的值。
N = 0:禁用匹配数的限制。N > 0:指示 Manticore 在找到N个匹配文档后立即停止处理结果。- 未设置:Manticore 自动决定阈值。
当 Manticore 无法确定匹配文档的确切数量时,查询 元信息 中的 total_relation 字段将显示 gte,表示 大于或等于。这表明实际匹配数至少为报告的 total_found(在 SQL 中)或 hits.total(在 JSON 中)。当计数准确时,total_relation 将显示 eq。
注意:在聚合查询中使用 cutoff 不推荐,因为它可能导致不准确或不完整的结果。
- Example
在聚合查询中使用 cutoff 可能导致不正确或误导性的结果,如下例所示:
drop table if exists t
--------------
Query OK, 0 rows affected (0.02 sec)
--------------
create table t(a int)
--------------
Query OK, 0 rows affected (0.04 sec)
--------------
insert into t(a) values(1),(2),(3),(1),(2),(3)
--------------
Query OK, 6 rows affected (0.00 sec)
--------------
select avg(a) from t option cutoff=1 facet a
--------------
+----------+
| avg(a) |
+----------+
| 1.000000 |
+----------+
1 row in set (0.00 sec)
--- 1 out of 1 results in 0ms ---
+------+----------+
| a | count(*) |
+------+----------+
| 1 | 1 |
+------+----------+
1 row in set (0.00 sec)
--- 1 out of 1 results in 0ms ---与不使用 cutoff 的相同查询进行比较:
--------------
select avg(a) from t facet a
--------------
+----------+
| avg(a) |
+----------+
| 2.000000 |
+----------+
1 row in set (0.00 sec)
--- 1 out of 1 results in 0ms ---
+------+----------+
| a | count(*) |
+------+----------+
| 1 | 2 |
| 2 | 2 |
| 3 | 2 |
+------+----------+
3 rows in set (0.00 sec)
--- 3 out of 3 results in 0ms ---整数。默认值为 3500。此选项设置在普通表中 count distinct 返回的计数保证精确的阈值。
接受的值范围为 500 到 15500。超出此范围的值将被限制。
当此选项设置为 0 时,它启用一种确保精确计数的算法。该算法收集 {group, value} 对,对其进行排序,并定期消除重复项。结果是在普通表中精确的计数。然而,由于其高内存消耗和慢查询执行,这种方法不适合高基数数据集。
当 distinct_precision_threshold 设置为大于 0 的值时,Manticore 采用不同的算法。它将计数加载到哈希表中并返回表的大小。如果哈希表变得太大,其内容将被移动到 HyperLogLog 数据结构中。此时,计数变得近似,因为 HyperLogLog 是一种概率算法。这种方法保持每组的最大内存使用量固定,但计数准确性存在权衡。
HyperLogLog 的精度和从哈希表转换到 HyperLogLog 的阈值源自 distinct_precision_threshold 设置。使用此选项时需谨慎,因为将其值加倍也会使计算计数所需的最大内存加倍。最大内存使用量可以大致通过此公式估算:64 * max_matches * distinct_precision_threshold,尽管在实践中,计数计算通常使用的内存少于最坏情况下的内存。
0 或 1(默认为 0)。尽可能扩展精确形式和/或带星号的关键词。有关更多详细信息,请参阅 expand_keywords。
0、off、1 或任何 blend_mode 选项的组合(默认为 0)。在查询解析期间,将混合关键词(包含通过 blend_chars 配置的字符的标记)扩展为其构成变体。启用后,像 "well-being"(如果 - 在 blend_chars 中配置)这样的关键词将被扩展为 "well-being"、"wellbeing"、"well" 和 "being" 等变体,然后在查询树中被分组为 OR 子树。
支持的值为:
0或off- 禁用混合扩展(默认)。混合关键词将按正常方式处理,不进行扩展。1- 启用混合扩展,并使用表的 blend_mode 设置来确定生成哪些变体。- 任何混合模式选项 - 启用混合扩展并使用指定的混合模式,覆盖表的
blend_mode设置。
有关选项的更多详细信息,请参阅 blend_mode。
命名整数列表(按字段的用户权重用于排序)。
示例:
SELECT ... OPTION field_weights=(title=10, body=3)
使用 global_idf 文件中的全局统计信息(频率)进行 IDF 计算。
带引号的、逗号分隔的 IDF 计算标志列表。已知标志包括:
normalized:BM25 变体,idf = log((N-n+1)/n),如 Robertson 等人所述plain:普通变体,idf = log(N/n),如 Sparck-Jones 所述tfidf_normalized:此外将 IDF 除以查询词数,使TF*IDF保持在 [0, 1] 范围内tfidf_unnormalized:不将 IDF 除以查询词数,其中 N 是集合大小,n 是匹配文档数
Manticore 历史默认的 IDF(逆文档频率)等同于 OPTION idf='normalized,tfidf_normalized',这些归一化可能会导致一些不良影响。
首先,idf=normalized 会导致关键词惩罚。例如,如果你搜索 the | something,并且 the 出现在超过 50% 的文档中,那么同时包含 the 和 something 的文档的权重将低于仅包含 something 的文档。使用 OPTION idf=plain 可避免此问题。普通 IDF 的范围在 [0, log(N)] 内,关键词永远不会被惩罚;而归一化 IDF 的范围在 [-log(N), log(N)] 内,过于频繁的关键词会被惩罚。
其次,idf=tfidf_normalized 会导致 IDF 在查询之间漂移。历史上,IDF 也会除以查询关键词数,确保所有关键词的 sum(tf*idf) 总和保持在 [0,1] 范围内。然而,这意味着像 word1 和 word1 | nonmatchingword2 这样的查询会为完全相同的结果集分配不同的权重,因为 word1 和 nonmatchingword2 的 IDF 都会被除以 2。使用 OPTION idf='tfidf_unnormalized' 可解决此问题。请注意,当禁用此归一化时,BM25、BM25A、BM25F() 排名因素将相应调整。
IDF 标志可以组合;plain 和 normalized 互斥;tfidf_unnormalized 和 tfidf_normalized 也互斥;未指定的标志在这些互斥组中默认使用其原始设置。这意味着 OPTION idf=plain 等同于完整指定 OPTION idf='plain,tfidf_normalized'。
指定查询的 Jieba 分词模式。
在使用 Jieba 中文分词时,有时需要为文档和查询使用不同的分词模式。有关完整模式列表,请参阅 jieba_mode。
命名整数列表。按表的用户权重用于排序。
0 或 1,自动汇总分布式表的所有本地部分的 DF,确保本地分片表的 IDF 一致(且准确)。默认启用用于 RT 表的磁盘分片。带有通配符的查询术语被忽略。
0 或 1(默认为 0)。设置 low_priority=1 以较低优先级执行查询,其任务重新调度的频率比其他正常优先级查询低 10 倍。
整数。每查询最大匹配值。
服务器为每个表在 RAM 中保留的最大匹配数,并可返回给客户端。默认值为 1000。
引入 max_matches 设置是为了控制和限制 RAM 使用,该设置决定了在搜索每个表时将保留多少匹配项在 RAM 中。每个找到的匹配项仍会被处理,但只有最佳的 N 个匹配项会被保留在内存中,并最终返回给客户端。例如,假设一个表包含 2,000,000 个匹配项用于某个查询。很少需要检索所有匹配项。相反,你需要扫描所有匹配项,但仅选择基于某些标准(例如按相关性、价格或其他因素排序)的“最佳”500 个匹配项,并将这 500 个匹配项以每页 20 到 100 个匹配项的形式显示给最终用户。仅跟踪最佳的 500 个匹配项比保留所有 2,000,000 个匹配项、排序后丢弃除前 20 个外的所有内容,对 RAM 和 CPU 更加高效。max_matches 控制该“最佳 N”中的 N 值。
此参数会显著影响每个查询的RAM和CPU使用量。值在1,000到10,000之间通常是可以接受的,但提高限制时应谨慎使用。随意将max_matches增加到1,000,000意味着searchd必须为每个查询分配并初始化一个包含100万条匹配项的缓冲区。这不可避免地会增加每个查询的RAM使用量,并且在某些情况下可能明显影响性能。
有关它如何影响max_matches选项行为的更多信息,请参阅max_matches_increase_threshold。
整数。设置max_matches可以增加的阈值。默认值为16384。
当启用pseudo_sharding且检测到分组属性的唯一值数量小于此阈值时,Manticore可能会增加max_matches以提高分组和/或聚合的准确性。当伪分片在多个线程中执行查询或RT表在磁盘块中进行并行搜索时,可能会发生准确性下降。
如果分组属性的唯一值数量小于阈值,max_matches将设置为该数量。否则,将使用默认的max_matches。
如果在查询选项中显式设置了max_matches,则此阈值无效。
请注意,如果此阈值设置得过高,会导致内存消耗增加和整体性能下降。
您还可以使用accurate_aggregation选项强制启用保证的分组/聚合准确性模式。
设置最大搜索查询时间(以毫秒为单位)。必须是非负整数。默认值为0,表示“不限制”。本地搜索查询一旦达到指定时间就会停止。请注意,如果您执行的搜索查询了多个本地表,此限制适用于每个表。请注意,由于不断跟踪是否需要停止查询所产生的开销,这可能会略微增加查询的响应时间。
整数。最大预测搜索时间;请参阅predicted_time_costs。
none允许在表使用index_exact_words启用时,将所有查询术语替换为它们的确切形式。这对于防止对查询术语进行词干提取或词形还原很有用。
- SQL
MySQL [(none)]> select * from tbl where match('-donald');
ERROR 1064 (42000): index t: query error: query is non-computable (single NOT operator)
MySQL [(none)]> select * from t where match('-donald') option not_terms_only_allowed=1;
+---------------------+-----------+
| id | field |
+---------------------+-----------+
| 1658178727135150081 | smth else |
+---------------------+-----------+从以下选项中选择:
proximity_bm25bm25nonewordcountproximitymatchanyfieldmasksph04exprexport
有关每个排序器的更多详细信息,请参阅搜索结果排序。
允许您为ORDER BY RAND()查询指定特定的整数种子值,例如:... OPTION rand_seed=1234。默认情况下,每个查询都会自动生成一个新的且不同的种子值。
整数。分布式重试次数。
整数。分布式重试延迟(以毫秒为单位)。
字符串。用于使用Scroll分页方法分页结果的滚动令牌。
pq- 优先队列,默认设置kbuffer- 为已预先排序的数据(例如按id排序的表数据)提供更快的排序 两种情况下结果集相同;选择其中一个选项可能会简单地提高(或降低)性能。
限制当前查询处理使用的最大线程数。默认值 - 无限制(查询可以占用全局定义的threads中的所有线程)。 对于查询批次,该选项必须附加到批次中的第一个查询,并在创建工作队列时应用,对整个批次有效。此选项与选项max_threads_per_query含义相同,但仅适用于当前查询或查询批次。
用引号括起的、以冒号分隔的library name:plugin name:optional string of settings字符串。当每个表调用全文搜索时,为每个搜索创建一个查询时的标记过滤器,允许您实现自定义分词器,根据自定义规则生成标记。
SELECT * FROM index WHERE MATCH ('yes@no') OPTION token_filter='mylib.so:blend:@'
限制单个通配符扩展的关键字最大数量,默认值为0表示无限制。有关更多详细信息,请参阅expansion_limit。
在极少数情况下,Manticore内置的查询分析器可能无法正确理解查询并确定是否应使用docid索引、二级索引或列扫描。要覆盖查询优化器的决策,您可以在查询中使用以下提示:
/*+ DocidIndex(id) */强制使用docid索引,/*+ NO_DocidIndex(id) */告诉优化器忽略它/*+ SecondaryIndex(<attr_name1>[, <attr_nameN>]) */强制使用二级索引(如果可用),/*+ NO_SecondaryIndex(id) */告诉优化器忽略它/*+ ColumnarScan(<attr_name1>[, <attr_nameN>]) */强制使用列扫描(如果属性是列式的),/*+ NO_ColumnarScan(id) */告诉优化器忽略它
请注意,当使用过滤器执行全文查询时,查询优化器会决定是对全文树的结果与过滤器结果进行交集运算,还是采用标准的先匹配后过滤方法。指定任何提示都将强制守护进程使用对全文树结果与过滤器结果进行交集运算的代码路径。
有关查询优化器工作原理的更多信息,请参阅基于成本的优化器页面。
- SQL
SELECT * FROM students where age > 21 /*+ SecondaryIndex(age) */当使用MySQL/MariaDB客户端时,请确保包含--comments标志以在查询中启用提示。
- mysql
mysql -P9306 -h0 --comments