Add jsonb property of type boolean - postgresql

I'm trying to add a property to an existing jsonb column (column "data").
I want have my jsonb document to log like this
{
// ... existing properties
"Filed": false // new property
}
I tried
UPDATE "doc" SET "data" = jsonb_set("data"::jsonb, 'Filed', false, true)
I get this error:
[42883] ERROR: function jsonb_set(jsonb, unknown, boolean, boolean) does not exist
Hint: No function matches the given name and argument types.
You might need to add explicit type casts. Position: 46

It should be
jsonb_set("data"::jsonb, '{Filed}', 'false', TRUE)
The second parameter is an array denoting the path to the appropriate key, and 'false' is the string representation of a JSON boolean.

Better use the || operator.
UPDATE "doc" SET "data" = "data" || '{"Filed": false}';
This one is equivalent but more suitable for parameterization:
UPDATE "doc" SET "data" = "data" || jsonb_build_object('Filed', false);

Related

How to filter data through WHERE json statement - postgres

structure of column is> SELECT "startedByAttendanceEntry" FROM "attendance_block"
[
{
person: {
firstName: "Koste";
lastName: "Litaci";
};
}
...
]
Data type is json, I can't change type or any of the structure of db
What I want to achieve is to select only elements with person firstName = Koste AND lastName = Litaci
what I tried
SELECT "startedByAttendanceEntry"
FROM "attendance_block"
WHERE 'person' ->> 'lastName' = 'Litaci'
AND 'person' ->> 'firstName' = 'Koste'
and many more all end it with err saying driverError: error: operator is not unique: unknown ->> unknown
Well, 'person' is a varchar/text constant, and thus you cannot apply a JSON operator on it. Additionally your JSON contains an array, so access through ->> won't work either because you need to specify the array index.
You can use the contains operator #> to find a specific key/value pair in the array. As you chose to not use the recommended jsonb type, you need to cast the column:
where "startedByAttendanceEntry"::jsonb #> '[{"person":{"firstName": "Koste", "lastName": "Litaci"}}]'
TOPIC AUTHOR EDIT:
all the code to work was as follow
SELECT "startedByAttendanceEntry"
FROM "attendance_block"
WHERE "startedByAttendanceEntry"::jsonb #> '{"person":{"firstName": "Koste", "lastName": "Litaci"}}'

Postgresql - querying jsonb throws a syntax error

I have a table that has a column data of jsonb type.
create table event
(
id bigserial
primary key,
created_at timestamp with time zone default now() not null,
type text not null,
created_by text,
data jsonb,
event_time timestamp with time zone default now() not null
);
In that field I am saving a json object that looks like this:
{
"comment": "Changed by recipient",
"source": "Recipient page"
}
I would like to query values in that table by the value of the comment property of the data json object. Something like this in based by examples [here][1]:
select * from event
where type = 'pickup-data-changed'
and data -> 'comment' = 'Changed by recipient'
If I query like that I get an invalid token error:
[22P02] ERROR: invalid input syntax for type json Detail: Token "Changed" is invalid. Position: 104
What am I doing wrong here?
If I do it as a double arrow like suggested in the comments:
select * from event
where type = 'pickup-data-changed'
and data ->-> 'comment' = 'Changed by recipient'
I get an error:
[42883] ERROR: operator does not exist: jsonb ->-> unknown Hint: No operator matches the given name and argument types. You might need to add explicit type casts.
How can I make this query work?
[1]: https://kb.objectrocket.com/postgresql/how-to-query-a-postgres-jsonb-column-1433
I get an invalid token error. What am I doing wrong here?
data -> 'comment' returns a value of type jsonb, so the right hand side of the comparison 'Changed by recipient' is parsed as JSON as well - and it's invalid JSON. To create a JSON string value to compare against, you'd need to write
… data -> 'comment' = '"Changed by recipient"'
If I do it as a double arrow like suggested in the comments, data ->-> 'comment'
The comments suggested
… data ->> 'comment' = 'Changed by recipient'
not ->->.
alternatives:
select * from event
where type = 'pickup-data-changed'
and data -> 'comment' = '"Changed by recipient"'::jsonb;
or
select * from event
where type = 'pickup-data-changed'
and data['comment'] = '"Changed by recipient"'::jsonb;

Call jsonb_contains function (postgres) using JPA criteria and JsonBinaryType

