diff --git a/docs/ecosystem.rst b/docs/ecosystem.rst index 883db8bc0..10923ab15 100644 --- a/docs/ecosystem.rst +++ b/docs/ecosystem.rst @@ -14,6 +14,8 @@ Community Tutorials * `"CodeLess" backend using postgres, postgrest and oauth2 authentication with keycloak `_ - A step-by-step tutorial for using PostgREST with KeyCloak(hosted on a managed service). +* `How PostgreSQL triggers work when called with a PostgREST PATCH HTTP request `_ - A tutorial to see how the old and new values are set or not when doing a PATCH request to PostgREST. + .. _templates: Templates diff --git a/docs/how-tos/casting-type-to-custom-json.rst b/docs/how-tos/casting-type-to-custom-json.rst deleted file mode 100644 index b4687bad2..000000000 --- a/docs/how-tos/casting-type-to-custom-json.rst +++ /dev/null @@ -1,97 +0,0 @@ -Casting a type to a custom JSON object -====================================== - -:author: `steve-chavez `_ - -While using PostgREST you might have noticed that certain PostgreSQL types translate to JSON strings when you would -have expected a JSON object or array. For example, let's see the case of `range types `_. - -.. code-block:: postgres - - -- example taken from https://www.postgresql.org/docs/current/rangetypes.html#RANGETYPES-EXAMPLES - create table reservations ( - room int - , during tsrange - ); - - insert into - reservations - values - (1108, tsrange('2010-01-01 14:30', '2010-01-01 15:30')); - -Here we have a column named **during** as a ``tsrange`` type, we would like to get it as JSON through PostgREST. - -.. code-block:: bash - - curl "http://localhost:3000/reservations" - -Result: - -.. code-block:: json - - [ - { - "room":1108, - "during":"[\"2010-01-01 14:30:00\",\"2010-01-01 15:30:00\")" - } - ] - -The **during** value is probably not the in the format you want. We get a JSON string because by default PostgreSQL casts -the type to JSON by using its ``text`` representation. We can change this representation to a custom JSON object by `creating a CAST `_ . - -To do this, first we'll define the function that will do the conversion from ``tsrange`` to ``json``. - -.. code-block:: postgres - - create or replace function tsrange_to_json(tsrange) returns json as $$ - select json_build_object( - 'lower', lower($1) - , 'upper', upper($1) - , 'lower_inc', lower_inc($1) - , 'upper_inc', upper_inc($1) - ); - $$ language sql; - -Using this function we'll create the CAST. - -.. code-block:: postgres - - create cast (tsrange as json) with function tsrange_to_json(tsrange) as assignment; - -And we'll do the request and :ref:`cast the column `. - -.. code-block:: bash - - curl "http://localhost:3000/reservations?select=room,during::json" - -The result now is: - -.. code-block:: json - - [ - { - "room":1108, - "during":{ - "lower" : "2010-01-01T14:30:00", - "upper" : "2010-01-01T15:30:00", - "lower_inc" : true, - "upper_inc" : false - } - } - ] - -You can use the same idea for creating custom casts for different types. - -.. note:: - - If you don't want to modify casts for built-in types, an option would be to `create a custom type `_ - for your own ``tsrange`` and add its own cast. - - .. code-block:: postgres - - create type mytsrange as range (subtype = timestamp, subtype_diff = tsrange_subdiff); - - -- define column types and casting function analoguously to the above example - -- ... - - create cast (mytsrange as json) with function mytsrange_to_json(mytsrange) as assignment; diff --git a/docs/how-tos/embedding-table-from-another-schema.rst b/docs/how-tos/embedding-table-from-another-schema.rst deleted file mode 100644 index 6631982de..000000000 --- a/docs/how-tos/embedding-table-from-another-schema.rst +++ /dev/null @@ -1,81 +0,0 @@ -Embedding a table from another schema -===================================== - -:author: `steve-chavez `_ - -Suppose you have a **people** table in the ``public`` schema and this schema is exposed through PostgREST's :ref:`db-schemas`. - -.. code-block:: postgres - - create table public.people( - id int primary key - , full_name text - ); - -And you want to :ref:`embed ` the **people** table with a **details** table that's in another schema named ``private``. - -.. code-block:: postgres - - create schema if not exists private; - - -- For simplicity's sake the table is devoid of constraints/domains on email, phone, etc. - create table private.details( - id int primary key references public.people - , email text - , phone text - , birthday date - , occupation text - , company text - ); - - -- other database objects in this schema - -- ... - -- ... - -To solve this, you can create a view of **details** in the ``public`` schema. We'll call it **public_details**. - -.. code-block:: postgres - - create view public.public_details as - select - id - , occupation - , company - from - private.details; - -Since PostgREST supports :ref:`embedding_views`, you can embed **people** with **public_details**. - -Let's insert some data to test this: - -.. code-block:: postgres - - insert into - public.people - values - (1, 'John Doe'), (2, 'Jane Doe'); - - insert into - private.details - values - (1, 'jhon@fake.com', '772-323-5433', '1990-02-01', 'Transportation attendant', 'Body Fate'), - (2, 'jane@fake.com', '480-474-6571', '1980-04-21', 'Geotechnical engineer', 'Earthworks Garden Kare'); - -.. important:: - - Make sure PostgREST's schema cache is up-to-date. See :ref:`schema_reloading`. - -Now, make the following request: - -.. code-block:: bash - - curl "http://localhost:3000/people?select=full_name,public_details(occupation,company)" - -The result should be: - -.. code-block:: json - - [ - {"full_name":"John Doe","public_details":[{"occupation":"Transportation attendant","company":"Body Fate"}]}, - {"full_name":"Jane Doe","public_details":[{"occupation":"Geotechnical engineer","company":"Earthworks Garden Kare"}]} - ] diff --git a/docs/how-tos/working-with-postgresql-data-types.rst b/docs/how-tos/working-with-postgresql-data-types.rst index 921be2ba5..06276f6c7 100644 --- a/docs/how-tos/working-with-postgresql-data-types.rst +++ b/docs/how-tos/working-with-postgresql-data-types.rst @@ -79,6 +79,259 @@ You can use other comparative filters and also `PostgreSQL special date/time inp } ] +JSON +---- + +To work with a ``json`` type column, you can handle the value as a JSON object. For instance, let's use this table: + +.. code-block:: postgres + + create table products ( + id int primary key, + name text unique, + extra_info json + ); + +Now, you can insert a new product using a JSON object for the ``extra_info`` column: + +.. tabs:: + + .. code-tab:: http + + POST /products HTTP/1.1 + Content-Type: application/json + + { + "id": 1, + "name": "Canned fish", + "extra_info": { + "expiry_date": "2025-12-31", + "exportable": true + } + } + + .. code-tab:: bash Curl + + curl "http://localhost:3000/products" \ + -X POST -H "Content-Type: application/json" \ + -d @- << EOF + { + "id": 1, + "name": "Canned fish", + "extra_info": { + "expiry_date": "2025-12-31", + "exportable": true + } + } + EOF + +To query and filter the data see :ref:`json_columns` for a complete reference. + +Composite Types +--------------- + +With PostgREST, you have two options to handle `composite type columns `_. On one hand you can use string representation and on the other you can handle it as you would a JSON column. Let's create a type and a table for this example: + +.. code-block:: postgres + + create type dimension as ( + length decimal(6,2), + width decimal (6,2), + height decimal (6,2), + unit text + ); + + create table products ( + id int primary key, + size dimension + ); + + insert into products (id, size) + values (1, '(5.0,5.0,10.0,"cm")'); + +Now, you could insert values using string representation as seen in the example above. + +.. tabs:: + + .. code-tab:: http + + POST /products HTTP/1.1 + Content-Type: application/json + + { "id": 2, "size": "(0.7,0.5,1.8,\"m\")" } + + .. code-tab:: bash Curl + + curl "http://localhost:3000/products" \ + -X POST -H "Content-Type: application/json" \ + -d @- << EOF + { "id": 2, "size": "(0.7,0.5,1.8,\"m\")" } + EOF + +Or, you could insert the data in JSON format. The following request is equivalent to the previous one: + +.. tabs:: + + .. code-tab:: http + + POST /products HTTP/1.1 + Content-Type: application/json + + { + "id": 2, + "size": { + "length": 0.7, + "width": 0.5, + "height": 1.8, + "unit": "m" + } + } + + .. code-tab:: bash Curl + + curl "http://localhost:3000/products" \ + -X POST -H "Content-Type: application/json" \ + -d @- << EOF + { + "id": 2, + "size": { + "length": 0.7, + "width": 0.5, + "height": 1.8, + "unit": "m" + } + } + EOF + +You can also query data using the arrow operators as you would for :ref:`JSON columns `. + +Ranges +------ + +To illustrate how to work with `ranges `_, let's use the following table as an example: + +.. code-block:: postgres + + create table events ( + id int primary key, + name text unique, + duration tsrange + ); + +Now, to insert a new event, specify the ``duration`` value as a string representation of the ``tsrange`` type, for example: + +.. tabs:: + + .. code-tab:: http + + POST /events HTTP/1.1 + Content-Type: application/json + + { + "id": 1, + "name": "New Year's Party", + "duration": "['2022-12-31 11:00','2023-01-01 06:00']" + } + + .. code-tab:: bash Curl + + curl "http://localhost:3000/events" \ + -X POST -H "Content-Type: application/json" \ + -d @- << EOF + { + "id": 1, + "name": "New Year's Party", + "duration": "['2022-12-31 11:00','2023-01-01 06:00']" + } + EOF + +You can use range :ref:`operators ` to filter the data. But what if you need get the events for the New Year 2023? Doing this filter ``events?duration=cs.2023-01-01`` will return an error because PostgreSQL needs an explicit cast to timestamp of the string value. A workaround would be to use a range starting and ending in the same date, like this: + +.. tabs:: + + .. code-tab:: http + + GET /events?duration=cs.[2023-01-01,2023-01-01] HTTP/1.1 + + .. code-tab:: bash Curl + + curl "http://localhost:3000/events?duration=cs.\[2023-01-01,2023-01-01\]" + +.. code-block:: json + + [ + { + "id": 1, + "name": "New Year's Party", + "duration": "[\"2022-12-31 11:00:00\",\"2023-01-01 06:00:00\"]" + } + ] + +.. _casting_range_to_json: + +Casting a Range to a JSON Object +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As you may have noticed, the ``tsrange`` value is returned as a string literal. To return it as a JSON value, first you need to create a function that will do the conversion from a ``tsrange`` type: + +.. code-block:: postgres + + create or replace function tsrange_to_json(tsrange) returns json as $$ + select json_build_object( + 'lower', lower($1) + , 'upper', upper($1) + , 'lower_inc', lower_inc($1) + , 'upper_inc', upper_inc($1) + ); + $$ language sql; + +Then, create the cast using this function: + +.. code-block:: postgres + + create cast (tsrange as json) with function tsrange_to_json(tsrange) as assignment; + +Finally, do the request :ref:`casting the range column `: + +.. tabs:: + + .. code-tab:: http + + GET /events?select=id,name,duration::json HTTP/1.1 + + .. code-tab:: bash Curl + + curl "http://localhost:3000/events?select=id,name,duration::json" + +.. code-block:: json + + [ + { + "id": 1, + "name": "New Year's Party", + "duration": { + "lower": "2022-12-31T11:00:00", + "upper": "2023-01-01T06:00:00", + "lower_inc": true, + "upper_inc": true + } + } + ] + +.. note:: + + If you don't want to modify casts for built-in types, an option would be to `create a custom type `_ + for your own ``tsrange`` and add its own cast. + + .. code-block:: postgres + + create type mytsrange as range (subtype = timestamp, subtype_diff = tsrange_subdiff); + + -- define column types and casting function analogously to the above example + -- ... + + create cast (mytsrange as json) with function mytsrange_to_json(mytsrange) as assignment; + hstore ------ diff --git a/docs/index.rst b/docs/index.rst index ea08aa3ad..9448cd089 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -204,10 +204,7 @@ These are recipes that'll help you address specific use-cases. how-tos/* -- :doc:`how-tos/casting-type-to-custom-json` -- :doc:`how-tos/embedding-table-from-another-schema` - :doc:`how-tos/providing-images-for-img` -- `How PostgreSQL triggers work when called with a PostgREST PATCH HTTP request `_ - :doc:`how-tos/working-with-postgresql-data-types` Ecosystem