How to update a text[] field with a string - postgresql

I have a table with a field called tags which can contain any number of strings:
Table "public.page"
Column | Type | Modifiers
----------------------+--------------------------+----------------------------------
tags | text[] | not null default ARRAY[]::text[]
I want to add a string to the tags field - but I can't seem to get the concat function to work for me. I've tried:
update page set tags=concat('My New String',tags);
ERROR: function concat(unknown, text[]) does not exist
LINE 1: update page set tags=concat('My New String',tags) where ...
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
and
update page set tags=('My New String'||tags);
ERROR: operator is not unique: unknown || text[]
LINE 1: update page set tags = ('My New String' || tags) where w...
^
HINT: Could not choose a best candidate operator. You might need to add explicit type casts.
Any ideas?

In PostgreSQL's type system, the literal 'My New String' is not a varchar or text value, but a literal of type unknown, which can be processed as any type. (For instance, the literal for a date could be '2013-08-29'; this would not be processed as a varchar and then converted to date, it would be interpreted as a "date literal" at a very low level.)
Often, PostgreSQL can deduce the type automatically, but when it can't, you need to use one of the following to tell it that you want the literal to be treated as text:
text 'My New String' (SQL standard literal syntax)
Cast('My New String' as text) (SQL standard cast syntax, but not really a cast in this context)
'My New String'::text (PostgreSQL non-standard cast syntax, but quite readable)
In your case , the error message operator is not unique: unknown || text[] is saying that there are multiple types that Postgres could interpret the literal as, each with their own definition of the || operator.
You therefore need something like this (I've removed the unnecessary parentheses):
update page set tags = 'My New String'::text || tags;

Did you try || to concatenate?
select array['abc','def']::text[] || 'qwerty'::text;
http://www.postgresql.org/docs/current/static/functions-array.html#ARRAY-OPERATORS-TABLE
Note: this answer was in response to the OP's original (unedited) question. Other answers contain more detail relevant to the updated question.

Related

How to update a jsonb column with a replaced value in pgAdmin?

I have a PostgreSQL table called files which includes a jsonb table called formats. While some rows are [null], others have objects with this structure:
{
"thumbnail": {
"ext": ".jpg",
"url": "https://some-url.com/image01.jpg",
"name": "image01.jpg",
//...other properties
}
}
For every row I want to update the thumbnail.url and replace some-url with other-url.
I'm far from being an expert in PostgreSQL (or any other DB for that matter), and after some reading I tried to run the following query in pgAdmin:
UPDATE files
SET formats = jsonb_set(formats, '{thumbnail.url}', REPLACE('{thumbnail.url}', 'some-url', 'other-url'))
And I received this error: function jsonb_set(jsonb, unknown, text) does not exist
I tried to set format jsonb_set(formats::jsonb...), tried to target '{thumbnail}' instead of '{thumbnail.url}' - always the same error.
What am I doing wrong? Or is pgAdmin really doesn't support this function? How can I do such an update with pgAdmin query tool?
We can try to use ->> to get JSON content value of url and then replace your expect value from that.
Because your url field of your JSON might be string type we need to use " to content it before cast as JSONB
jsonb_set(target jsonb, path text[], new_value jsonb [, create_missing boolean])
UPDATE files
SET formats = jsonb_set(formats, '{thumbnail,url}', CONCAT('"',REPLACE(formats->'thumbnail'->>'url','some-url','other-url'),'"')::JSONB);
sqlfiddle
The second parameter of jsonb_set() must be an array with one array element for each "path" element. So the second parameter should be '{thumbnail,url}' or more obvious: array['thumbnail', 'url']
And the third parameter must be a jsonb value, but replace returns a text, so you need to use e.g. to_jsonb() to convert the result of the replace() to a jsonb value.
And as D-Shih pointed out, you need to extract the old value using ->>. But to get the URL you need to "navigate" to it: formats -> 'thumbnail ->> 'url'
I would also add a WHERE clause so that you only update rows that actually contain a URL.
UPDATE files
SET formats = jsonb_set(formats,
'{thumbnail,url}',
to_jsonb(replace(formats -> 'thumbnail' ->> 'url', 'some-url', 'other-url'))
)
where (formats -> 'thumbnail') ? 'url'

Text and jsonb concatenation in a single postgresql query

How can I concatenate a string inside of a concatenated jsonb object in postgresql? In other words, I am using the JSONb concatenate operator as well as the text concatenate operator in the same query and running into trouble.
Or... if there is a totally different query I should be executing, I'd appreciate hearing suggestions. The goal is to update a row containing a jsonb column. We don't want to overwrite existing key value pairs in the jsonb column that are not provided in the query and we also want to update multiple rows at once.
My query:
update contacts as c set data = data || '{"geomatch": "MATCH","latitude":'||v.latitude||'}'
from (values (16247746,40.814140),
(16247747,20.900840),
(16247748,20.890570)) as v(contact_id,latitude) where c.contact_id = v.contact_id
The Error:
ERROR: invalid input syntax for type json
LINE 85: update contacts as c set data = data || '{"geomatch": "MATCH...
^
DETAIL: The input string ended unexpectedly.
CONTEXT: JSON data, line 1: {"geomatch": "MATCH","latitude":
SQL state: 22P02
Character: 4573
You might be looking for
SET data = data || ('{"geomatch": "MATCH","latitude":'||v.latitude||'}')::jsonb
-- ^^ jsonb ^^ text ^^ text
but that's not how one should build JSON objects - that v.latitude might not be a valid JSON literal, or even contain some injection like "", "otherKey": "oops". (Admittedly, in your example you control the values, and they're numbers so it might be fine, but it's still a bad practice). Instead, use jsonb_build_object:
SET data = data || jsonb_build_object('geomatch', 'MATCH', 'latitude', v.latitude)
There are two problems. The first is operator precedence preventing your concatenation of a jsonb object to what is read a text object. The second is that the concatenation of text pieces requires a cast to jsonb.
This should work:
update contacts as c
set data = data || ('{"geomatch": "MATCH","latitude":'||v.latitude||'}')::jsonb
from (values (16247746,40.814140),
(16247747,20.900840),
(16247748,20.890570)) as v(contact_id,latitude)
where c.contact_id = v.contact_id
;

How to compare character varying (varcar) to UUID in PostgreSQL?

Operator does not exist: character varying = uuid
Client id is UUId and should be why it is not working.
Where I am wrong, since I have tried almost everything I imagined.
SELECT * FROM "cobranca_assinatura"
INNER JOIN "cadastro_cliente" ON ("cobranca_assinatura"."cliente_id" = "cadastro_cliente"."id")
WHERE "cadastro_cliente"."nome" LIKE marcelo% ESCAPE '\'
[2019-03-21 14:40:34] [42883] ERROR: operator does not exist:
character varying = uuid [2019-03-21 14:40:34]
Dica: No operator
matches the given name and argument type(s). You might need to add
explicit type casts.
uuid is a specific datatype. To you it looks like text, but it's not. You cannot compare uuid using string functions (uuid like "abc%"), or compare it with text.
As Tamer suggests, you can cast it first, if you need to compare.
SELECT *
FROM (SELECT 'A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11'::uuid as my_uuid) foo
WHERE my_uuid::text like 'a%'
For example, above I create a uuid by casting a string to uuid type. (You'll fail if you attempt to cast just any old string to uuid because 'abc' cannot be a uuid).
Then with a uuid item called 'my_uuid', I cast to back to a string to use string comparison. (Note the 'A' becomes 'a'!)
In java -> spring boot for JDBC template. I got the same issue. This is type-mismatch error, it's expecting UUID datatype but supplying String.
So, I converted UUID from String using UUID.fromString() and using UUID type in prepared statement (SQL)
Example:
String testSelectQry = "SELECT * from facility_announcements where id=:announcementID";
SqlParameterSource params = new MapSqlParameterSource("announcementID", UUID.fromString("094b76da-4140-11eb-b139-0242ac11000f"));
namedParameterJdbcTemplate.query(testSelectQry, params, new FacilityAnnouncementMapper());

Postgres: create index on attribute of attribute in JSONB column?

I'm working in Postgres 9.6.5. I have the following table:
id | integer
data | jsonb
The data in the data column is nested, in the form:
{ 'identification': { 'registration_number': 'foo' }}
I'd like to index registration_number, so I can query on it. I've tried this (based on this answer):
CREATE INDEX ON mytable((data->>'identification'->>'registration_number'));
But got this:
ERROR: operator does not exist: text ->> unknown
LINE 1: CREATE INDEX ON psc((data->>'identification'->>'registration... ^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
What am I doing wrong?
You want:
CREATE INDEX ON mytable((data -> 'identification' ->> 'registration_number'));
The -> operator returns the jsonb object under the key, and the ->> operator returns the jsonb object under the key as text. The most notable difference between the two operators is that ->> will "unwrap" string values (i.e. remove double quotes from the TEXT representation).
The error you're seeing is reported because data ->> 'identification' returns text, and the subsequent ->> is not defined for the text type.
Since version 9.3 Postgres has the #> and #>> operators. This operators allow the user to specify a path (using an array of text) inside jsonb column to get the value.
You could use this operator to achieve your goal in a simpler way.
CREATE INDEX ON mytable((data #>> '{identification, registration_number}'));

Set numeric column to equal formatted varchar currency column in PostgreSQL

I have a VARCHAR(1000) column of prices with dollar signs (e.g. $100) and I have created a new NUMERIC(15,2) column, which I'd like to set equal to the prices in the VARCHAR column.
This is what worked for me in MySQL:
UPDATE product_table
SET cost = REPLACE(REPLACE(price, '$', ''), ',','');
but in PostgreSQL it throws an error:
ERROR: column "cost" is of type numeric but expression is of type character
LINE 2: SET cost = REPLACE(REPLACE(price, '$', ''), ',','');
^
HINT: You will need to rewrite or cast the expression.
I tried to follow the hint and tried some Google searches for examples, but my small brain hasn't been able to figure it out.
In PostgreSQL you can do this in one swoop, rather than replacing '$' and ',' s in separate calls:
UPDATE product_table
SET cost = regexp_replace(price, '[$,]', '', 'g')::numeric(15,2);
In regexp_replace the pattern [$,] means to replace either of '$' or ',' with the replace string (the empty string '' in this case), and the 'g' flag indicates that all such patterns need to be replaced.
Then you need to cast the resulting string to a numeric(15,2) value.
Simply cast the result of REPLACE with cast .. as numeric.
Try this:
UPDATE product_table
SET cost = CAST(REPLACE(REPLACE(price, '$', ''), ',','') AS NUMERIC);
I wouldn't suggest having this table structure though, because it can lead to anomalies (cost value doesn't reflect the price value).