Skip to content
This repository has been archived by the owner on Feb 24, 2024. It is now read-only.

Commit

Permalink
WIP remove embedding disambiguation in favor of computed rels
Browse files Browse the repository at this point in the history
  • Loading branch information
laurenceisla committed Jul 14, 2023
1 parent c74b9c4 commit e284b4c
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 129 deletions.
8 changes: 8 additions & 0 deletions diagrams/employees.er
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[Employees]
*id
first_name
last_name
username
supervisor_id

Employees 1--* Employees
7 changes: 7 additions & 0 deletions diagrams/presidents.er
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[Presidents]
*id
first_name
last_name
predecessor_id

Presidents 1--? Presidents
Binary file added docs/_static/employees.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/_static/presidents.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
158 changes: 31 additions & 127 deletions docs/references/api/resource_embedding.rst
Original file line number Diff line number Diff line change
Expand Up @@ -821,61 +821,33 @@ Response:
}
.. _embed_disamb:

Embedding Disambiguation
------------------------

For doing resource embedding, PostgREST infers the relationship between two tables based on a foreign key between them.
However, in cases where there's more than one foreign key between two tables, it's not possible to infer the relationship unambiguously
by just specifying the tables names.

.. _hint_disamb:
.. _target_disamb:
.. _multiple_relationships:

Target Disambiguation
~~~~~~~~~~~~~~~~~~~~~
Multiple Relationships
----------------------

When there are multiple foreign keys between two or more tables, PostgREST does not infer the embedding directly.
You need to use :ref:`computed_relationships` in these cases.

For example, suppose you have the following ``orders`` and ``addresses`` tables:

.. image:: ../../_static/orders.png

And you try to embed ``orders`` with ``addresses`` (this is the **target**):

.. tabs::

.. code-tab:: http

GET /orders?select=*,addresses(*) HTTP/1.1

.. code-tab:: bash Curl

curl "http://localhost:3000/orders?select=*,addresses(*)" -i

Since the ``orders`` table has two foreign keys to the ``addresses`` table — an order has a billing address and a shipping address —
the request is ambiguous and PostgREST will respond with an error:

.. code-block:: http
HTTP/1.1 300 Multiple Choices
{..}
If this happens, you need to disambiguate the request by adding precision to the **target**.
Instead of the **table name**, you can specify the **foreign key constraint name** or the **column name** that is part of the foreign key.

Let's try first with the **foreign key constraint name**. To make it clearer we can name it:
To successfully embed ``orders`` with ``addresses``, create computed relationships for the foreign keys that you need to use:

.. code-block:: postgresql
ALTER TABLE orders
ADD CONSTRAINT billing_address foreign key (billing_address_id) references addresses(id),
ADD CONSTRAINT shipping_address foreign key (shipping_address_id) references addresses(id);
create function billing_address(orders) returns setof addresses rows 1 as $$
select * from addresses where id = $1.billing_address_id
$$ stable language sql;
-- Or if the constraints names were already generated by PostgreSQL we can rename them
-- ALTER TABLE orders
-- RENAME CONSTRAINT orders_billing_address_id_fkey TO billing_address,
-- RENAME CONSTRAINT orders_shipping_address_id_fkey TO shipping_address;
create function shipping_address(orders) returns setof addresses rows 1 as $$
select * from addresses where id = $1.shipping_address_id
$$ stable language sql;
Now we can unambiguously embed the billing address by specifying the ``billing_address`` foreign key constraint as the **target**.
For instance, now we can unambiguously embed the billing address by specifying the ``billing_address`` computed relationship.

.. tabs::

Expand All @@ -898,97 +870,33 @@ Now we can unambiguously embed the billing address by specifying the ``billing_a
}
]
Alternatively, you can specify the **column name** of the foreign key constraint as the **target**. This can be aliased to make
the result more clear.

.. tabs::

.. code-tab:: http

