Postgresql 中文操作指南
8.14. JSON Types #
JSON 数据类型用于存储 JSON(JavaScript 对象表示法)数据,如 RFC 7159中所规定。此类数据也可以存储为 text,但 JSON 数据类型的优势在于,它强制要求每个存储的值都根据 JSON 规则有效。还有各种可用于存储在这些数据类型中的数据的 JSON 特定函数和运算符;请参见 Section 9.16。
JSON data types are for storing JSON (JavaScript Object Notation) data, as specified in RFC 7159. Such data can also be stored as text, but the JSON data types have the advantage of enforcing that each stored value is valid according to the JSON rules. There are also assorted JSON-specific functions and operators available for data stored in these data types; see Section 9.16.
PostgreSQL 提供了两种用于存储 JSON 数据的类型:json_和 _jsonb。为了实现这些数据类型的查询机制,PostgreSQL 还提供了 _jsonpath_数据类型, Section 8.14.7 中对其进行了说明。
PostgreSQL offers two types for storing JSON data: json and jsonb. To implement efficient query mechanisms for these data types, PostgreSQL also provides the jsonpath data type described in Section 8.14.7.
json 和 jsonb 数据类型接受 almost 相同的输入值集。主要的实际区别在于效率。json 数据类型存储输入文本的精确副本,处理函数在每次执行时都必须重新解析;而 jsonb 数据存储在分解的二进制格式中,由于增加了转换开销,其输入速度略慢,但处理速度明显更快,因为无需重新解析。jsonb 还支持索引,这可能是一个显着的优势。
The json and jsonb data types accept almost identical sets of values as input. The major practical difference is one of efficiency. The json data type stores an exact copy of the input text, which processing functions must reparse on each execution; while jsonb data is stored in a decomposed binary format that makes it slightly slower to input due to added conversion overhead, but significantly faster to process, since no reparsing is needed. jsonb also supports indexing, which can be a significant advantage.
由于 json 类型存储输入文本的精确副本,因此它将保留标记之间的语义无关空格,以及 JSON 对象中键的顺序。此外,如果值中的 JSON 对象包含相同的键不止一次,则保留所有键/值对。(处理函数将最后一个值视为有效值。)相比之下,jsonb 不保留空格,不保留对象键的顺序,也不保留重复的对象键。如果在输入中指定了重复的键,则只保留最后一个值。
Because the json type stores an exact copy of the input text, it will preserve semantically-insignificant white space between tokens, as well as the order of keys within JSON objects. Also, if a JSON object within the value contains the same key more than once, all the key/value pairs are kept. (The processing functions consider the last value as the operative one.) By contrast, jsonb does not preserve white space, does not preserve the order of object keys, and does not keep duplicate object keys. If duplicate keys are specified in the input, only the last value is kept.
一般而言,除非存在非常特殊的需求(例如对象键排序的旧假设),大多数应用程序都应更喜欢将 JSON 数据存储为 jsonb。
In general, most applications should prefer to store JSON data as jsonb, unless there are quite specialized needs, such as legacy assumptions about ordering of object keys.
RFC 7159 规定 JSON 字符串应编码为 UTF8。因此,如果数据库编码不是 UTF8,则 JSON 类型无法严格符合 JSON 规范。尝试直接包含无法在数据库编码中表示的字符将失败;相反,允许包含可以在数据库编码中表示但不能在 UTF8 中表示的字符。
RFC 7159 specifies that JSON strings should be encoded in UTF8. It is therefore not possible for the JSON types to conform rigidly to the JSON specification unless the database encoding is UTF8. Attempts to directly include characters that cannot be represented in the database encoding will fail; conversely, characters that can be represented in the database encoding but not in UTF8 will be allowed.
RFC 7159允许JSON字符串包含由 \u_XXXX_ 表示的Unicode转义序列。在 json 类型的输入函数中,允许Unicode转义,而不管数据库编码如何,而且仅检查其语法正确性(即 \u 后面是否有四个十六进制数字)。但是, jsonb 的输入函数更为严格:它禁止对不能用数据库编码表示的字符进行Unicode转义。 jsonb 类型也拒绝 \u0000 (因为它不能用PostgreSQL的 text 类型表示),并且它坚持正确使用Unicode代理对来指定Unicode基本多语言平面的外部字符。有效的Unicode转义转换为等效的单个字符以进行存储;这包括将代理对折叠成单个字符。
RFC 7159 permits JSON strings to contain Unicode escape sequences denoted by \u_XXXX_. In the input function for the json type, Unicode escapes are allowed regardless of the database encoding, and are checked only for syntactic correctness (that is, that four hex digits follow \u). However, the input function for jsonb is stricter: it disallows Unicode escapes for characters that cannot be represented in the database encoding. The jsonb type also rejects \u0000 (because that cannot be represented in PostgreSQL’s text type), and it insists that any use of Unicode surrogate pairs to designate characters outside the Unicode Basic Multilingual Plane be correct. Valid Unicode escapes are converted to the equivalent single character for storage; this includes folding surrogate pairs into a single character.
Note
Section 9.16 中描述的许多 JSON 处理函数将把 Unicode 转义转换为常规字符,因此即使它们的输入类型为 json 而不是 jsonb,也仍然会引发刚才描述的同类错误。尽管这允许在不支持已表示字符的数据库编码中简单存储(不做处理)JSON Unicode 转义序列,但 json 输入函数不执行这些检查的事实可被视作一种历史遗留问题。
Many of the JSON processing functions described in Section 9.16 will convert Unicode escapes to regular characters, and will therefore throw the same types of errors just described even if their input is of type json not jsonb. The fact that the json input function does not make these checks may be considered a historical artifact, although it does allow for simple storage (without processing) of JSON Unicode escapes in a database encoding that does not support the represented characters.
在将文本 JSON 输入转换为 _jsonb_时,RFC 7159 中描述的基本类型会有效映射到本机 PostgreSQL 类型,如 Table 8.23所示。因此,对于构成有效 _jsonb_数据的额外限制很小,而这些限制不适用于 _json_类型,也不适用于抽象的 JSON,与基础数据类型可以表示的内容限制相对应。值得注意的是,_jsonb_将拒绝超出 PostgreSQL _numeric_数据类型的范围的数字,而 _json_不会。RFC 7159 允许此类实现定义的限制。然而,在实践中,此类问题更有可能发生在其他实现中,因为将 JSON 的 _number_基本类型表示为 IEEE 754 双精度浮点数很常见(RFC 7159 明确预期并允许了这一点)。将 JSON 作为与此类系统互换的格式使用时,应该考虑与 PostgreSQL 最初存储的数据相比丢失数字精度的风险。
When converting textual JSON input into jsonb, the primitive types described by RFC 7159 are effectively mapped onto native PostgreSQL types, as shown in Table 8.23. Therefore, there are some minor additional constraints on what constitutes valid jsonb data that do not apply to the json type, nor to JSON in the abstract, corresponding to limits on what can be represented by the underlying data type. Notably, jsonb will reject numbers that are outside the range of the PostgreSQL numeric data type, while json will not. Such implementation-defined restrictions are permitted by RFC 7159. However, in practice such problems are far more likely to occur in other implementations, as it is common to represent JSON’s number primitive type as IEEE 754 double precision floating point (which RFC 7159 explicitly anticipates and allows for). When using JSON as an interchange format with such systems, the danger of losing numeric precision compared to data originally stored by PostgreSQL should be considered.
相反,如表中所述,对于 JSON 基本类型的输入格式有一些较小的限制,这不适用于相应的 PostgreSQL 类型。
Conversely, as noted in the table there are some minor restrictions on the input format of JSON primitive types that do not apply to the corresponding PostgreSQL types.
Table 8.23. JSON Primitive Types and Corresponding PostgreSQL Types
Table 8.23. JSON Primitive Types and Corresponding PostgreSQL Types
JSON primitive type |
PostgreSQL type |
Notes |
string |
text |
\u0000 is disallowed, as are Unicode escapes representing characters not available in the database encoding |
number |
numeric |
NaN and infinity values are disallowed |
boolean |
boolean |
Only lowercase true and false spellings are accepted |
null |
(none) |
SQL NULL is a different concept |
8.14.1. JSON Input and Output Syntax #
JSON 数据类型的输入/输出语法如 RFC 7159 中所指定。
The input/output syntax for the JSON data types is as specified in RFC 7159.
以下是所有有效的 json(或 jsonb)表达式:
The following are all valid json (or jsonb) expressions:
-- Simple scalar/primitive value
-- Primitive values can be numbers, quoted strings, true, false, or null
SELECT '5'::json;
-- Array of zero or more elements (elements need not be of same type)
SELECT '[1, 2, "foo", null]'::json;
-- Object containing pairs of keys and values
-- Note that object keys must always be quoted strings
SELECT '{"bar": "baz", "balance": 7.77, "active": false}'::json;
-- Arrays and objects can be nested arbitrarily
SELECT '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}'::json;
如前所述,当输入一个 JSON 值然后在不进行任何其他处理的情况下打印该值时,json 输出与输入相同的文本,而 jsonb 则不会保留空格等语义无关的详细信息。例如,请注意此处差异:
As previously stated, when a JSON value is input and then printed without any additional processing, json outputs the same text that was input, while jsonb does not preserve semantically-insignificant details such as whitespace. For example, note the differences here:
SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::json;
json
-------------------------------------------------
{"bar": "baz", "balance": 7.77, "active":false}
(1 row)
SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::jsonb;
jsonb
--------------------------------------------------
{"bar": "baz", "active": false, "balance": 7.77}
(1 row)
一个值得注意的语义无关细节是,在 jsonb 中,数字将根据底层 numeric 类型的行为进行打印。在实践中,这意味着以 E 符号输入的数字将不带符号打印,例如:
One semantically-insignificant detail worth noting is that in jsonb, numbers will be printed according to the behavior of the underlying numeric type. In practice this means that numbers entered with E notation will be printed without it, for example:
SELECT '{"reading": 1.230e-5}'::json, '{"reading": 1.230e-5}'::jsonb;
json | jsonb
-----------------------+-------------------------
{"reading": 1.230e-5} | {"reading": 0.00001230}
(1 row)
但是,jsonb 将保留尾随小数零,如本例所示,即使对于相等性检查之类的目的语义上不重要。
However, jsonb will preserve trailing fractional zeroes, as seen in this example, even though those are semantically insignificant for purposes such as equality checks.
有关用于构造和处理 JSON 值的内置函数和运算符列表,请参见 Section 9.16。
For the list of built-in functions and operators available for constructing and processing JSON values, see Section 9.16.
8.14.2. Designing JSON Documents #
将数据表示为 JSON 可以比传统的关联数据模型更灵活,在需求不断变化的环境中非常吸引人。两种方法完全有可能在同一个应用程序中共存并相互补充。但是,即使对于需要最大灵活性的应用程序,仍然建议 JSON 文档具有固定的结构。该结构通常不强制执行(虽然可以声明性地执行某些业务规则),但具有可预测的结构使编写有用的总结表中的一组“文档”(数据)的查询变得更加容易。
Representing data as JSON can be considerably more flexible than the traditional relational data model, which is compelling in environments where requirements are fluid. It is quite possible for both approaches to co-exist and complement each other within the same application. However, even for applications where maximal flexibility is desired, it is still recommended that JSON documents have a somewhat fixed structure. The structure is typically unenforced (though enforcing some business rules declaratively is possible), but having a predictable structure makes it easier to write queries that usefully summarize a set of “documents” (datums) in a table.
存储在表中的 JSON 数据受与任何其他数据类型相同的并发控制考虑。尽管存储大型文档是可行的,但请记住,任何更新都会获取对整行的行级锁。考虑将 JSON 文档限制为可管理的大小,以减少更新事务之间的锁争用。理想情况下,每个 JSON 文档都应该表示一个原子数据,业务规则规定不能合理地进一步细分为可以独立修改的较小数据。
JSON data is subject to the same concurrency-control considerations as any other data type when stored in a table. Although storing large documents is practicable, keep in mind that any update acquires a row-level lock on the whole row. Consider limiting JSON documents to a manageable size in order to decrease lock contention among updating transactions. Ideally, JSON documents should each represent an atomic datum that business rules dictate cannot reasonably be further subdivided into smaller datums that could be modified independently.
8.14.3. jsonb Containment and Existence #
测试 containment 是 jsonb 的一项重要功能。没有针对 json 类型的一组并行设施。包含测试是否一个 jsonb 文档包含在另一个文档中。除了有注释的,这些示例返回 true:
Testing containment is an important capability of jsonb. There is no parallel set of facilities for the json type. Containment tests whether one jsonb document has contained within it another one. These examples return true except as noted:
-- Simple scalar/primitive values contain only the identical value:
SELECT '"foo"'::jsonb @> '"foo"'::jsonb;
-- The array on the right side is contained within the one on the left:
SELECT '[1, 2, 3]'::jsonb @> '[1, 3]'::jsonb;
-- Order of array elements is not significant, so this is also true:
SELECT '[1, 2, 3]'::jsonb @> '[3, 1]'::jsonb;
-- Duplicate array elements don't matter either:
SELECT '[1, 2, 3]'::jsonb @> '[1, 2, 2]'::jsonb;
-- The object with a single pair on the right side is contained
-- within the object on the left side:
SELECT '{"product": "PostgreSQL", "version": 9.4, "jsonb": true}'::jsonb @> '{"version": 9.4}'::jsonb;
-- The array on the right side is not considered contained within the
-- array on the left, even though a similar array is nested within it:
SELECT '[1, 2, [1, 3]]'::jsonb @> '[1, 3]'::jsonb; -- yields false
-- But with a layer of nesting, it is contained:
SELECT '[1, 2, [1, 3]]'::jsonb @> '[[1, 3]]'::jsonb;
-- Similarly, containment is not reported here:
SELECT '{"foo": {"bar": "baz"}}'::jsonb @> '{"bar": "baz"}'::jsonb; -- yields false
-- A top-level key and an empty object is contained:
SELECT '{"foo": {"bar": "baz"}}'::jsonb @> '{"foo": {}}'::jsonb;
一般原则是,包含的对象在结构和数据内容上必须与被包含的对象匹配,在从包含的对象中丢弃一些不匹配的数组元素或对象键/值对后可能匹配。但是请记住,在执行包含匹配时,数组元素的顺序并不重要,并且实际上只考虑一次重复数组元素。
The general principle is that the contained object must match the containing object as to structure and data contents, possibly after discarding some non-matching array elements or object key/value pairs from the containing object. But remember that the order of array elements is not significant when doing a containment match, and duplicate array elements are effectively considered only once.
作为对结构必须匹配的一般原则的特殊例外,数组可以包含基本值:
As a special exception to the general principle that the structures must match, an array may contain a primitive value:
-- This array contains the primitive string value:
SELECT '["foo", "bar"]'::jsonb @> '"bar"'::jsonb;
-- This exception is not reciprocal -- non-containment is reported here:
SELECT '"bar"'::jsonb @> '["bar"]'::jsonb; -- yields false
jsonb 也有一个 existence 运算符,它是包含主题的一个变体:它测试字符串(作为一个 text 值给出)是否作为对象键或数组元素出现在 jsonb 值的顶层。这些示例返回 true,除非另有说明:
jsonb also has an existence operator, which is a variation on the theme of containment: it tests whether a string (given as a text value) appears as an object key or array element at the top level of the jsonb value. These examples return true except as noted:
-- String exists as array element:
SELECT '["foo", "bar", "baz"]'::jsonb ? 'bar';
-- String exists as object key:
SELECT '{"foo": "bar"}'::jsonb ? 'foo';
-- Object values are not considered:
SELECT '{"foo": "bar"}'::jsonb ? 'bar'; -- yields false
-- As with containment, existence must match at the top level:
SELECT '{"foo": {"bar": "baz"}}'::jsonb ? 'bar'; -- yields false
-- A string is considered to exist if it matches a primitive JSON string:
SELECT '"foo"'::jsonb ? 'foo';
当涉及许多键或元素时,JSON 对象比数组更适合用于测试包含或是否存在,因为与数组不同,它们在内部针对搜索进行了优化,并且不需要线性搜索。
JSON objects are better suited than arrays for testing containment or existence when there are many keys or elements involved, because unlike arrays they are internally optimized for searching, and do not need to be searched linearly.
Tip
因为 JSON 包含是嵌套的,所以适当的查询可以跳过子对象的显式选择。例如,假设我们有一个 doc 列,在该列中包含顶层的对象,其中大多数对象包含 tags 域,该域包含子对象的数组。此查询查找既包含 "term":"paris" 又包含 "term":"food" 的子对象的条目,同时忽略 tags 数组外的任何此类键:
Because JSON containment is nested, an appropriate query can skip explicit selection of sub-objects. As an example, suppose that we have a doc column containing objects at the top level, with most objects containing tags fields that contain arrays of sub-objects. This query finds entries in which sub-objects containing both "term":"paris" and "term":"food" appear, while ignoring any such keys outside the tags array:
SELECT doc->'site_name' FROM websites
WHERE doc @> '{"tags":[{"term":"paris"}, {"term":"food"}]}';
可以使用下面这种方式实现相同的事情:
One could accomplish the same thing with, say,
SELECT doc->'site_name' FROM websites
WHERE doc->'tags' @> '[{"term":"paris"}, {"term":"food"}]';
但这种方法的灵活性较差,并且通常效率也不高。
but that approach is less flexible, and often less efficient as well.
另一方面,JSON 存在运算符不是嵌套的:它只会查找 JSON 值顶层的指定键或数组元素。
On the other hand, the JSON existence operator is not nested: it will only look for the specified key or array element at top level of the JSON value.
各种包含和存在运算符与所有其他 JSON 运算符和函数都在 Section 9.16中记录。
The various containment and existence operators, along with all other JSON operators and functions are documented in Section 9.16.
8.14.4. jsonb Indexing #
GIN 索引可用于有效搜索大量 jsonb 文档(资料)中出现的键或键/值对。提供了两个 GIN “运算符类”,提供不同的性能和灵活性权衡。
GIN indexes can be used to efficiently search for keys or key/value pairs occurring within a large number of jsonb documents (datums). Two GIN “operator classes” are provided, offering different performance and flexibility trade-offs.
jsonb_的默认 GIN 运算符类支持使用键存在运算符 _?、?|_和 _?&、包含运算符 _@>_以及 _jsonpath_匹配运算符 _@?_和 _@@_进行查询。(有关这些运算符实现的语义的详细信息,请参见 Table 9.46。)使用此运算符类创建索引的一个示例是:
The default GIN operator class for jsonb supports queries with the key-exists operators ?, ?| and ?&, the containment operator @>, and the jsonpath match operators @? and @@. (For details of the semantics that these operators implement, see Table 9.46.) An example of creating an index with this operator class is:
CREATE INDEX idxgin ON api USING GIN (jdoc);
非默认 GIN 运算符类 jsonb_path_ops 不支持键存在运算符,但支持 @>、@? 和 @@。使用此运算符类创建索引的示例:
The non-default GIN operator class jsonb_path_ops does not support the key-exists operators, but it does support @>, @? and @@. An example of creating an index with this operator class is:
CREATE INDEX idxginp ON api USING GIN (jdoc jsonb_path_ops);
考虑从第三方网络服务检索的 JSON 文档的表的示例,其中有记录的模式定义。一个典型的文档是:
Consider the example of a table that stores JSON documents retrieved from a third-party web service, with a documented schema definition. A typical document is:
{
"guid": "9c36adc1-7fb5-4d5b-83b4-90356a46061a",
"name": "Angela Barton",
"is_active": true,
"company": "Magnafone",
"address": "178 Howard Place, Gulf, Washington, 702",
"registered": "2009-11-07T08:53:22 +08:00",
"latitude": 19.793713,
"longitude": 86.513373,
"tags": [
"enim",
"aliquip",
"qui"
]
}
我们把这些文档存储在一个名为 api 的表中,在一个名为 jdoc 的 jsonb 列中。如果在此列上创建了 GIN 索引,则可以利用该索引进行诸如以下的查询:
We store these documents in a table named api, in a jsonb column named jdoc. If a GIN index is created on this column, queries like the following can make use of the index:
-- Find documents in which the key "company" has value "Magnafone"
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"company": "Magnafone"}';
但是,索引无法用于诸如以下的查询,因为尽管运算符 ? 可被索引,但它不会直接应用于已编制索引的列 jdoc:
However, the index could not be used for queries like the following, because though the operator ? is indexable, it is not applied directly to the indexed column jdoc:
-- Find documents in which the key "tags" contains key or array element "qui"
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc -> 'tags' ? 'qui';
即使如此,通过适当使用表达式索引,上面的查询仍可以使用索引。如果查询 "tags" 键内的特定项很常见,那么定义一个这样的索引可能是有价值的:
Still, with appropriate use of expression indexes, the above query can use an index. If querying for particular items within the "tags" key is common, defining an index like this may be worthwhile:
CREATE INDEX idxgintags ON api USING GIN ((jdoc -> 'tags'));
现在,_WHERE_子句 _jdoc → 'tags' ? 'qui'_将被识别为可索引运算符 _?_对索引表达式 _jdoc → 'tags'_的一个应用。(可以在 Section 11.7中找到有关表达式索引的更多信息。)
Now, the WHERE clause jdoc → 'tags' ? 'qui' will be recognized as an application of the indexable operator ? to the indexed expression jdoc → 'tags'. (More information on expression indexes can be found in Section 11.7.)
查询的另一种方法是利用包含,例如:
Another approach to querying is to exploit containment, for example:
-- Find documents in which the key "tags" contains array element "qui"
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"tags": ["qui"]}';
jdoc 列上的一个简单 GIN 索引可以支持此查询。但请注意,这样一个索引将存储 jdoc 列中的每个键和值的副本,而前一个示例的表达式索引仅存储在 tags 键下找到的数据。虽然简单索引方法要灵活得多(因为它支持针对任何键的查询),但有针对性的表达式索引可能比简单索引更小、更快的搜索速度。
A simple GIN index on the jdoc column can support this query. But note that such an index will store copies of every key and value in the jdoc column, whereas the expression index of the previous example stores only data found under the tags key. While the simple-index approach is far more flexible (since it supports queries about any key), targeted expression indexes are likely to be smaller and faster to search than a simple index.
GIN 索引还支持执行 jsonpath 匹配的 @? 和 @@ 运算符。示例为:
GIN indexes also support the @? and @@ operators, which perform jsonpath matching. Examples are
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @? '$.tags[*] ? (@ == "qui")';
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @@ '$.tags[*] == "qui"';
对于这些运算符,GIN索引从 jsonpath 模式中提取 accessors_chain = constant 形式的子句,并根据这些子句中提到的键和值执行索引搜索。访问器链可能包括 ._key_ 、 [], and [_index]_ accessors. The jsonb_ops operator class also supports .* and .* 访问器,但 jsonb_path_ops 运算符类没有。
For these operators, a GIN index extracts clauses of the form accessors_chain_ = constant out of the _jsonpath pattern, and does the index search based on the keys and values mentioned in these clauses. The accessors chain may include ._key_, [], and [_index]_ accessors. The jsonb_ops operator class also supports .* and .* accessors, but the jsonb_path_ops operator class does not.
虽然 jsonb_path_ops 操作符类仅支持带有 @>、@? 和 @@ 操作符的查询,但它比默认操作符类 jsonb_ops 具有显著的性能优势。jsonb_path_ops 索引通常比针对相同数据的 jsonb_ops 索引小得多,并且搜索的特殊性更好,尤其是在查询包含在数据中频繁出现的键时。因此,搜索操作通常比使用默认操作符类时执行得更好。
Although the jsonb_path_ops operator class supports only queries with the @>, @? and @@ operators, it has notable performance advantages over the default operator class jsonb_ops. A jsonb_path_ops index is usually much smaller than a jsonb_ops index over the same data, and the specificity of searches is better, particularly when queries contain keys that appear frequently in the data. Therefore search operations typically perform better than with the default operator class.
jsonb_ops 和 jsonb_path_ops GIN索引之间的技术差异在于,前者为数据中的每个键和值创建独立的索引项,而后者仅为数据中的每个值创建索引项。 [7 ]基本上,每个 jsonb_path_ops 索引项都是值和导致该值的键的哈希;例如,要索引 {"foo": {"bar": "baz"}} ,将创建单个索引项,并将三个 foo 、 bar 和 baz 全部合并到哈希值中。因此,寻找此结构的包含查询将导致极其具体的索引搜索;但根本没有办法找出 foo 是否表现为一个键。另一方面, jsonb_ops 索引将创建分别表示 foo 、 bar 和 baz 的三个索引项;然后执行包含查询,它将查找包含这三个项的所有行。虽然GIN索引可以相当高效地执行这样的AND搜索,但它仍然不如等效的 jsonb_path_ops 搜索具体且速度慢,尤其是如果包含任何三个索引项之一的大量行时。
The technical difference between a jsonb_ops and a jsonb_path_ops GIN index is that the former creates independent index items for each key and value in the data, while the latter creates index items only for each value in the data. [7] Basically, each jsonb_path_ops index item is a hash of the value and the key(s) leading to it; for example to index {"foo": {"bar": "baz"}}, a single index item would be created incorporating all three of foo, bar, and baz into the hash value. Thus a containment query looking for this structure would result in an extremely specific index search; but there is no way at all to find out whether foo appears as a key. On the other hand, a jsonb_ops index would create three index items representing foo, bar, and baz separately; then to do the containment query, it would look for rows containing all three of these items. While GIN indexes can perform such an AND search fairly efficiently, it will still be less specific and slower than the equivalent jsonb_path_ops search, especially if there are a very large number of rows containing any single one of the three index items.
jsonb_path_ops 方法的一个缺点是,它不会为不包含任何值的 JSON 结构(例如 {"a": {}})生成索引条目。如果请求搜索包含此类结构的文档,则需要进行全索引扫描,这非常慢。因此,jsonb_path_ops 不适用于经常执行此类搜索的应用程序。
A disadvantage of the jsonb_path_ops approach is that it produces no index entries for JSON structures not containing any values, such as {"a": {}}. If a search for documents containing such a structure is requested, it will require a full-index scan, which is quite slow. jsonb_path_ops is therefore ill-suited for applications that often perform such searches.
jsonb 还支持 btree 和 hash 索引。这些通常仅在检查完整 JSON 文档的相等性很重要时才有用。jsonb 数据项的 btree 排序很少受到关注,但为了完整性,它是:
jsonb also supports btree and hash indexes. These are usually useful only if it’s important to check equality of complete JSON documents. The btree ordering for jsonb datums is seldom of great interest, but for completeness it is:
Object > Array > Boolean > Number > String > Null
Object with n pairs > object with n - 1 pairs
Array with n elements > array with n - 1 elements
具有相等对数的对象按以下顺序进行比较:
Objects with equal numbers of pairs are compared in the order:
key-1, value-1, key-2 ...
请注意,对象键按其存储顺序进行比较;特别是,由于较短的键存储在较长键之前,这可能导致不直观的结果,例如:
Note that object keys are compared in their storage order; in particular, since shorter keys are stored before longer keys, this can lead to results that might be unintuitive, such as:
{ "aa": 1, "c": 1} > {"b": 1, "d": 1}
类似地,具有相等元素数量的数组按以下顺序进行比较:
Similarly, arrays with equal numbers of elements are compared in the order:
element-1, element-2 ...
使用与底层 PostgreSQL 数据类型相同的比较规则比较原始 JSON 值。字符串使用默认数据库排序规则进行比较。
Primitive JSON values are compared using the same comparison rules as for the underlying PostgreSQL data type. Strings are compared using the default database collation.
8.14.5. jsonb Subscripting #
jsonb 数据类型支持数组式下标表达式,以提取和修改元素。嵌套值可以通过链接下标表达式来表示,遵循与 jsonb_set 函数中的 path 参数相同的规则。如果 jsonb 值是一个数组,则数字下标从零开始,负整数从数组的最后一个元素向后计数。不支持切片表达式。下标表达式的结果始终为 jsonb 数据类型。
The jsonb data type supports array-style subscripting expressions to extract and modify elements. Nested values can be indicated by chaining subscripting expressions, following the same rules as the path argument in the jsonb_set function. If a jsonb value is an array, numeric subscripts start at zero, and negative integers count backwards from the last element of the array. Slice expressions are not supported. The result of a subscripting expression is always of the jsonb data type.
UPDATE_语句可以在 _SET_子句中使用下标修改 _jsonb_值。对于所有受影响的值,只要它们存在,就必须能够遍历下标路径。例如,如果每个 _val、val['a']_和 _val['a']['b']_都是对象,则路径 _val['a']['b']['c']_可以一直遍历到 _c。如果任何 val['a']_或 _val['a']['b']_没有定义,它将被创建为一个空对象并根据需要填充。然而,如果任何 _val_本身或其中间值被定义为非对象,例如字符串、数字或 _jsonb__null,则无法进行遍历,因此会引发错误并中止事务。
UPDATE statements may use subscripting in the SET clause to modify jsonb values. Subscript paths must be traversable for all affected values insofar as they exist. For instance, the path val['a']['b']['c'] can be traversed all the way to c if every val, val['a'], and val['a']['b'] is an object. If any val['a'] or val['a']['b'] is not defined, it will be created as an empty object and filled as necessary. However, if any val itself or one of the intermediary values is defined as a non-object such as a string, number, or jsonb null, traversal cannot proceed so an error is raised and the transaction aborted.
下标语法的示例:
An example of subscripting syntax:
-- Extract object value by key
SELECT ('{"a": 1}'::jsonb)['a'];
-- Extract nested object value by key path
SELECT ('{"a": {"b": {"c": 1}}}'::jsonb)['a']['b']['c'];
-- Extract array element by index
SELECT ('[1, "2", null]'::jsonb)[1];
-- Update object value by key. Note the quotes around '1': the assigned
-- value must be of the jsonb type as well
UPDATE table_name SET jsonb_field['key'] = '1';
-- This will raise an error if any record's jsonb_field['a']['b'] is something
-- other than an object. For example, the value {"a": 1} has a numeric value
-- of the key 'a'.
UPDATE table_name SET jsonb_field['a']['b']['c'] = '1';
-- Filter records using a WHERE clause with subscripting. Since the result of
-- subscripting is jsonb, the value we compare it against must also be jsonb.
-- The double quotes make "value" also a valid jsonb string.
SELECT * FROM table_name WHERE jsonb_field['key'] = '"value"';
通过下标进行 jsonb 赋值以不同于 jsonb_set 的方式处理一些边缘情况。当源 jsonb 值为 NULL 时,通过下标进行赋值将进行,就像它是下标键暗示的类型(对象或数组)的空 JSON 值:
jsonb assignment via subscripting handles a few edge cases differently from jsonb_set. When a source jsonb value is NULL, assignment via subscripting will proceed as if it was an empty JSON value of the type (object or array) implied by the subscript key:
-- Where jsonb_field was NULL, it is now {"a": 1}
UPDATE table_name SET jsonb_field['a'] = '1';
-- Where jsonb_field was NULL, it is now [1]
UPDATE table_name SET jsonb_field[0] = '1';
如果为包含的元素太少的数组指定索引,则 NULL 元素将附加,直到达到索引并且可以设置值。
If an index is specified for an array containing too few elements, NULL elements will be appended until the index is reachable and the value can be set.
-- Where jsonb_field was [], it is now [null, null, 2];
-- where jsonb_field was [0], it is now [0, null, 2]
UPDATE table_name SET jsonb_field[2] = '2';
只要要遍历的最后一个现有元素是一个对象或数组(由相应的下标暗示),jsonb 值就会接受对不存在的下标路径的赋值(路径中最后一个下标指示的元素未遍历,可以是任何内容)。嵌套数组和对象结构将被创建,并且在前一种情况下,null 填充,如下标路径所示,直到可以放置赋值值。
A jsonb value will accept assignments to nonexistent subscript paths as long as the last existing element to be traversed is an object or array, as implied by the corresponding subscript (the element indicated by the last subscript in the path is not traversed and may be anything). Nested array and object structures will be created, and in the former case null-padded, as specified by the subscript path until the assigned value can be placed.
-- Where jsonb_field was {}, it is now {"a": [{"b": 1}]}
UPDATE table_name SET jsonb_field['a'][0]['b'] = '1';
-- Where jsonb_field was [], it is now [null, {"a": 1}]
UPDATE table_name SET jsonb_field[1]['a'] = '1';
8.14.6. Transforms #
有多个附加扩展来实现针对不同过程语言的 jsonb 类型的转换。
Additional extensions are available that implement transforms for the jsonb type for different procedural languages.
PL/Perl 的扩展称为 jsonb_plperl 和 jsonb_plperlu。如果使用它们,jsonb 值将分别映射到 Perl 数组、哈希和标量。
The extensions for PL/Perl are called jsonb_plperl and jsonb_plperlu. If you use them, jsonb values are mapped to Perl arrays, hashes, and scalars, as appropriate.
PL/Python 的扩展称为 jsonb_plpython3u。如果使用它,jsonb 值将分别映射到 Python 字典、列表和标量。
The extension for PL/Python is called jsonb_plpython3u. If you use it, jsonb values are mapped to Python dictionaries, lists, and scalars, as appropriate.
在这些扩展中,jsonb_plperl 被认为是“受信任的”,即它可以由在当前数据库上具有 CREATE 权限的非超级用户安装。其余需要超级用户权限才能安装。
Of these extensions, jsonb_plperl is considered “trusted”, that is, it can be installed by non-superusers who have CREATE privilege on the current database. The rest require superuser privilege to install.
8.14.7. jsonpath Type #
jsonpath 类型实现对 PostgreSQL 中 SQL/JSON 路径语言的支持,以有效地查询 JSON 数据。它提供了已解析 SQL/JSON 路径表达式的二进制表示形式,该表示形式指定了路径引擎应从 JSON 数据中检索的项目,以便使用 SQL/JSON 查询函数进行进一步处理。
The jsonpath type implements support for the SQL/JSON path language in PostgreSQL to efficiently query JSON data. It provides a binary representation of the parsed SQL/JSON path expression that specifies the items to be retrieved by the path engine from the JSON data for further processing with the SQL/JSON query functions.
SQL/JSON 路径谓词和运算符的语义通常遵循 SQL。同时,为了提供处理 JSON 数据的自然方式,SQL/JSON 路径语法使用了一些 JavaScript 约定:
The semantics of SQL/JSON path predicates and operators generally follow SQL. At the same time, to provide a natural way of working with JSON data, SQL/JSON path syntax uses some JavaScript conventions:
SQL/JSON 路径表达式中的数字文字遵循 JavaScript 规则,这在一些小细节上与 SQL 和 JSON 都不同。例如,SQL/JSON 路径允许 .1 和 1.,而这在 JSON 中无效。例如,支持非十进制整数文字和下划线分隔符, 1_000_000,0x1EEE_FFFF,0o273,0b100101。在 SQL/JSON 路径(以及 JavaScript 中,但不是在 SQL 本身中),基数前缀之后一定不能紧跟下划线分隔符。
Numeric literals in SQL/JSON path expressions follow JavaScript rules, which are different from both SQL and JSON in some minor details. For example, SQL/JSON path allows .1 and 1., which are invalid in JSON. Non-decimal integer literals and underscore separators are supported, for example, 1_000_000, 0x1EEE_FFFF, 0o273, 0b100101. In SQL/JSON path (and in JavaScript, but not in SQL proper), there must not be an underscore separator directly after the radix prefix.
SQL/JSON路径表达式通常以SQL字符字符串文本形式写在SQL查询中,因此必须用单引号引起来,并且值中所需的任何单引号都必须加倍(请参阅 Section 4.1.2.1 )。某些形式的路径表达式需要它们内部的字符串文本。这些嵌入的字符串文本遵循JavaScript/ECMAScript惯例:它们必须用双引号引起来,可以在其中使用反斜杠转义来表示难以输入的字符。特别是,在嵌入式字符串文本中编写双引号的方式是 \" ,而要写入反斜杠本身,必须编写 \\ 。其他特殊反斜杠序列包括JavaScript字符串中识别的序列: \b 、 \f 、 \n 、 \r 、 \t 、 \v 用于各种ASCII控制字符, \x_NN for a character code written with only two hex digits, \u_NNNN for a Unicode character identified by its 4-hex-digit code point, and \u{_N… _ 用于使用1到6个十六进制数字编写的Unicode字符代码点。
An SQL/JSON path expression is typically written in an SQL query as an SQL character string literal, so it must be enclosed in single quotes, and any single quotes desired within the value must be doubled (see Section 4.1.2.1). Some forms of path expressions require string literals within them. These embedded string literals follow JavaScript/ECMAScript conventions: they must be surrounded by double quotes, and backslash escapes may be used within them to represent otherwise-hard-to-type characters. In particular, the way to write a double quote within an embedded string literal is \", and to write a backslash itself, you must write \\. Other special backslash sequences include those recognized in JavaScript strings: \b, \f, \n, \r, \t, \v for various ASCII control characters, \x_NN for a character code written with only two hex digits, _\u_NNNN for a Unicode character identified by its 4-hex-digit code point, and _\u{_N…}_ for a Unicode character code point written with 1 to 6 hex digits.
路径表达式由一系列路径元素组成,这些路径元素可以是以下任何一项:
A path expression consists of a sequence of path elements, which can be any of the following:
有关将 jsonpath 表达式与 SQL/JSON 查询函数配合使用的详细信息,请参阅 Section 9.16.2。
For details on using jsonpath expressions with SQL/JSON query functions, see Section 9.16.2.
Table 8.24. jsonpath Variables
Table 8.24. jsonpath Variables
Variable |
Description |
$ |
A variable representing the JSON value being queried (the context item). |
$varname |
A named variable. Its value can be set by the parameter vars of several JSON processing functions; see Table 9.49 for details. |
@ |
A variable representing the result of path evaluation in filter expressions. |
Table 8.25. jsonpath Accessors
Table 8.25. jsonpath Accessors
Accessor Operator |
Description |
._key_ ."$_varname"_ |
Member accessor that returns an object member with the specified key. If the key name matches some named variable starting with $ or does not meet the JavaScript rules for an identifier, it must be enclosed in double quotes to make it a string literal. |
.* |
Wildcard member accessor that returns the values of all members located at the top level of the current object. |
.** |
Recursive wildcard member accessor that processes all levels of the JSON hierarchy of the current object and returns all the member values, regardless of their nesting level. This is a PostgreSQL extension of the SQL/JSON standard. |
.{_level}_ .{_start_level to end_level}_ |
Like .**, but selects only the specified levels of the JSON hierarchy. Nesting levels are specified as integers. Level zero corresponds to the current object. To access the lowest nesting level, you can use the last keyword. This is a PostgreSQL extension of the SQL/JSON standard. |
[_subscript, …]_ |
Array element accessor. subscript can be given in two forms: index or start_index_ to end_index. The first form returns a single array element by its index. The second form returns an array slice by the range of indexes, including the elements that correspond to the provided _start_index and end_index. The specified index can be an integer, as well as an expression returning a single numeric value, which is automatically cast to integer. Index zero corresponds to the first array element. You can also use the last keyword to denote the last array element, which is useful for handling arrays of unknown length. |
[*] |
Wildcard array element accessor that returns all array elements. |
[7 ]出于此目的,术语“值”包括数组元素,尽管JSON术语有时认为数组元素不同于对象中的值。
[7] For this purpose, the term “value” includes array elements, though JSON terminology sometimes considers array elements distinct from values within objects.