Chỉ hơn một năm trước đây chúng tôi đã hỏi PostgreSQL là cơ sở dữ liệu JSON tiếp theo của bạn... Hiện tại, với phiên bản PostgreSQL 9.5, là thời gian để kiểm tra luật của Betteridge vẫn được áp dụng. Hãy nói về JSONB được hỗ trợ trong PostgreSQL 9.5.

Để hiểu rõ hơn, chúng ta sẽ tìm hiểu lịch sử của JSON trong PostgreSQL. Tuy nhiên, bạn cũng có thể bỏ qua phần đầu, để đọc các tính năng mới. Câu chuyện JSON bắt đầu với sự xuất hiện của JSON trong PostgreSQL 9.2.

JSON trong 9.2

Kiểu dữ liệu JSON xuất hiện lần đầu trong PostgreSQL 9.2 về cơ bản là một cột text được gắn cờ như dữ liệu JSON để xử lý thông qua một parser. Trong 9.2, bạn có thể chuyển các dòng và mảng trong json và cho mọi thứ khác bằng cách sử dụng một trong các ngôn ngữ PL. Nó hữu ích trong một vài trường hợp nhưng vẫn cần nhiều hơn. Để minh họa, nếu chúng ta có dữ liệu JSON giống như thế này:

{
  "title": "The Shawshank Redemption",
  "num_votes": 1566874,
  "rating": 9.3,
  "year": "1994",
  "type": "feature",
  "can_rate": true,
  "tconst": "tt0111161",
  "image": {
    "url": "http://ia.media-imdb.com/images/M/MV5BODU4MjU4NjIwNl5BMl5BanBnXkFtZTgwMDU2MjEyMDE@._V1_.jpg",
    "width": 933,
    "height": 1388
  }
}

Chúng ta có thể tạo một bảng:

CREATE TABLE filmsjson ( id BIGSERIAL PRIMARY KEY, data JSON );

Và chèn dữ liệu vào bảng trên:

compose=> INSERT INTO filmsjson (data) VALUES ('{  
  "title": "The Shawshank Redemption",
  "num_votes": 1566874,
  "rating": 9.3,
  "year": "1994",
  "type": "feature",
  "can_rate": true,
  "tconst": "tt0111161",
  "image": {
    "url": "http://ia.media-imdb.com/images/M/MV5BODU4MjU4NjIwNl5BMl5BanBnXkFtZ
TgwMDU2MjEyMDE@._V1_.jpg",  
    "width": 933,
    "height": 1388
  }
}')
INSERT 0 1  
compose=> select * FROM filmsjson;  
 id |                                                   data                                                    
----+-----------------------------------------------------------------------------------------------------------
  1 | {                                                                                                        +
    |   "title": "The Shawshank Redemption",                                                                   +
    |   "num_votes": 1566874,                                                                                  +
    |   "rating": 9.3,                                                                                         +
    |   "year": "1994",                                                                                        +
    |   "type": "feature",                                                                                     +
    |   "can_rate": true,                                                                                      +
    |   "tconst": "tt0111161",                                                                                 +
    |   "image": {                                                                                             +
    |     "url": "http://ia.media-imdb.com/images/M/MV5BODU4MjU4NjIwNl5BMl5BanBnXkFtZTgwMDU2MjEyMDE@._V1_.jpg",+
    |     "width": 933,                                                                                        +
    |     "height": 1388                                                                                       +
    |   }                                                                                                      +
    | }
(1 row);

Ngoài việc lưu trữ và nhận toàn bộ tài liệu, có rất ít thứ chúng ta có thể làm với nó. Chú ý, tất cả khoảng trắng vẫn được giữ nguyên. Cái sẽ quan trọng sau này...

Cơ sở dữ liệu PostgreSQL được sử dụng rất nhiều trong các khóa học lập trìnhthực tập lập trình tại TechMaster.

Nhanh chóng chuyển tới PostgreSQL 9.3