GET /orders?select=name,billing_address:billing_address_id(name) HTTP/1.1

.. code-tab:: bash Curl
.. _recursive_embed:

curl "http://localhost:3000/orders?select=name,billing_address:billing_address_id(name)"

.. code-block:: json
[
{
"name": "Personal Water Filter",
"billing_address": {
"name": "32 Glenlake Dr.Dearborn, MI 48124"
}
}
]
.. _hint_disamb:

Hint Disambiguation
~~~~~~~~~~~~~~~~~~~

If specifying the **target** is not enough for unambiguous embedding, you can add a **hint**. For example, let's assume we create
two views of ``addresses``: ``central_addresses`` and ``eastern_addresses``.

PostgREST cannot detect a view as an embedded resource by using a column name or foreign key name as targets, that is why we need to use the view name ``central_addresses`` instead. But, still, this is not enough for an unambiguous embed.

.. tabs::

.. code-tab:: http

GET /orders?select=*,central_addresses(*) HTTP/1.1

.. code-tab:: bash Curl

curl "http://localhost:3000/orders?select=*,central_addresses(*)" -i

.. code-block:: http
HTTP/1.1 300 Multiple Choices
For solving this case, in addition to the **target**, we can add a **hint**.
Here, we still specify ``central_addresses`` as the **target** and use the ``billing_address`` foreign key as the **hint**:

.. tabs::

.. code-tab:: http

GET /orders?select=*,central_addresses!billing_address(*) HTTP/1.1

.. code-tab:: bash Curl

curl 'http://localhost:3000/orders?select=*,central_addresses!billing_address(*)' -i

.. code-block:: http
HTTP/1.1 200 OK
[ ... ]
Similarly to the **target**, the **hint** can be a **table name**, **foreign key constraint name** or **column name**.
Recursive Embedding
-------------------

Hints also work alongside ``!inner`` if a top level filtering is needed. From the above example:
You need :ref:`computed_relationships` when embedding a resource with itself.

.. tabs::
.. _recursive_o2o_embed:

.. code-tab:: http
Recursive One-To-One Embedding
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

GET /orders?select=*,central_addresses!billing_address!inner(*)&central_addresses.code=AB1000 HTTP/1.1
.. image:: ../../_static/presidents.png

.. code-tab:: bash Curl
.. _recursive_o2m_embed:

curl "http://localhost:3000/orders?select=*,central_addresses!billing_address!inner(*)&central_addresses.code=AB1000"
Recursive One-To-Many Embedding
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. note::
.. image:: ../../_static/employees.png

If the relationship is so complex that hint disambiguation does not solve it, you can use :ref:`computed_relationships`.
.. _recursive_m2m_embed:

.. _recursive_m2m_disamb:
Recursive Many-To-Many Embedding
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Recursive Many-To-Many Disambiguation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Use hints plus the :ref:`spread_embed` to disambiguate a recursive many-to-many relationship. Having the following:
Having the following:

.. image:: ../../_static/users.png

Expand Down Expand Up @@ -1017,7 +925,3 @@ To get all the subscribers of a user:
]
}
]
.. note::

We're working on a better interface for recursive relationships to reduce the request verbosity.
4 changes: 2 additions & 2 deletions docs/references/errors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,8 @@ Related to a :ref:`stale schema cache <stale_schema>`. Most of the time, these e
| PGRST200 | | exist in the database. |
+---------------+-------------+-------------------------------------------------------------+
| .. _pgrst201: | 300 | An ambiguous embedding request was made. |
| | | See :ref:`embed_disamb`. |
| PGRST201 | | |
| | | See :ref:`multiple_relationships` and |
| PGRST201 | | :ref:`recursive_embed`. |
+---------------+-------------+-------------------------------------------------------------+
| .. _pgrst202: | 404 | Caused by a stale function signature, otherwise |
| | | the function may not exist in the database. |
Expand Down

0 comments on commit e284b4c

Please sign in to comment.