I have a JPA/Hibernate entity which has a JSONB column (using https://github.com/vladmihalcea/hibernate-types ) for storing a list of strings. This works fine so far.
#TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
#Type(type = "jsonb")
#Column(name = "TAGS", columnDefinition = "jsonb")
private List<String> tags;
Now I want to check if another string is contained in the list of strings.
I can do this by writing a native query and use the #> operator from Postgres. Because of other reasons (the query is more complex) I do not want to go in that direction. My current approach is calling the jsonb_contains method in a Spring Data specification (since the operator is just alias to this function), e.g. jsonb_contains('["tag1", "tag2", "tag3"]','["tag1"]'). What I am struggling with is, getting the second parameter right.
My initial approach is to also use a List of Strings.
public static Specification<MyEntity> hasTag(String tag) {
return (root, query, cb) -> {
if (StringUtils.isEmpty(tag)) {
return criteriaBuilder.conjunction();
}
Expression<Boolean> expression = criteriaBuilder.function("jsonb_contains",
Boolean.class,
root.get("tags"),
criteriaBuilder.literal(List.of(tag)));
return criteriaBuilder.isTrue(expression);
};
}
This results in the following error.
Caused by: org.postgresql.util.PSQLException: ERROR: function jsonb_contains(jsonb, character varying) does not exist
Hinweis: No function matches the given name and argument types. You might need to add explicit type casts.
Position: 375
It does know that root.get("tags") is mapped to JSONB but for the second parameter it does not. How can I get this right? Is this actually possible?
jsonb_contains(jsob, jsonb) parameters must be jsonb type.
You can not pass a Java String as a parameter to the function.
You can not do casting in Postgresql via JPA Criteria.
Using JSONObject or whatever does not help because Postgresql sees it as
bytea type.
There are 2 possible solutions:
Solution 1
Create jsonb with jsonb_build_object(text[]) function and send it to jsonb_contains(jsonb, jsonb) function:
public static Specification<MyEntity> hasTag(String tag) {
// get List of key-value: [key1, value1, key2, value2...]
List<Object> tags = List.of(tag);
// create jsonb from array list
Expression<?> jsonb = criteriaBuilder.function(
"jsonb_build_object",
Object.class,
cb.literal(tags)
);
Expression<Boolean> expression = criteriaBuilder.function(
"jsonb_contains",
Boolean.class,
root.get("tags"),
jsonb
);
return criteriaBuilder.isTrue(expression);
}
Solution 2
Create custom function in your Postgresql and use it in Java:
SQL:
CREATE FUNCTION jsonb_contains_as_text(a jsonb, b text)
RETURNS BOOLEAN AS $$
SELECT CASE
WHEN a #> b::jsonb THEN TRUE
ELSE FALSE
END;$$
LANGUAGE SQL IMMUTABLE STRICT;
Java Code:
public static Specification<MyEntity> hasTag(String tag) {
Expression<Boolean> expression = criteriaBuilder.function(
"jsonb_contains_as_text",
Boolean.class,
root.get("tags"),
criteriaBuilder.literal(tag)
);
return criteriaBuilder.isTrue(expression);
}
I think that the reason is that you pass the varchar as the second param. jsonb_contains() requires two jsonb params.
To check a jsonb array contains all/any values from a string array you need to use another operators: ?& or ?|.
The methods bindings for them in PSQL 9.4 are: jsonb_exists_all and jsonb_exists_any correspondingly.
In your PSQL version, you could check it by the following command:
select * from pg_operator where oprname = '?&'

Entity Framework Core PostgreSQL EF.Functions.JsonTypeof

.Where(e => EF.Functions.JsonTypeof(e.Customer.GetProperty("Age")))
This syntax in https://www.npgsql.org/efcore/mapping/json.html?tabs=data-annotations%2Cjsondocument not works!! show this error: cannot implicitly convert "string" to "bool"
The sample syntax from the link is simply incomplete. JsonTypeof returns string an according to the link is mapped to jsonb_typeof which
Returns the type of the outermost JSON value as a text string. Possible types are object, array, string, number, boolean, and null.
So the correct sample usage in Where clause would compare the return value of the method to a string containing one of the aforementioned values, for instance
.Where(e => EF.Functions.JsonTypeof(e.Customer.GetProperty("Age")) == "number")

How to insert empty array into jsonb column (pgsql) by Yii2?

Created a migration with a new field of jsonb type, not null and default value = []. (example of stored data: ["235", "214"]) and add a rule to model [['unique_users'], 'safe']
public function up()
{
$connection = Yii::$app->getDb();
$sql = 'ALTER TABLE offer ADD unique_users jsonb not null default \'[]\'';
$command = $connection->createCommand($sql, []);
$command->queryAll();
}
Result: Added a unique_users field with a default value [] to each row. jsonb_typeof(unique_users) returns an array type.
Created needed query for test
select jsonb_array_length(unique_users) from test where unique_users #> '"19"'::jsonb
Result from PgAdmin:
It seemed that everything was ready. But after saving a new record with Yii2, I received a query error:
ERROR: you can not get the length of a scalar
And I saw that another value was recorded in the field - ""
I was tryed to add the validation rule to Model: ['unique_users', 'default', 'value' => '[]'],.
Result:
...with the same problem of query - value is not an array. jsonb_typeof(unique_users) returns an string type.
How to insert empty array into jsonb column?
I think you're accidentally sending an empty string as the value for your unique_users field. If the value would be completely empty it should take the default DB value for the column. Please make sure the unique_users field is completely empty (null) when saving.
You can however also do this with a default value rule. This should do the trick:
['unique_users', 'default', 'value' => json_encode([])],
['unique_users', 'default', 'value' => []],