Đằng sau parser mới cho JSON trong PostgreSQL 9.3, các toán tử xuất hiện để trích xuất các giá trị từ kiểu dữ liệu JSON. Đứng đầu trong số chúng là -> cái có thể lấy một số nguyên, trích xuất một giá trị từ một mảng hoặc lấy một chuỗi, trong một kiểu dữ liệu JSON và ->> tương tự như -> nhưng trả lại text. Xây dựng dựa trên các toán tử này là #> và #>> cái cho phép sử dụng đường dẫn tới một giá trị đặc biệt để trích xuất.

compose=> select data->'title' from filmsjson;  
          ?column?          
----------------------------
 "The Shawshank Redemption"
(1 row)
compose=> select data#>'{image,width}' from filmsjson;  
 ?column? 
----------
 933
(1 row)

Đường dẫn là một danh sách các key với cấp độ giảm dần trong tài liệu JSON. Đừng quên dấu ngoặc nhọn { } biểu diễn JSON, mặc dù đây là một mảng text, cái PostgreSQL biên dịch thành một text[]. Điều đó có nghĩa là truy vấn ở bên trên tương đương với:

select data#>ARRAY['image','width'] from filmsjsonb;  

Mặc dù kết hợp một tập hợp các hàm nhưng vẫn còn khá nhiều hạn chế. Nó không thực sự cho phép thực hiện các truy vấn phức tạp, việc đánh chỉ mục giới hạn trên các trường cụ thể và chỉ có một vài cách để tạo ra các phần tử JSON. Nhưng quan trọng nhất là việc parsing một trường text là không hiệu quả.

Cắt đến PostgreSQL 9.4

PostgreSQL 9.4 giới thiệu JSONB. JSONB là một phiên bản mã hóa binary của JSON nó lưu trữ các key và giá trị của tài liệu JSON hiệu quả hơn. Điều này có nghĩa là tất cả các các khoảng trắng sẽ được bỏ đi và cùng với nó là tất cả sự cần thiết phải phân tích cú pháp JSON. Bạn cũng không thể có các keys giống nhau cùng cấp độ và bạn thường mất định dạng cấu trúc tài liệu. Đó là một sự hi sinh có giá trị bởi vì tất cả mọi thứ nói chung hiệu quả hơn vì không cần phải parsing text. Nó cũng làm insert dữ liệu chậm đi bởi vì các phân tích thực sự được thực hiện. Để thấy sự khác biệt, hãy tạo một bảng JSONB và chèn dữ liệu vào nó.

compose=> CREATE TABLE filmsjsonb ( id BIGSERIAL PRIMARY KEY, data JSONB );  
CREATE TABLE  
compose=> INSERT INTO filmsjsonb (data) VALUES ('{  
  "title": "The Shawshank Redemption",
  "num_votes": 1566874,
  "rating": 9.3,
  "year": "1994",
  "type": "feature",
  "can_rate": true,
  "tconst": "tt0111161",
  "image": {
    "url": "http://ia.media-imdb.com/images/M/MV5BODU4MjU4NjIwNl5BMl5BanBnXkFtZ
TgwMDU2MjEyMDE@._V1_.jpg",  
    "width": 933,
    "height": 1388
  }
}');
INSERT 0 1  
compose=> select * from filmsjsonb  
id |                                                                                                                                                 data  
----+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  1 | {"type": "feature", "year": "1994", "image": {"url": "http://ia.media-imdb.com/images/M/MV5BODU4MjU4NjIwNl5BMl5BanBnXkFtZTgwMDU2MjEyMDE@._V1_.jpg", "width": 933, "height": 1388}, "title": "The Shawshank Redemption", "rating": 9.3, "tconst": "tt0111161", "can_rate": true, "num_votes": 1566874}
(1 row)

Bạn có thể thấy các khoảng trắng bị bỏ đi để lại danh sách key/value ngắn gọn.

