(Incomplete) MBQL Reference
(未完成)MBQL 参考
This guide is incomplete, and it is also out of date. Some features are missing or different, and others could use clarification. For an always up-to-date guide to the syntax of MBQL, check out https://github.com/metabase/metabase/blob/master/src/metabase/mbql/schema.cljc.
本指南不完整且已过时。部分功能缺失或存在差异,另一些可能需要进一步说明。要获取始终最新的 MBQL 语法指南,请查阅 https://github.com/metabase/metabase/blob/master/src/metabase/mbql/schema.cljc。
This guide is still useful to understand some of the design philosophy around MBQL, but should no longer be used as a grammar reference.
本指南对于理解 MBQL 的部分设计理念仍有帮助,但不应再作为语法参考使用。
MBQL, the Metabase Query Language, is the language used to express "structured" queries in Metabase. It is generated by the Query Builder in the Metabase frontend, and translated into a myriad of "native" query languages -- including SQL, Mongo Aggregation Pipelines, and Druid -- by the backend. (Metabase can also run queries in these native languages directly, but those are not considered part of MBQL).
MBQL(Metabase 查询语言)是用于在 Metabase 中表达“结构化”查询的语言。它由 Metabase 前端的查询构建器生成,并由后端翻译成多种“原生”查询语言——包括 SQL、Mongo 聚合管道和 Druid 等(Metabase 也可以直接运行这些原生语言的查询,但这些不被视为 MBQL 的一部分)。
If you're new to MBQL or this document, please familiarize yourself
with MBQL's core concepts, specifically the conventions used in the MBQL grammar and examples.
如果您是 MBQL 或本文档的新用户,请先熟悉 MBQL 的核心概念,特别是 MBQL 语法和示例中使用的约定。
- {
:aggregation
aggregation-clause
}-
aggregation-clause:
- [
:avg
concrete-field
] - [
:count
] - [
:count
concrete-field
] - [
:count-where
filter-clause
] - [
:cum-count
concrete-field
] - [
:cum-sum
concrete-field
] - [
:distinct
concrete-field
] - [
:stddev
concrete-field
] - [
:sum
concrete-field
] - [
:sum-where
concrete-field
filter-clause
] - [
:min
concrete-field
] - [
:max
concrete-field
] - [
:share
filter-clause
]
- [
-
- {
:breakout
[&concrete-field
]} - {
:expressions
{expression-name
expression
}} - {
:fields
[&field
]} - {
:filter
filter-clause
}-
filter-clause
: - [
:and
&filter-clause
]
[:and
和filter-clause
] - [
:or
&filter-clause
]
[:or
和filter-clause
] - [
:not
filter-clause
] - [
:=
concrete-field
value
&value
]
[:=
concrete-field
value
和value
] - [
:!=
concrete-field
value
&value
]
[:!=
concrete-field
value
和value
] - [
:<
concrete-field
orderable-value
] - [
:>
concrete-field
orderable-value
] - [
:<=
concrete-field
orderable-value
] - [
:>=
concrete-field
orderable-value
] - [
:is-null
concrete-field
] - [
:not-null
concrete-field
] - [
:between
concrete-field
minorderable-value
maxorderable-value
]
[:between
concrete-field
最小值orderable-value
最大值orderable-value
] - [
:inside
latconcrete-field
lonconcrete-field
lat-maxnumeric-literal
lon-minnumeric-literal
lat-minnumeric-literal
lon-maxnumeric-literal
]
[:inside
纬度concrete-field
经度concrete-field
纬度最大值numeric-literal
经度最小值numeric-literal
纬度最小值numeric-literal
经度最大值numeric-literal
] - [
:starts-with
concrete-field
string-literal
] - [
:contains
concrete-field
string-literal
] - [
:does-not-contain
concrete-field
string-literal
] - [
:ends-with
concrete-field
string-literal
] - [
:time-interval
fieldconcrete-field
n:current
|:last
|:next
|integer-literal
unitrelative-datetime-unit
]
[:time-interval
字段concrete-field
数值:current
|:last
|:next
|integer-literal
单位relative-datetime-unit
]
-
- {
:limit
integer-literal
} - {
:order-by
[&order-by-clause
]}-
order-by-clause
:- [direction
:asc
|:desc
fieldfield
]
[方向:asc
|:desc
字段field
]
- [direction
-
- {
:page
{:page
integer-literal
,:items
integer-literal
}} - {
:source-table
integer-literal
}
For the most part, this document uses Clojure in the examples and its terminology and syntax.
本文档大部分示例采用 Clojure 语言,并沿用其术语与语法体系。
- Literal
:tokens
are written in monospaced font with a preceding:
. In queries, you may pass these tokens as either strings or Lisp keywords. See the conceptual explanation of tokens for more information.
字面量:tokens
以等宽字体书写,并带有前置的:
符号。在查询中,您可以将这些标记作为字符串或 Lisp 关键字传递。有关标记的概念性说明,请参阅相关解释。 -
parameter-names are used in certain cases for clarification.
参数名称在某些情况下用于澄清说明。 - optional varargs are written as in a Clojure binding, using
&
可选可变参数采用 Clojure 绑定写法,使用&
表示。 -
Maps/dictionaries/hashes/associative arrays are written in Clojure syntax, e.g.
{:key "value"}
映射/字典/哈希/关联数组采用 Clojure 语法编写,例如{:key "value"}
For example, a grammar like this:
例如,像这样的语法:
- [
:=
fieldconcrete-field
valuevalue
& additional-valuesvalue
]
[:=
字段concrete-field
值value
及附加值value
]
Could correspond to one of these clauses in real life:
在现实生活中可能对应以下条款之一:
[:= [:field-id 5] 100]
["=" ["field-id" 5] 100 200]
In examples, ->
is used to denote the output of a query or expression, especially in cases where an expression is automatically translated to another form.
<angle-brackets>
are used for clarity to represent arguments to expressions instead of giving literal values like in the previous example:
在示例中, ->
用于表示查询或表达式的输出结果,特别是在表达式被自动转换为另一种形式的情况下。而 <angle-brackets>
则用于清晰表示表达式的参数,而非像前例那样直接给出字面值:
[:= <field> <v1> <v2>]
-> [:or [:= <field> <v1>]
[:= <field> <v2>]]
Tokens are literal keywords (in the Lisp/Ruby sense) or strings in MBQL and have special meaning, much like keywords in a programming language. You'll usually see them at
the beginning of a bracketed expression:
在 MBQL 中,标记是字面意义上的关键字(类似 Lisp/Ruby 中的概念)或字符串,具有特殊含义,类似于编程语言中的关键字。通常它们会出现在方括号表达式开头:
[:= <field> <value>] ; := is a token
When writing a query, you may use either Clojure keywords or strings for tokens. Tokens are also case-insensitive and lisp-case
/snake_case
insensitive.
For example, these query fragments are all understood to be the same thing:
编写查询时,标记可使用 Clojure 关键字或字符串形式。标记同时不区分大小写及 lisp-case
/ snake_case
格式。例如,以下查询片段均会被解析为相同含义:
{:source_table 100}
{:source-table 100}
{"SOURCE_TABLE" 100}
{"source_table" 100}
In the examples in this document, tokens are written in a monospaced font with a preceding colon, like :lisp-keywords
. For consistency, all tokens are written in (lower-case)
lisp-case
.
本文档示例中,标记(token)以等宽字体显示并带有前置冒号,如 :lisp-keywords
。为保持统一,所有标记均采用(小写) lisp-case
书写。
Most of the forms in MBQL '98 look something like:
MBQL '98 中的大多数形式大致如下:
[:= [:field-id 100] "COOL"]
It may be helpful to think of MBQL as a square-bracketed LISP; indeed, this is how such expressions are
internally implemented.
The expression above becomes a function call like:
不妨将 MBQL 视为一种带方括号的 LISP 语言;实际上,其内部实现正是如此。上述表达式会转化为类似这样的函数调用:
(= (field-id 100) "COOL")
The parameters to MBQL's square-bracked funcalls are rvalues in the C++ sense:
MBQL 中方括号函数调用的参数在 C++意义上属于右值:
An rvalue is a temporary value that does not persist beyond the expression that uses it.
右值是一种临时值,其生命周期不会超过使用它的表达式。
An rvalue is either a field reference
or a literal value
.
右值要么是 field reference
,要么是 literal value
。
A field reference rvalue is a concrete reference to a metabase.models.field/Field
(such as its ID
) or some
derivative calculation thereof (an expression reference), or an aggregate field reference to one of the fields
produced by the query's aggregation being performed, such as :count
.
字段引用右值是对 metabase.models.field/Field
(如其 ID
)的具体引用,或对其衍生计算(表达式引用)的引用,亦或是针对查询执行聚合操作所产生字段的聚合字段引用,例如 :count
。
A concrete Field reference refers to a column represented internally by a metabase.models.field/Field
.
All columns in a database are mapped to Fields
on the backend, so a
concrete Field reference effectively means any existing column in the database (as opposed to one generated as the result of a query, such as a count
column
returned when doing an aggregation
). You can think of a concrete field as any column that would come back when doing a SQL SELECT(*)
query.
具体字段引用指的是由内部 metabase.models.field/Field
表示的列。数据库中的所有列在后端都映射到 Fields
,因此具体字段引用实际上指的是数据库中的任何现有列(与查询生成的列相对,例如执行 aggregation
时返回的 count
列)。您可以将具体字段视为执行 SQL SELECT(*)
查询时会返回的任何列。
A field reference may be local
or foreign
, optionally wrapped by a datetime-field
form; for most intents and purposes, a
expression reference is also considered a "concrete field" and may be used in the same places.
字段引用可以是 local
或 foreign
,可选择性地用 datetime-field
形式包裹;在大多数情况下,表达式引用也被视为"具体字段",可在相同场景中使用。
Local field references are the most common type of field references. Local simply means that the Field
being referenced belongs to a query's source table
(as opposed to a different Table in the Database).
本地字段引用是最常见的字段引用类型。"本地"意味着被引用的 Field
属于查询的源表(而非数据库中的其他表)。
Local field references look like:
本地字段引用格式如下:
- [
:field-id
integer-literal
]
e.g. 例如
[:field-id 100] ; the metabase.models.Field whose ID is 100
In older versions of MBQL, raw integer literals were used for local field references. This caused
ambiguity with integer literals in certain clauses, so each clause assumed certain argument positions were always field references, while others were always assumed
to be values. As such, in older versions of MBQL it was impossible to include multiple Fields in a single filter clause:
在旧版 MBQL 中,本地字段引用直接使用原始整数字面量。这导致某些子句中整数字面量存在歧义,因此每个子句默认某些参数位置始终是字段引用,而其他位置则始终被视为值。正因如此,在旧版 MBQL 中无法在单个过滤子句中包含多个字段:
;; MBQL '98
{:filter [[:= [:field-id 10] [:field-id 11]]]} ; return rows where Field 10 == Field 11
;; MBQL '95
{"filter" [["=" 10 11]] ; Oops! Return rows where Field 10 == 11
MBQL '98 eliminates this ambiguity in order to relax the restrictions on where Fields can be used in filter clauses.
MBQL '98 通过消除这种歧义来放宽过滤子句中字段使用位置的限制。
Foreign field references are just like local references, but are used to refer to Fields
belonging to a Table
other than
the source table. Access to these Fields requires JOINS
to their parent Table. To avoid ambiguity when
performing the join, you must specify a Field belonging to the source Table that is a foreign key to use to perform the join.
外部字段引用与本地引用类似,但用于指向不属于源表的 Fields
。访问这些字段需要 Table
其父表的 JOINS
。为避免连接操作时的歧义,必须指定源表中作为外键的字段来执行连接。
Foreign field references look like:
外部字段引用格式如下:
- [
:fk->
fk-field-idinteger-literal
, dest-field-idinteger-literal
]
[:fk->
外键字段 IDinteger-literal
, 目标字段 IDinteger-literal
]
ex. 例如
;; Order by Field 20, which belongs to a different Table. Join to its Table via Field 10, which belongs to the source Table.
{:order-by [[:asc [:fk-> 10 20]]}
It may be helpful to think of the form as suggesting you access dest-field-id through fk-field-id.
可以这样理解该形式:通过外键字段 ID 来访问目标字段 ID。
The Field with fk-field-id must have a corresponding metabase.models.ForeignKey
instance, which should be created automatically during the sync process. For obvious reasons,
databases that don't support joins cannot use foreign field references; drivers that do support them declare the feature :foreign-keys
.
具有外键字段 ID 的字段必须存在对应的 metabase.models.ForeignKey
实例,该实例应在同步过程中自动创建。显然,不支持连接的数据库无法使用外键字段引用;支持此功能的驱动程序需声明该特性 :foreign-keys
。
- [
:datetime-field
local-field-reference
|foreign-field-reference
datetime-unit
]
Datetime Fields
wrap a datetime field and specify how its values should be bucketed. Datetime fields used without being wrapped in a datetime-field
form are automatically converted to datetime-field
forms with :day
bucketing for legacy backward-compatibility reasons. This is not usually what you want, so be sure to use the :datetime-field
form to pick the bucketing you'd like!
Datetime Fields
用于包裹日期时间字段并指定其值应如何分桶。未经 datetime-field
形式包裹直接使用的日期时间字段,出于向后兼容性考虑会自动转换为采用 :day
分桶方式的 datetime-field
形式。这通常不符合预期,因此请务必使用 :datetime-field
形式来选择所需的分桶方式!
datetime-field
forms can be used anywhere you can otherwise use a local
or foreign
field reference.
datetime-field
形式可用于任何原本使用 local
或 foreign
字段引用的场景。
;; Row count broken out by Field 20, grouped by day (the default)
{:aggregation [:count]
:breakout [[:field-id 20]]}
;; Row count broken out by Field 20, grouped by year
{:aggregation [:count]
:breakout [[:datetime-field [:field-id 20] :year]]}
Older versions of MBQL required an AS
in the datetime-field
form in a misguided
attempt to improve readability, e.g.:
旧版 MBQL 曾错误地要求在 datetime-field
形式中使用 AS
以试图提升可读性,例如:
;; MBQL '95
["datetime_field" 100 "as" "week-of-year"]
This parameter should be considered deprecated now that MBQL has been re-envisioned as a square-bracketed Lisp. MBQL '98 will currently accept the older
syntax, albeit begrudgingly.
由于 MBQL 已被重新构想为方括号 Lisp 风格,该参数现应视为已弃用。MBQL '98 目前仍会勉强接受旧语法。
An aggregate field reference refers to one of the fields produced by the query's aggregation, such as :count
. It has the form:
聚合字段引用指向查询聚合产生的某个字段,例如 :count
。其形式为:
- [
:aggregation
indexinteger-literal
]
[:aggregation
索引integer-literal
]
index is the index of the aggregation
in question. Since MBQL current only supports a single aggregation clause, index currently must always be zero.
索引表示所引用 aggregation
的位置序号。由于 MBQL 当前仅支持单一聚合子句,索引目前必须始终为零。
aggregate-field
clauses are primarily used for ordering results:
aggregate-field
子句主要用于对结果进行排序:
{:aggregation [:sum [:field-id 10]]
:breakout [[:field-id 20]]
:order-by [[:desc [:aggregation 0]]} ; order by sum, descending
- [
:expression
expression-namestring-literal
]
[:expression
表达式名称string-literal
]
An expression reference refers to one of the expressions added to the query. Suppose you have an :expressions
clause like this:
表达式引用指向查询中添加的某个表达式。假设您有如下 :expressions
子句:
{:expressions {:sales_tax [:- <id of total> <id of subtotal>]}}
You can refer to this expression elsewhere in the query, e.g. in the :order-by
clause:
您可以在查询的其他位置引用此表达式,例如在 :order-by
子句中:
{:order-by [[:desc [:expression :sales_tax]]]} ; sort results of the query by the value of (total - subtotal)
For purposes of this grammar an expression reference can be considered a concrete field reference and used anywhere one is accepted. The sole exception is within the :expressions
clause itself, where expression references aren't allowed.
在本语法中,表达式引用可视为具体字段引用,并可在任何接受字段引用的地方使用。唯一例外是在 :expressions
子句内部,此处不允许使用表达式引用。
A value can be nil
, a boolean (true
or false
), a string literal
, a numeric literal
, or a datetime literal
.
值可以是 nil
、布尔值( true
或 false
)、 string literal
、 numeric literal
或 datetime literal
。
Values are used in filter clauses
to restrict the results returned by a query. Some filter clauses, like =
, can accept any type of
value; others are more restrictive, and only accept certain types of values. Several filters such as <
only accept values that are considered
orderable, i.e. numbers and datetimes.
值用于 filter clauses
中以限制查询返回的结果。某些过滤子句(如 =
)可接受任何类型的值;其他子句限制更严格,仅接受特定类型的值。诸如 <
等若干过滤器仅接受可排序的值,即数字和日期时间类型。
A (presumably UTF-8 encoded) string literal. Implemented internally as a java.lang.String
.
(推测为 UTF-8 编码的)字符串字面量,内部实现为 java.lang.String
。
"I'm a string literal!😋"
An orderable value is something that makes sense in comparisons like <
-- either a numeric literal
or a datetime literal
.
可排序值指在类似 <
的比较中有意义的对象——可以是 numeric literal
或 datetime literal
。
[:= <field> true] ; makes sense
[:< <field> true] ; doesn't make sense -- values of a field cannot be "less than" true
[:< <field> 100] ; makes sense, values of a field can be less than 100
Either integer literals
or floating-point literals
.
可以是 integer literals
或 floating-point literals
。
A integer. Internally, these are represented java.lang.Integer
, which is 32-bit; thus MBQL integers cannot be
less than -231 or greater than 231-1.
一个整数。在内部,这些数值以 java.lang.Integer
表示(32 位),因此 MBQL 中的整数不能小于 -231 或大于 231-1 。
A double-precision floating point number. Internally, these are represented by java.lang.Double
.
双精度浮点数。在内部,这些由 java.lang.Double
表示。
A datetime literal represents a moment in time, either absolute or relative to the moment the query is ran.
一个日期时间字面量表示时间中的一个时刻,可以是绝对的,也可以是相对于查询运行时刻的。
- To represent an absolute moment in time, use an ISO-8601 formatted string. In Clojure, you may also use a
java.sql.Date
or one of its subclasses, such asjava.sql.Timestamp
.
要表示绝对时间点,请使用 ISO-8601 格式的字符串。在 Clojure 中,也可以使用java.sql.Date
或其子类,例如java.sql.Timestamp
。 - To represent a moment in time relative to when the query is ran, use a
relative-datetime form
.
要表示相对于查询运行时刻的时间点,请使用relative-datetime form
。
ISO-8601 formatted strings are used to represent an absolute moment in time.
ISO-8601 格式的字符串用于表示绝对时间点。
Some examples of valid ISO-8601 strings:
有效的 ISO-8601 字符串示例:
"2014"
"2014-09"
"2014-09-21"
"2015-09-20T19:31"
"2015-09-20T19:31:36"
"2015-09-20T19:31:36Z"
"2015-09-20T19:31:36.000"
"2015-09-20T19:31:36.000Z"
"2015-09-20T19:31:36.000+00:00"
Some QP code actually parses datetime strings according to RFC 3339, which is basically a subset of ISO 8601. You probably won't need to
worry about the differences between the two; almost every string valid in format is valid in the other. ISO-8601 is a much more widely-known standard than RFC 3339; for the sake of
familiarity the rest of this document only specifically mentions the former.
部分 QP 代码实际按照 RFC 3339 标准解析日期时间字符串,该标准基本是 ISO 8601 的子集。通常无需关注两者差异——几乎任一格式有效的字符串在另一标准中同样有效。ISO-8601 是比 RFC 3339 更为广泛认知的标准;为便于理解,本文后续仅明确提及前者。
Relative datetimes represent a point in time relative to each moment the query is ran, such as today or 1 year ago. A relative datetime is expressed as one of the
following forms:
相对日期时间表示相对于查询执行时刻的时间点,例如"今天"或"1 年前"。其表达形式包括以下之一:
- [
:relative-datetime
:current
] - [
:relative-datetime
ninteger-literal
unitsrelative-datetime-unit
]
[:relative-datetime
ninteger-literal
单位relative-datetime-unit
]
The first form represents the moment in time a query is ran. It is analogous to the SQL NOW() function.
第一种形式表示查询运行时的当前时刻,类似于 SQL 中的 NOW()函数。
The second form represents a moment in time n
units
from the moment the query is ran. For example, you could express 31 days ago as follows:
第二种形式表示相对于查询运行时时刻的 n
units
时间点。例如,你可以用如下方式表示 31 天前:
[:relative-datetime -31 :day]
Valid units for a relative datetime form are:
相对时间表单的有效单位包括:
:minute
:hour
:day
:week
:month
:quarter
:year
Subtraction and addition of units are done simply; in other words, -2 :day
is the same as -48 :hour
or -2880 :minute
.
单位加减运算简单直接;换言之, -2 :day
等同于 -48 :hour
或 -2880 :minute
。
These units are used to specify the desired bucketing of fields with the datetime-field
form. Bucketing, as used in this document,
refers collectively to both datetime truncation and extraction operations. Truncation operations are flooring operations that convert values to a lower resolution (such as
the start of the current day or of the current month); extraction operations extract some element of a date (such as the month) without regard to other elements.
这些单位用于通过 datetime-field
形式指定字段所需的分桶方式。本文档中所述的分桶,统指日期时间截断和提取操作。截断操作属于向下取整操作,将值转换为更低精度(例如当前日或当前月的起始点);提取操作则在不考虑其他元素的情况下提取日期的某个部分(如月份)。
Truncation is analogous to date_trunc
in Postgres, while extraction is analogous to
extract
.
截断操作类似于 Postgres 中的 date_trunc
,而提取操作则类似于 extract
。
Token 标记 | Operation 操作 | Example 示例 | Description 描述 |
---|---|---|---|
default | N/A 不适用 | 2016-02-01 21:59:20 | No bucketing should take place -- use the default resolution of the column in question, such as seconds or milliseconds. 不应进行分桶处理——使用该列的默认分辨率,例如秒或毫秒。 |
minute | truncation 截断 | 2016-02-01 21:59 | Truncate to the beginning of the current minute. 截断至当前分钟的开始时刻。 |
minute-of-hour | extraction 提取 | 59 | The minute of the the current hour. Ranges from 0 - 59. 当前小时中的分钟数。范围从 0 - 59 开始。 |
hour | truncation 截断 | 2016-02-01 21:00 | Truncate to beginning of the current hour. 截断至当前小时的开始时刻。 |
hour-of-day | extraction 提取 | 21 | Extract the current hour. Ranges from 0 - 23. 提取当前小时数。范围从 0 - 23 开始。 |
day | truncation 截断 | 2016-01-01 | Truncate to the beginning of the current day. 截取至当前天的起始时间。 |
day-of-week | extraction 提取 | 2 [Monday] | Day of week, with Sunday as the first day of the week (1) to Saturday(7) 星期几,以周日为一周的第一天( 1 )至周六( 7 ) |
day-of-month | extraction 提取 | 1 | The calendar date. 日历日期。 |
day-of-year | extraction 提取 | 32 | Day of the year, with January 1st the first day (1). 一年中的第几天,1 月 1 日为第一天( 1 )。 |
week | truncation 截断 | 2016-01-31 | Truncate to the beginning of the week (Sunday). 截断至周起始(周日)。 |
week-of-year | extraction 提取 | 6 | Week of the year with the week containing January 1st as week 1. 一年中的周数,包含 1 月 1 日的周为第 1 周。 |
month | truncation 截断 | 2016-02 | Truncate to the first day of the month. 截断至当月的第一天。 |
month-of-year | extraction 提取 | 2 [Feb] | Extract the month of the year as you'd see on a calendar (e.g. January is 01). 提取日历上显示的月份(例如一月对应 01 )。 |
quarter | truncation 截断 | 2016-01-01 | Truncate to the beginning of the quarter. Quarters start on Jan 1st, April 1st, July 1st, and October 1st of each year. 截取至季度起始日。季度开始于每年的 1 月 1 日、4 月 1 日、7 月 1 日和 10 月 1 日。 |
quarter-of-year | extraction 提取 | 1 | The number of the current quarter (1-4), with the quarter beginning Jan 1st as 1. 当前季度数( 1-4 ),其中 1 月 1 日开始的季度为 1 。 |
year | extraction 提取 | 2016 | Extract the current year. 提取当前年份。 |
An aggregation
is analogous to SQL aggregation functions like COUNT()
or SUM()
. When specified, it consists of a single clause, e.g.
aggregation
类似于 SQL 聚合函数如 COUNT()
或 SUM()
。指定时,它由单个子句组成,例如
{:aggregation [:sum [:field-id 100]]}
The following are valid aggregation clauses:
以下是有效的聚合子句:
Aggregations are often used in combination with breakouts
, but this is not required. When an aggregation is used without a breakout
, the query will
return a single row with a single value. When one or more breakout
fields is specified, it will return a row for each distinct value.
聚合常与 breakouts
结合使用,但并非必须。当聚合不带 breakout
时,查询将返回单行单值。当指定一个或多个 breakout
字段时,将返回每个唯一值对应的行。
Suppose we have a table of venues, with a price
column representing the number of $$$
we'd see on a Yelp-like app.
A count
aggregation with no breakout returns the total number of rows, while breaking out by price
returns the count of venues for each given price:
假设我们有一个场馆表,其中 price
列表示类似 Yelp 应用中可见的 $$$
数量。不带分组的 count
聚合返回总行数,而按 price
分组则返回每个价格区间的场馆计数:
{:aggregation [:count]}
-> [[100]]
{:aggregation [:count], :breakout [<price>]}
-> [[1 22]
[2 59]
[3 13]
[4 6]]
In future iterations of the QL (probably MBQL '98SE
or MBQL ME
), multiple aggregation clauses will also be allowed, but for now, only a single aggregation can be specified.
在 QL 的未来迭代版本中(可能是 MBQL '98SE
或 MBQL ME
),将允许多个聚合子句,但目前仅支持指定单一聚合。
Older versions of MBQL supported an additional aggregation type, rows
. In MBQL '95, this was made the default behavior, and was ignored by the Query Processor;
in MBQL '98, it is considered deprecated, and the QP will issue a warning when it is encountered.
旧版 MBQL 支持额外的聚合类型 rows
。在 MBQL '95 中,这成为默认行为并被查询处理器忽略;在 MBQL '98 中,该类型被视为已弃用,查询处理器遇到时会发出警告。
Return the average value of a numeric field.
返回数值字段的平均值。
- [
:avg
concrete-field
]
Return the count of rows in the source-table
. This takes a single optional arg, a field:
返回 source-table
中的行数。此操作接受一个可选参数,即字段:
- [
:count
] - [
:count
concrete-field
]
If field is specified, the query retuns the count of rows where field has a non-nil
value.
若指定字段,则查询返回该字段具有非 nil
值的行数。
count
is analogous to SQL's COUNT
. With no args, it behaves like COUNT(*)
; with field,
it is analogous to COUNT(column_name)
.
count
类似于 SQL 中的 COUNT
。无参数时,其行为类似于 COUNT(*)
;带字段参数时,则类似于 COUNT(column_name)
。
Returns the count of rows for which filter-clause
returns true.
返回 filter-clause
结果为真的行数。
- [
:count-where
filter-clause
]
Return the cumulative count of a numeric field. This is useful for doing things like total number of sales over time -- charts that go "up and to the right". 📈
返回数值字段的累计计数。这对于实现诸如随时间累计销售总量等功能非常有用——图表呈现“向右上方增长”的趋势。📈
- [
:cum-count
concrete-field
]
This aggregation is implemented entirely by middleware on the backend, so individual drivers don't need to worry about handling it. Note that it doesn't make particular sense
to use this aggregation without any breakout columns; in that case, cum-count
will be treated the same as count
该聚合功能完全由后端中间件实现,因此各驱动程序无需单独处理。请注意,在没有分组列的情况下使用此聚合并无实际意义;此时 cum-count
将被视为与 count
相同
Return the cumulative sum of a numeric field.
返回数值字段的累计总和。
- [
:cum-sum
concrete-field
]
This aggregation is implemented entirely by middleware on the backend, so individual drivers don't need to worry about handling it. Note that it doesn't make particular sense
to use this aggregation without any breakout columns; in that case, cum-sum
will be treated the same as sum
.
该聚合功能完全由后端中间件实现,因此各驱动程序无需单独处理。请注意,若未指定任何分组列,使用此聚合并无实际意义;此时 cum-sum
将被视同 sum
处理。
Return the number of distinct values of a field.
返回某字段的不同值数量。
- [
:distinct
concrete-field
]
This is analogous to COUNT(DISTINCT column)
in SQL.
这类似于 SQL 中的 COUNT(DISTINCT column)
。
Return the standard deviation of values of a numeric field.
返回数值字段值的标准差。
- [
:stddev
concrete-field
]
This is not supported by all drivers; drivers that support it declare the feature standard-deviation-aggregations
.
并非所有驱动都支持此功能;支持该功能的驱动会声明特性 standard-deviation-aggregations
。
Return the sum of all values of a numeric field. This is analogous to SQL SUM(column)
.
返回数值字段所有值的总和。这类似于 SQL 中的 SUM(column)
。
- [
:sum
concrete-field
]
Depending on the database and underlying column type, the results will either by an integer or a floating-point.
根据数据库及底层列类型的不同,结果可能是整数或浮点数。
Returns the sum of all values of concrete-field
in rows for which filter-clause
returns true.
返回满足 filter-clause
条件为真的行中 concrete-field
字段所有值的总和。
- [
:count-where
concrete-field
filter-clause
]
Return the minimum value of a numeric or datetime field. This is analogous to SQL MIN(column)
.
返回数值或日期时间字段的最小值。这类似于 SQL 中的 MIN(column)
。
- [
:min
concrete-field
]
Return the maximum value of a numeric or datetime field. This is analogous to SQL MAX(column)
.
返回数值或日期时间字段的最大值。这类似于 SQL 中的 MAX(column)
。
- [
:min
concrete-field
]
Returns the share of rows for which filter-clause
returns true.
返回 filter-clause
结果为真的行所占比例。
- [
:share
filter-clause
]
- {
:breakout
[&concrete-field
]}
Breakouts group a query by the distinct values of one or more columns. When used in conjunction with an aggregation, they are
analogous to GROUP BY
statements in SQL. Unlike SQL, however, breakout clauses can be used
without aggregate functions; in this case, they return the distinct values of a field, analogous to SQL's SELECT DISTINCT
.
分组(Breakouts)通过一个或多个列的不同值对查询进行分组。与聚合函数结合使用时,它们类似于 SQL 中的 GROUP BY
语句。但与 SQL 不同的是,分组子句可不搭配聚合函数使用;此时它们返回字段的唯一值,类似于 SQL 的 SELECT DISTINCT
。
;; Get distinct values of Field 10
;; Equivalent SQL: SELECT DISTINCT field_10
{:breakout [[:field-id 10]]}
;; Count of all rows broken out by (i.e., grouped by) distinct values of Field 10
;; Equivalent SQL: SELECT COUNT(*) GROUP BY field_10
{:aggregation [:count]
:breakout [[:field-id 10]]}
- {
:expressions
{expression-name
expression
}}
Expressions are used to add new columns to the results of a query; for each row, their values are derived from calculations involving the values of other columns and/or numeric constants.
表达式用于向查询结果中添加新列;对于每一行,其值由涉及其他列和/或数值常量的计算得出。
Suppose we have a table called sales
with data like:
假设我们有一个名为 sales
的表,其数据如下:
subtotal 小计 | total 总计 |
---|---|
10.00 | 11.00 |
50.00 | 52.50 |
40.00 | 41.00 |
Now suppose we'd like to see what the sales tax for each sale was. We can add an expression to derive sales_tax
from total - subtotal
. The MBQL form for doing so looks like:
现在假设我们想查看每笔销售的销售税是多少。我们可以添加一个表达式,从 total - subtotal
中推导出 sales_tax
。其 MBQL 形式如下:
{:expressions {:sales_tax [:- [:field-id <id of total>]
[:field-id <id of subtotal>]]}}
The generated SQL will look something like:
生成的 SQL 语句大致如下:
SELECT subtotal, total, (total - subtotal) AS sales_tax
FROM sales
The results of a query including this :expressions
clause would look like:
包含此 :expressions
子句的查询结果将呈现为:
subtotal 小计 | total 总计 | sales_tax 销售税 |
---|---|---|
10.00 | 11.00 | 1.00 |
50.00 | 52.50 | 2.50 |
40.00 | 41.00 | 1.00 |
Expressions are not available for all drivers; drivers that offer them advertise the :expressions
feature.
并非所有数据库驱动都支持表达式功能;提供该功能的驱动会标明支持 :expressions
特性。
- [
+
arg1concrete field
|number literal
|expression
arg2concrete field
|number literal
|expression
]
[+
参数 1concrete field
|number literal
|expression
参数 2concrete field
|number literal
|expression
] - [
-
arg1concrete field
|number literal
|expression
arg2concrete field
|number literal
|expression
]
[-
参数 1concrete field
|number literal
|expression
参数 2concrete field
|number literal
|expression
] - [
/
arg1concrete field
|number literal
|expression
arg2concrete field
|number literal
|expression
]
[/
参数 1concrete field
|number literal
|expression
参数 2concrete field
|number literal
|expression
] - [
*
arg1concrete field
|number literal
|expression
arg2concrete field
|number literal
|expression
]
[*
参数 1concrete field
|number literal
|expression
参数 2concrete field
|number literal
|expression
]
The behavior of the various expression operators above should be self-explanatory. Nested expressions may be used as one or both arguments, as may numeric literals; note, however,
that field references should refer to a field with numeric values, or the query will fail (it doesn't make sense to generate to try to divide a string, for example). Field references also
may not be expression references, which are otherwise allowed wherever a concrete field is specified; this restriction reflects inherent limitations in underlying database
systems.
上述各类表达式运算符的行为应当不言自明。嵌套表达式可作为单参数或双参数使用,数字字面量亦然;但需注意,字段引用应指向具有数值的字段,否则查询将失败(例如尝试对字符串进行除法运算显然不合理)。字段引用也不得为表达式引用——尽管在其他需要具体字段的场景中允许使用表达式引用;此限制反映了底层数据库系统的固有局限性。
You can refer to the results of expressions in other clauses. See expression references for more details.
您可以在其他子句中引用表达式的结果。更多详情请参阅表达式引用部分。
More operators may be added in the future if expressions prove popular. Possibilities include string operators, such as concatenation, uppercasing, lowercasing, etc.
如果表达式功能受到欢迎,未来可能会添加更多运算符,例如字符串操作符(连接、大写转换、小写转换等)。
- {
:fields
[&field
]}
Conceptually similar to a SQL SELECT
statement; specifies which Fields
the query should return. When specified, fields
is a sequence of one or
more field references:
概念上类似于 SQL 中的 SELECT
语句;用于指定查询应返回的 Fields
。当指定时, fields
是一个或多个字段引用的序列:
{:fields [[:field-id 100] [:field-id 200]]}
- {
:filter
filter-clause
}
filter
is used to "filter" the the rows returned by a query, and is analogous to the SQL WHERE
clause.
filter
用于“筛选”查询返回的行,类似于 SQL 中的 WHERE
子句。
A filter clause is one of the following:
过滤子句可以是以下之一:
and
or
not
=
!=
<
>
<=
>=
is-null
not-null
between
inside
starts-with
contains
does-not-contain
ends-with
time-interval
- [
:and
& clausesfilter-clause
]
[:and
& 子句filter-clause
]
Compound filter clause: return rows that satisfy all clauses. clauses can be any valid filter clause,
including nested compound :and
or :or
clauses.
复合过滤条件:返回满足所有子条件的行。子条件可以是任何有效的过滤条件,包括嵌套的复合 :and
或 :or
条件。
If only a single clause is passed, it is treated as if there were no surrounding :and
clause:
如果仅传入单个条件,则视为没有外围的 :and
条件包裹:
[:and [:= <field> <value>]] -> [:= <field> <value>]
- [
:and
& clausesfilter-clause
]
[:and
& 子条件filter-clause
]
Compound filter clause: return rows that satisfy one or more clauses. clauses can be any valid filter clause,
including nested compound :and
or :or
clauses.
复合过滤条件:返回满足一个或多个条件的行。条件可以是任何有效的过滤子句,包括嵌套的复合 :and
或 :or
子句。
If only a single clause is passed, it is treated as if there were no surrounding :or
clause:
如果仅传入单个条件,则视为没有外层 :or
条件的情况:
[:or [:= <field> <value>]] -> [:= <field> <value>]
- [
:not
filter-clause
]
The not
filter returns results that do not match its argument (i.e., it logically negates its argument). not
works exactly like SQL's NOT
or Clojure's not
function.
not
过滤器返回不匹配其参数的结果(即逻辑上对其参数取反)。其功能与 SQL 中的 NOT
或 Clojure 的 not
函数完全相同。
In most cases a not
form is automatically translated to a simpler, logically equivalent form:
在大多数情况下, not
形式会被自动转换为更简单且逻辑等效的形式:
(not (and <x> <y>)) -> (or (not <x>) (not <y>))
(not (not <x>)) -> <x>
(not (= <x> <y>) -> (!= <x> <y>)
The negation of some filters (like starts-with
) have no logical equivalent in MBQL, so drivers still need to know how to handle not
.
某些过滤器(如 starts-with
)的否定形式在 MBQL 中没有逻辑等价物,因此驱动程序仍需了解如何处理 not
。
- [
:=
concrete-field
value
&value
]
[:=
concrete-field
value
和value
]
With a single value
, return rows where the values of field match value. With multiple values, this behaves like a
SQL IN
operator, and returns rows that match any of the values.
当使用单个 value
时,返回字段值匹配指定值的行。对于多个值,其行为类似于 SQL 中的 IN
操作符,返回匹配任意值的行。
[:= <field> 100] ; return rows where <field> == 100
[:= <field> 100 200] ; return rows where <field> == 100 *or* field == 200
Internally, =
filters with multiple values are translated to compound statements, e.g.:
在内部,带有多个值的 =
过滤器会被转换为复合语句,例如:
[:= <field> 100 200]
-> [:or [:= <field> 100]
[:= <field> 200]]
- [
:!=
concrete-field
value
&value
]
[:!=
concrete-field
value
和value
]
With a single value
, return rows where the values of field are not equal to value. With multiple values, this behaves like a
SQL NOT IN
operator, and returns rows that match none of the values.
当使用单个 value
时,返回字段值不等于指定值的行。对于多个值,其行为类似于 SQL 中的 NOT IN
操作符,返回不匹配任何值的行。
[:!= <field> 100] ; return rows where <field> != 100
[:!= <field> 100 200] ; return rows where <field> != 100 *and* field != 200
Internally, !=
filters with multiple values are translated to compound statements, e.g.:
在内部,带有多个值的 !=
过滤器会被转换为复合语句,例如:
[:!= <field> 100 200]
-> [:and [:!= <field> 100]
[:!= <field> 200]]
- [
:<
fieldconcrete-field
valueorderable-value
]
[:<
字段concrete-field
值orderable-value
]
Return results where field is less than value. field and value must be orderable, i.e. numbers or a datetimes.
返回字段值小于指定值的结果。字段和值必须可排序,例如数字或日期时间类型。
- [
:>
fieldconcrete-field
valueorderable-value
]
[:>
字段concrete-field
值orderable-value
]
Return results where field is greater than value. field and value must be orderable, i.e. numbers or a datetimes.
返回字段值大于指定值的记录。字段和值必须可排序,例如数字或日期时间类型。
- [
:<=
fieldconcrete-field
valueorderable-value
]
[:<=
字段concrete-field
值orderable-value
]
Return results where field is less than or equal to value. field and value must be orderable, i.e. numbers or a
datetimes.
返回字段小于或等于值的记录。字段和值必须可排序,例如数字或日期时间。
- [
:>=
fieldconcrete-field
valueorderable-value
]
[:>=
字段concrete-field
值orderable-value
]
Return results where field is greater than or equal to value. field and value must be orderable, i.e. numbers or a
datetimes.
返回字段值大于或等于指定值的结果。字段和值必须可排序,例如数字或日期时间类型。
- [
:is-null
fieldconcrete-field
]
[:is-null
字段concrete-field
]
Return results where field is nil
. Convenience for [:= <field> nil]
. This filter isn't particularly useful in my opinion and may be deprecated in the future; prefer using =
instead.
返回字段值为 nil
的结果。这是 [:= <field> nil]
的便捷写法。个人认为此筛选条件并不实用,未来可能弃用;建议优先使用 =
。
- [
:not-null
fieldconcrete-field
]
[ 字段concrete-field
]
Return results where field is non-nil
. Convenience for [:!= <field> nil]
. This filter isn't particularly useful in my opinion and may be deprecated in the future; prefer using !=
instead.
返回字段非 nil
的结果。作为 [:!= <field> nil]
的便捷用法。个人认为此筛选条件并不实用,未来可能弃用;建议改用 !=
。
- [
:between
fieldconcrete-field
minorderable-value
maxorderable-value
]
[:between
字段concrete-field
最小值orderable-value
最大值orderable-value
]
Analogous to the SQL BETWEEN
operator.
Return results where field is between min and max, inclusive. field, min, and max must be orderable
, i.e. numbers
or datetimes.
类似于 SQL 中的 BETWEEN
操作符。返回字段值介于最小值和最大值之间(包含边界)的结果。字段、最小值和最大值必须是 orderable
,即数字或日期时间类型。
between
is conceptually similar to
between
在概念上类似于
[:and [:>= <field> <min>]
[:<= <field> <max>]]
and can be implemented by drivers with no corresponding native concept as such.
对于没有对应原生概念的驱动程序,可以按此方式实现。
inside
is used for doing geo bounding boxes:
inside
用于执行地理边界框查询:
- [
:inside
latconcrete-field
lonconcrete-field
lat-maxnumeric-literal
lon-minnumeric-literal
lat-minnumeric-literal
lon-maxnumeric-literal
]
[:inside
纬度concrete-field
经度concrete-field
纬度最大值numeric-literal
经度最小值numeric-literal
纬度最小值numeric-literal
经度最大值numeric-literal
]
Drivers don't need to specifically handle inside
filters. Internally, they're automatically translated to to a pair of between
clauses:
驱动程序无需专门处理 inside
筛选条件。在内部,它们会自动转换为一对 between
子句:
[:inside <lat> <lon> <lat-max> <lon-min> <lat-min> <lon-max>]
-> [:and [:between <lat-field> <lat-min> <lat-max>]
[:between <lon-field> <lon-min> <lon-max>]]
- [
:starts-with
fieldconcrete-field
valuestring-literal
]
[:starts-with
字段concrete-field
值string-literal
]
The starts-with filter restricts results to those where values of string field start with the substring value. Analogous to SQL <field> LIKE '<value>%'
.
开头匹配筛选器将结果限制为字符串字段值以该子字符串开头的记录。类似于 SQL 中的 <field> LIKE '<value>%'
。
- [
:contains
fieldconcrete-field
valuestring-literal
]
[:contains
字段concrete-field
值string-literal
]
The contains filter restricts results to those where values of string field contain the substring value. Analogous to SQL <field> LIKE '%<value>%'
.
包含过滤器将结果限制为字符串字段的值包含子字符串值的记录。类似于 SQL 中的 <field> LIKE '%<value>%'
。
e.g. 例如
;; return results where values of string Field 10 contain the substring "BBQ"
{:filter [:contains [:field-id 10] "BBQ"]}
- [
:does-not-contain
fieldconcrete-field
valuestring-literal
]
[:does-not-contain
字段concrete-field
值string-literal
]
The does not contain filter restricts results to those where values of string field do not contain the substring value. Analogous to SQL NOT (<field> LIKE '%<value>%')
.
"不包含"筛选器将结果限制为字符串字段值不包含该子字符串值的记录。功能等同于 SQL 中的 NOT (<field> LIKE '%<value>%')
。
This filter is just syntactic sugar for wrapping a contains
filter in a not
form, and is implemented as such on the backend (i.e., individual drivers don't need to worry about handling this clause directly).
该筛选器仅为将 contains
筛选器包装为 not
形式的语法糖,后端实际也如此实现(即各驱动无需直接处理该子句)。
[:does-not-contain <field> <string>] -> [:not [:contains <field> <string>]]
Prefer using the equivalent not
/contains
form instead whenever possible; this form will likely be deprecated at some point in the future. It is provided for the time being solely as a convenience to the Metabase frontend until support for not
is implemented.
请尽可能使用等效的 not
/ contains
形式;当前形式未来可能被弃用。目前仅为 Metabase 前端在实现 not
支持前的临时便利方案。
- [
:ends-with
fieldconcrete-field
valuestring-literal
]
[:ends-with
字段concrete-field
值string-literal
]
The ends-with filter restricts results to those where values of string field end with the substring value. Analogous to SQL <field> LIKE '%<value>'
.
ends-with 过滤器将结果限制为字符串字段值以指定子字符串结尾的记录。功能类似于 SQL 中的 <field> LIKE '%<value>'
。
- [
:time-interval
fieldconcrete-field
n:current
|:last
|:next
|integer-literal
unitrelative-datetime-unit
]
[:time-interval
字段concrete-field
数值:current
|:last
|:next
|integer-literal
单位relative-datetime-unit
]
time-interval
is syntactic sugar for restricting results to ones where datetime field matches a certain datetime or range of datetimes.
" time-interval
"是语法糖,用于将结果限制为日期时间字段匹配特定日期时间或日期时间范围的记录。
;; Assuming today is Feb 8th, 2016...
;; return results where <field> is any time on Feb 8th 2016
[:time-interval <field> :current :day]
;; return results where <field> is during the previous month (any date in Jan 2016)
[:time-interval <field> :last :month]
;; Return results in the last 30 days, excluding the current day (Jan 8th <-> Feb 7th)
[:time-interval <field> -30 :day]
:current
, :last
, and :next
are allowed as values on n for readability purposes and have the same effect as as the integers 0
, -1
, and 1
, respectively.
出于可读性考虑,允许使用" :current
"、" :last
"和" :next
"作为 n 的值,其效果分别等同于整数" 0
"、" -1
"和" 1
"。
time-interval
is translated into a =
or between
filter, depending on the value of n.
" time-interval
"会根据 n 的值被转换为" =
"或" between
"过滤器。
TODO Examples of translation to =
/between
clauses
待办事项:转换为" =
"或" between
"子句的示例
- {
:limit
ninteger-literal
}
limit
specifies the maximum number of rows that should be returned by the query. It is analogous to a SQL LIMIT
clause (or SELECT TOP
in MS SQL).
limit
指定查询应返回的最大行数,类似于 SQL 中的 LIMIT
子句(或 MS SQL 中的 SELECT TOP
)。
ex. 例如
{:source-table 20
:limit 10} ; return the first 10 rows of Table 20
n must be greater than zero.
n 必须大于零。
- {
:order-by
[&order-by-clause
]}
You can specify how results should be sorted with order-by
. When specified, it consists of one or more order by clauses, each of which specify a field
and sort direction (ascending or descending).
您可以通过 order-by
指定结果的排序方式。当指定时,它包含一个或多个排序子句,每个子句指定一个 field
和排序方向(升序或降序)。
order-by
is analogous to SQL's ORDER BY
. Like SQL, you can specify multiple columns to sort by; sorting is done by the first column,
then by the second in the case of duplicate values for the first, and so forth.
order-by
类似于 SQL 中的 ORDER BY
。与 SQL 一样,您可以指定多个排序列;首先按第一列排序,在第一列值相同的情况下按第二列排序,以此类推。
Ex. 示例:
;; Sort by values of Field 10 in ascending order
{:order-by [[:asc [:field-id 10]]]}
;; Sort by :count in descending order
{:aggregation [:count]
:breakout [[:field-id 10]]
:order-by [[:desc [:aggregate-field 0]]]}
- [direction
:asc
|:desc
fieldfield
]
[方向:asc
|:desc
字段field
]
- {
:source-table
integer-literal
}
The source table clause specifies the ID of the Metabase Table that should
be queried. A Metabase Table is the internal object used to represent a physical table/collection/datasource/etc and store metadata about it.
源表子句指定应查询的 Metabase 表的 ID。Metabase 表是用于表示物理表/集合/数据源等的内部对象,并存储其相关元数据。
This clause is roughly equivalent in purpose to a simple SQL FROM
clause (i.e., on with no joins or sub-selects).
Local Field References are references to Metabase Fields that belong to this Table. To include data from other Tables (and perform the equivalent of a SQL JOIN
) you
can include Foreign Field References somewhere in your query.
该子句在功能上大致等同于简单的 SQL FROM
子句(即不带连接或子查询的语句)。本地字段引用指向属于此表的 Metabase 字段。若需包含其他表的数据(实现类似 SQL JOIN
的功能),可在查询中某处添加外键字段引用。
- [
:named
aggregation-clause
] - {
:source-query
query
} :binning-strategy
:field-literal
- joins +
:joined-field
连接 +:joined-field
- Backend 后端
- Metabase Developer Reference
Metabase 开发者参考指南- Developer Pro Tips 开发者专业技巧
- Metabase Clojure Style Guide
Metabase Clojure 风格指南 - Metabase Glossary Metabase 术语表
- Metabase Plugin Manifest Reference
Metabase 插件清单参考 - Setting up email in development
开发环境中的电子邮件设置 - Understanding Timezones in Metabase
理解 Metabase 中的时区设置 - Using the REST API
使用 REST API - IntelliJ/Cursive IDE Setup
IntelliJ/Cursive IDE 设置
- Product Management 产品管理
- QA and Testing 质量保证与测试
-
Writing A Driver
编写驱动程序 - Driver Notices 驱动程序通知
- REST API Notices REST API 公告
- Writing style guide for documentation and blog posts (WIP)
文档与博客文章的写作风格指南(进行中)