I'm doing a cross join lateral on an jsonb array and I'm looking to get the row_number (or its equivalent) for the array elements. looking at the row_number docs I see that I'd need to do a "order by" in addition to the "partition by", but there's not really a sort criterion I could use -- arrays just have a set order, and I need to retrieve the index into the array along with the rest of the data.
The client table will have entries like this
{
  "id": "cj49q33oa000",
  "email": {
    "address": "",
    "after": "2016-06-28T12:28:58.016Z",
    "error": "Et corporis sed."
  },
  "name": "Arnold Schinner",
  "birthdate": "2016-07-29T05:09:33.693Z",
  "status": "paused",
  "sex": "f",
  "waist": [
    {
      "completed": "2017-06-23T10:37:37.500Z"
    },
    {
      "planned": "2017-06-23T10:37:37.500Z"
    },
    {
      "planned": "2017-06-23T10:37:37.500Z"
    },
    {
      "planned": "2017-06-23T10:37:37.500Z"
    }
  ]
}
and I'd run a query like
SELECT client->>'id' AS id, waist.planned
  FROM clients
 CROSS JOIN LATERAL JSONB_TO_RECORDSET(client->'waist') 
                 AS waist(planned TIMESTAMP WITH TIME ZONE)
 WHERE waist.planned IS NOT NULL
but I need to get waist.position_in_array in some way.
Use the function jsonb_array_elements(...) with ordinality.
select 
    client->>'id' as id, 
    (value->>'planned')::timestamptz as planned, 
    ordinality
from clients
cross join lateral jsonb_array_elements(client->'waist') with ordinality
where value->>'planned' is not null;
      id      |         planned          | ordinality 
--------------+--------------------------+------------
 cj49q33oa000 | 2017-06-23 12:37:37.5+02 |          2
 cj49q33oa000 | 2017-06-23 12:37:37.5+02 |          3
 cj49q33oa000 | 2017-06-23 12:37:37.5+02 |          4
(4 rows)
Db<>fiddle.
You can use the ROWS FROM syntax to combine json_to_recordset and jsonb_to_recordset with WITH ORDINALITY.
The documentation as back as 9.5 says
ROWS FROM( function_call [, ... ] ) [WITH ORDINALITY] [[AS] table_alias [(column_alias [, ... ])]]
So this works (tested on 12 but should work at least on all versions >=9.5)
WITH my_json AS 
(
  SELECT '[{"id":1, "name":"somename", "bool":true},
           {"id":2, "name":null, "bool":false}]'::json jsn
)
SELECT jsn_with_ordinality.*
  FROM my_json,
  ROWS FROM (json_to_recordset(jsn) AS (id int, name TEXT, bool boolean)) 
  WITH ORDINALITY jsn_with_ordinality;
Results in:
| id | name | bool | ordinality | 
|---|---|---|---|
| 1 | somename | true | 1 | 
| 2 | false | 2 | 
I do wonder however if it's any safer than just adding a row_number() over()...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With