Mặc dù chia sẻ nhiều tính năng, nhưng đây là phần khá thú vị: JSONB không có hàm tạo. Trong 9.4, kiểu dữ liệu JSONB là tập hợp các hàm tạo mở rộng: json_build_object()json_build_array() và json_object(). Các hàm này được sử dụng để tạo thành phiên bản JSONB. Nó phản ánh logic các lập trình viên PostgreSQL đã áp dụng - JSON cho việc lưu trữ tài liệu, JSONB nhanh, thao tác hiệu quả. Vì trong khi JSON và JSONB đều có các toán tử  ->->>#> và #>> , chỉ JSONB có toán tử "contains" và "exists" @><@??| và ?&.

Toán tử "exists" kiểm tra một chuỗi là một key trong dữ liệu JSONB, ví dụ chúng ta có thể kiểm tra key "rating" có tồn tại trong trường "data":

compose=> select data->'title' from filmsjsonb where data ? 'rating';  
          ?column?          
----------------------------
 "The Shawshank Redemption"
(1 row)

Nhưng nếu truy vấn key "url" trong giá trị của keys "image", chúng ta sẽ thất bại:

compose=> select data->'title' from filmsjsonb where data ? 'url';  
 ?column? 
----------
(0 rows)

Chúng ta có thể kiểm tra giá trị "image" giống như thế này:

compose=> select data->'title' from filmsjsonb where data->'image' ? 'url';  
          ?column?          
----------------------------
 "The Shawshank Redemption"
(1 row)

Toán tử ?| làm việc tương tự nhưng có thêm "or" cho các key phù hợp thay vì chỉ một key duy nhất. Toán tử  ?& cũng tương tự nhưng thêm "and" tất cả các giá trị chuỗi trong mảng phải phù hợp.

Nhưng toán tử "exists" chỉ kiểm tra sự hiện diện. Với toán tử "contains" '@>' bạn có thể kiểm tra các key, đường dẫn và giá trị. Trước tiên, hãy import một vài movie vào database. Bây giờ, chúng ta muốn các movie trong năm 1972, chúng ta có thể  tìm kiếm các bản ghi chứa "year":"1972".

compose=> select data->'title' from filmsjsonb where data @> '{"year":"1972"}';  
    ?column?     
-----------------
 "The Godfather"
 "Solaris"
(2 rows)

Chúng ta có thể tìm kiếm các giá trị cụ thể trong đối tượng:

compose=> select data->'title' from filmsjsonb where data @> '{ "image":{"width":500}}';  
               ?column?               
--------------------------------------
 "The Green Mile"
 "My Neighbor Totoro"
 "Nausicaä of the Valley of the Wind"
(3 rows)

9.4 cũng cho phép tạo chỉ mục GIN bao gồm tất cả các trường trong tài liệu JSONB cho tất cả các toán tử JSON. Cũng có thể tạo chỉ mục GIN với  json_path_ops  nó tạo các chỉ mục nhỏ hơn, nhanh hơn nhưng chỉ sử dụng toán tử "contains" ' @> ' cái thực sự hữu ích với các thao tác tìm kiếm các giá trị cụ thể trong các tài liệu JSON lồng nhau. Mặc dù vậy, vẫn còn nhiều phạm vi cho khả năng đánh chỉ mục toàn diện hơn.

Mặc dù trong 9.4 PostgreSQL bạn có thể tạo, trích xuất và đánh chỉ mục JSON/JSONB. Cái còn thiếu là khả năng chỉnh sửa kiểu dữ liệu JSON/JSONB. Bạn cần truyền dữ liệu JSON tới một ngôn ngữ kịch bản PLv8 hoặc PLPerl nơi chúng ta có thể chỉnh sửa JSON. Vì thế 9.4 chưa hẳn là môi trường xử lý tài liệu JSON với đầy đủ các dịch vụ.

Đi vào PostgreSQL 9.5

Những tính năng mới của PostgreSQL 9.5 là khả năng sửa đổi và thao tác dữ liệu JSONB. Một phần trong số chúng là hàm jsonb_pretty() làm cho JSONB dễ đọc hơn, hãy bắt đầu với câu truy vấn:

compose=> select data from filmsjsonb where id=1;  
                                                                                                                                                 data                                                                                                                                                  
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 {"type": "feature", "year": "1994", "image": {"url": "http://ia.media-imdb.com/images/M/MV5BODU4MjU4NjIwNl5BMl5BanBnXkFtZTgwMDU2MjEyMDE@._V1_.jpg", "width": 933, "height": 1388}, "title": "The Shawshank Redemption", "rating": 9.3, "tconst": "tt0111161", "can_rate": true, "num_votes": 1566874}
(1 row)

Với hàm jsonb_pretty()  :

compose=> select jsonb_pretty(data) from filmsjsonb where id=1;  
                                                 jsonb_pretty                                                  
---------------------------------------------------------------------------------------------------------------
 {                                                                                                            +
     "type": "feature",                                                                                       +
     "year": "1994",                                                                                          +
     "image": {                                                                                               +
         "url": "http://ia.media-imdb.com/images/M/MV5BODU4MjU4NjIwNl5BMl5BanBnXkFtZTgwMDU2MjEyMDE@._V1_.jpg",+
         "width": 933,                                                                                        +
         "height": 1388                                                                                       +
     },                                                                                                       +
     "title": "The Shawshank Redemption",                                                                     +
     "rating": 9.3,                                                                                           +
     "tconst": "tt0111161",                                                                                   +
     "can_rate": true,                                                                                        +
     "num_votes": 1566874                                                                                     +
 }
(1 row)

Kết quả câu truy vấn dễ dọc hơn.

Thao tác delete

Delete là các chỉnh sửa đơn giản nhất. Chỉ cần nói cái bạn muốn bỏ đi và  thực hiện. PostgreSQL 9.5 giới thiệu các toán tử  - và #- . Toán tử làm việc giống như toán tử  -> ngoại trừ thay vì trả lại một giá trị từ một mảng (nếu cung cấp một số nguyên như tham số) hoặc đối tượng (nếu cung cấp một chuỗi), nó xóa giá trị hoặc cặp key/value. Vì thế, với cơ sở dữ liệu lưu trữ movie, nếu chúng ta muốn xóa trường "rating" đây có thể là mẹo:

compose=> update filmsjsonb set data=data-'rating';  
UPDATE 250  

Toán tử  #- làm được nhiều hơn, nó nhận một đường dẫn như một tham số. Nếu chúng ta muốn xóa các thuộc tính kích thước của hình ảnh:

compose=> update filmsjsonb set data=data#-'{image,width}';  
UPDATE 250  
compose=> update filmsjsonb set data=data#-'{image,height}';  
UPDATE 250  
compose=> select jsonb_pretty(data) from filmsjsonb where id=1;  
                                                 jsonb_pretty                                                 
--------------------------------------------------------------------------------------------------------------
 {                                                                                                           +
     "type": "feature",                                                                                      +
     "year": "1994",                                                                                         +
     "image": {                                                                                              +
         "url": "http://ia.media-imdb.com/images/M/MV5BODU4MjU4NjIwNl5BMl5BanBnXkFtZTgwMDU2MjEyMDE@._V1_.jpg"+
     },                                                                                                      +
     "title": "The Shawshank Redemption",                                                                    +
     "tconst": "tt0111161",                                                                                  +
     "can_rate": true,                                                                                       +
     "num_votes": 1566874                                                                                    +
 }
(1 row)

 Chúng ta viết 2 câu lệnh update vì đường dẫn cụ thể không cho phép các key tùy chọn, nhưng chúng ta có thể giảm xuống một câu lệnh update bằng biểu thức phức tạp hơn:

compose=> update filmsjsonb set data#-'{image,height}'#-'{image,width}';  
UPDATE 250  

Mặc dù bạn có thể xóa dữ liệu từ database, nhớ rằng bạn cũng có thể chỉ cần xóa nó từ kết quả:

compose=> select jsonb_pretty(data#-'{image,height}'#-'{image,width}') from  
 filmsjsonb where id=1;
                                                 jsonb_pretty                                                 
--------------------------------------------------------------------------------------------------------------
 {                                                                                                           +
     "type": "feature",                                                                                      +
     "year": "1994",                                                                                         +
     "image": {                                                                                              +
         "url": "http://ia.media-imdb.com/images/M/MV5BODU4MjU4NjIwNl5BMl5BanBnXkFtZTgwMDU2MjEyMDE@._V1_.jpg"+
     },                                                                                                      +
    .... 

Toán tử nối

Toán tử này kết hợp 2 đối tượng JSONB thành một. Nó làm việc với các key ở cấp độ cao nhất. Nếu 2 đối tượng JSONB có cùng key, nó sẽ lấy giá trị của key ở đối tượng bên phải. Chúng ta có thể sử dụng điều này để update dữ liệu. Ví dụ, chúng ta cần thiết lập giá trị của trường "can_rate" thành false, xóa trường "num_votes" và thêm một trường mới "revote" có giá trị là true:

compose=> update filmsjsonb set data=data || '{"can_rate":false,"num_votes":0,"  
revote":true }';  
UPDATE 250  
compose=> select jsonb_pretty(data) from filmsjsonb where id=1;  
                                                 jsonb_pretty                                                  
---------------------------------------------------------------------------------------------------------------
 {                                                                                                            +
     "type": "feature",                                                                                       +
     "year": "1994",                                                                                          +
     "image": {                                                                                               +
         "url": "http://ia.media-imdb.com/images/M/MV5BODU4MjU4NjIwNl5BMl5BanBnXkFtZTgwMDU2MjEyMDE@._V1_.jpg",+
         "width": 933,                                                                                        +
         "height": 1388                                                                                       +
     },                                                                                                       +
     "title": "The Shawshank Redemption",                                                                     +
     "rating": 9.3,                                                                                           +
     "revote": true,                                                                                          +
     "tconst": "tt0111161",                                                                                   +
     "can_rate": false,                                                                                       +
     "num_votes": 0                                                                                           +
 }
(1 row)

Đây là một cách hữu ích để hợp nhất các kiểu dữ liệu JSONB, ví dụ trong quá trình xử lý các bài viết. Cập nhật một trường ở cấp độ cao nhất có thể không cần thiết. Cập nhật một trường ở cấp độ thấp hơn trong tài liệu, sau đó bạn hợp nhất đối tượng ở cấp độ này. Nếu chỉ muốn thiết lập giá trị của một trường cụ thể.

jsonb_set for success

Hàm "jsonb_set()" được thiết kế để cập nhật một trường ở bất kỳ đâu trong tài liệu JSON. Ví dụ:

compose=> update filmsjsonb SET data = jsonb_set(data,'{"image","width"}',to_js  
onb(1024)) where id=1;  

Câu lệnh trên sẽ thay đổi giá trị của thuộc tính image.width thành 1024. Các tham số của hàm "jsonb_set()" khá đơn giản: tham số đầu tiên là trường có kiểu dữ liệu là JSONB bạn muốn chỉnh sửa, tham số thứ 2 là đường dẫn và tham số thứ 3 là giá trị thay thế giá trị hiện tại của thuộc tính ở cuối đường dẫn. Nếu cặp key/value ở cuối đường dẫn không tồn tại, mặc định hàm "jsonb_set()" sẽ tạo và thiết lập giá trị cho nó. Để không tạo cặp key/value trong trường hợp không tồn tại, thêm tham số thứ 4 (không bắt buộc) là "create_missing" với giá trị là "false". Nếu giá trị của "create_missing" là "true" nhưng các thành phần khác của đường dẫn không tồn tại "jsonb_set()" sẽ không cố tạo toàn bộ đường dẫn. Ví dụ chúng ta muốn thêm một đối tượng mới cho trường "data" chỉ cần làm:

compose=> update filmsjsonb SET data = jsonb_set(data,'{"image","quality"}','{"  
copyright":"company X","registered":true}') where id=2;  
compose=> select jsonb_pretty(data) from filmsjsonb where id=2;  
                                                 jsonb_pretty                                                  
---------------------------------------------------------------------------------------------------------------
 {                                                                                                            +
     "type": "feature",                                                                                       +
     "year": "1972",                                                                                          +
     "image": {                                                                                               +
         "url": "http://ia.media-imdb.com/images/M/MV5BMjEyMjcyNDI4MF5BMl5BanBnXkFtZTcwMDA5Mzg3OA@@._V1_.jpg",+
         "width": 1024,                                                                                       +
         "height": 500,                                                                                       +
         "quality": {                                                                                         +
             "copyright": "company X",                                                                        +
             "registered": true                                                                               +
         }                                                                                                    +
     },                                                                                                       +
     "title": "The Godfather",                                                                                +
     "rating": 9.2,                                                                                           +
     "tconst": "tt0068646",                                                                                   +
     "can_rate": true,                                                                                        +
     "num_votes": 1072605                                                                                     +
 }
(1 row)

"jsonb_set()" có lẽ là sự bổ sung quan trọng nhất trong các hàm JSON của Postgres9.5. Nó cho phép thay đổi dữ liệu trong kiểu dữ liệu JSONB. Nhớ rằng chúng ta mới chỉ sử dụng các giá trị đơn giản trong các ví dụ. Bạn có thể có các truy vấn con tạo các các giá trị mới và gán chúng vào các tài liệu con hoặc các mảng trong JSONB. 

Xem xét điều này

Tất cả điều trên dẫn đến một vị trí thú vị cho PostgreSQL. Các cải tiến JSON của PostgreSQL 9.5 cho phép bạn sử dụng PostgreSQL như một cơ sở dữ liệu JSON. Nó nhanh và đầy đủ chức năng. Cho dù bạn muốn xem xét những lựa chọn khác.

Ví dụ, các API tương đối dễ tiếp cận hoặc các thư viện client mà nhiều cơ sở dữ liệu JSON không có. Nơi PostgreSQL có cách thức đặc biệt của SQL để thao tác với JSON cái được sử dụng song song với phần còn lại của SQL để khai thác toàn bộ sức mạnh của nó. Điều này có nghĩa là bạn vẫn cần học SQL, một yêu cầu bắt buộc, tuy nhiên, có nhiều người sử dụng điều này làm lý do để chọn cơ sở dữ liệu "NoSQL".

Bạn có thể sử dụng PostgreSQL để tạo các tài liệu JSON/JONB phong phú và phức tạp trong cơ sở dữ liệu. Nếu bạn đang làm như vậy, bạn có thể muốn xem xét liệu mình có đang sử dụng PostgreSQL tốt hay không. Nếu sự phong phú và phức tạp của các tài liệu xuất phát từ sự liên quan của chúng với nhau thì mô hình quan hệ là sự lựa chọn tốt hơn mô hình dữ liệu cái có dữ liệu đan xen. Mô hình quan hệ cũng có lợi thế là nó xử lý yêu cầu mà không tạo ra một lượng lớn dữ liệu bị trùng lặp. Nó cũng có một thập kỷ để thẩm định và tối ưu.

Việc JSON được hỗ trợ PostgreSQL đã xóa bỏ các rào cản xử lý JSON trong môi trường SQL. Các tính năng mới trong phiên bản 9.5 cũng làm giảm các rào cản khác, bổ sung thêm khả năng truy cập, các hàm và các thao tác hiệu quả để chỉnh sửa các tài liệu JSONB.

PostgreSQL 9.5 không phải là cơ sở dữ liệu JSON tiếp theo của bạn, nhưng nó là một cơ sở dữ liệu quan hệ tuyệt vời với đầy đủ các tính năng để thao tác với JSON. Các cải tiến về JSON đi cùng với nhiều cải tiến khác ở phía quan hệ của cơ sở dữ liệu, "upsert", bỏ khóa và các bảng mẫu tốt hơn.

Với PostgreSQL bạn có thể sử dụng dữ liệu quan hệ và JSON cùng với nhau.

Bài viết được dịch từ:

https://compose.io/articles/could-postgresql-9-5-be-your-next-json-database/?utm_source=postgresweekly&utm_medium=email