JSON_REMOVE object in MySQL - mysql

I am trying to delete objects from my JSON array in MySQL.
I have a table called cart with two fields quote_id type int and items type json with the following row stored inside MySQL
quote_id: 0
items:
[
{
"a":42,
"b":"test4"
},
{
"a":32,
"b":"test3"
}
]
I am trying to create a query which would delete json objects from the json array. For example every
{
"a":32,
"b":"test3"
}
I have tried many queries. First I ended up with this:
UPDATE cart
SET items = IFNULL(JSON_REMOVE(items, JSON_UNQUOTE(JSON_SEARCH(items, 'one', 'test3'))), items)
WHERE quote_id = 13392;
However it just deletes "b":"test3" from the second object and left the "a":32 in it and I need a query that would find the whole object and would delete it.
This is my second query:
UPDATE cart
SET items = IFNULL(JSON_REMOVE(items, JSON_SEARCH(items, 'one', CAST('{"a": 32, "b": "test3"}' AS JSON))), items)
WHERE quote_id = 13392;
However I don't think the search on it works. I tried it without using the CAST()AS JSON, however it still did not work.
As I said I am pretty sure the problem is with the JSON_SEARCH, but maybe someone has the solution?
Thank you!

The JSON_SEARCH returns the path of the property, not the path to the object itself.
So you can use the following solution to get the object path with SUBSTR:
SELECT JSON_REMOVE(items,
SUBSTR(JSON_UNQUOTE(JSON_SEARCH(items, 'one', 'test3')), 1, LOCATE('.', JSON_UNQUOTE(JSON_SEARCH(items, 'one', 'test3')))-1)
) FROM cart
You can also use REGEXP_SUBSTR to get the object path:
SELECT JSON_REMOVE(items, REGEXP_SUBSTR(JSON_UNQUOTE(JSON_SEARCH(items, 'one', 'test3')), '^\\$\\[[0-9]+\\]'))
FROM cart
demo on dbfiddle.uk

Related

Wrap/Convert json object into array of objects MySQL

I have a column named data and I have to update its content from something like {} to [{}] for each record in table A, I tried to use JSON_ARRAY() but it gives me a quoted
["{\"something\": \"true\"}"]
but I'd like to have something like
[{ "something": "true" }]
How I do it now?
SELECT JSON_ARRAY(data) FROM A;
How should I update it either using JSON_SET() or UPDATE?
You need to use a path to get the data as JSON, rather than referring to the column by itself. The path $ means the top-level object.
update A
SET data = CASE
WHEN data IS NULL THEN '[]' -- NULL becomes empty array
WHEN LEFT(data, 1) = '[' THEN data -- leave existing array alone
ELSE JSON_ARRAY(data->"$") -- put object inside array
END
DEMO
Try using
SELECT JSON_ARRAY_AGG(JSON_OBJECT(data)) from A;

Using json_extract in sqlite to pull data from parent and child objects

I'm starting to explore the JSON1 library for sqlite and have been so far successful in the basic queries I've created. I'm now looking to create a more complicated query that pulls data from multiple levels.
Here's the example JSON object I'm starting with (and most of the data is very similar).
{
"height": 140.0,
"id": "cp",
"label": {
"bind": "cp_label"
},
"type": "color_picker",
"user_data": {
"my_property": 2
},
"uuid": "948cb959-74df-4af8-9e9c-c3cb53ac9915",
"value": {
"bind": "cp_color"
},
"width": 200.0
}
This json object is buried about seven levels deep in a json structure and I pulled it from the larger json construct using an sql statement like this:
SELECT value FROM forms, json_tree(forms.formJSON, '$.root')
WHERE type = 'object'
AND json_extract(value, '$.id') = #sControlID
// In this example, #sControlID is a variable that represents the `id` value we're looking for, which is 'cp'
But what I really need to pull from this object are the following:
the value from key type ("color_picker" in this example)
the values from keys bind ("cp_color" and "cp_label" in this example)
the keys value and label (which have values of {"bind":"<string>"} in this example)
For that last item, the key name (value and label in this case) can be any number of keywords, but no matter the keyword, the value will be an object of the form {"bind":"<some_string>"}. Also, there could be multiple keys that have a bind object associated with them, and I'd need to return all of them.
For the first two items, the keywords will always be type and bind.
With the json example above, I'd ideally like to retrieve two rows:
type key value
color_picker value cp_color
color_picker label cp_label
When I use json_extract methods, I end up retrieving the object {"bind":"cp_color"} from the json_tree table, but I also need to retrieve the data from the parent object. I feel like I need to do some kind of union, but my attempts have so far been unsuccessful. Any ideas here?
Note: if the {"bind":"<string>"} object doesn't exist as a child of the parent object, I don't want any rows returned.
Well, I was on the right track and eventually figured out it. I created a separate query for each of the items I was looking for, then INNER JOINed all the json_tree tables from each of the queries to have all the required fields available. Then I json_extracted the required data from each of the json fields I needed data from. In the end, it gave me exactly what I was looking for, though I'm sure it could be written more efficiently.
For anyone interested, this is what hte final query ended up looking like:
SELECT IFNULL(json_extract(parent.value, '$.type'), '_window_'), child.key, json_extract(child.value, '$.bind') FROM (SELECT json_tree.* FROM nui_forms, json_tree(nui_forms.formJSON, '$') WHERE type = 'object' AND json_extract(nui_forms.formJSON, '$.id') = #sWindowID) parent INNER JOIN (SELECT json_tree.* FROM nui_forms, json_tree(nui_forms.formJSON, '$') WHERE type = 'object' AND json_extract(value, '$.bind') != 'NULL' AND json_extract(nui_forms.formJSON, '$.id') = #sWindowID) child ON child.parent = parent.id;
If you have any tips on reducing its complexity, feel free to comment!

How do we modify an json array object regardless of its position?

The problem
Each entity owns an id and a json field. That json field simply stores a json list of objects.
Entity{ id, json }
"1, '[{"tag": "Player"}, {"position": {"x": 20, "y": 20}}]'"
The order of those json objects is not always the same and i want to update the json object inside the array where "tag" :"Player". I basically wanna change the tag.
I tried to use json_replace, but it didnt worked because it seems like that function does not accept the $** wildcard. But i cant use $[0] because that json object is not always at the first position. Thats what i tried.
UPDATE entity
SET jsonComponents = JSON_REPLACE(
jsonComponents ,
'$**.tag' ,
'NewTag'
)
WHERE
entity.id = 1
The Question
How are we supposed to modify/remove an json object inside an pure json list, if we dont know where its located at ? How can we modify/remove a json object inside a list regardless of its position inside the list ?
Im actually very glad for any help on this topic, couldnt find anything about it...
The solution
If we dont know the path of the json object we seek to modify... we simply query for the path using json_search
update entity
set jsonComponents = JSON_REPLACE(
jsonComponents,
JSON_UNQUOTE(json_search(jsonComponents, 'one', 'Player')),
'NewTag'
)
where entity.id = 0

Returning MySQL data as an OBJECT rather than an ARRAY (Knex)

Is there a way to get the output of a MySQL query to list rows in the following structure
{
1:{voo:bar,doo:dar},
2:{voo:mar,doo:har}
}
as opposed to
[
{id:1,voo:bar,doo:dar},
{id:2,voo:mar,doo:har}
]
which I then have to loop through to create the desired object?
I should add that within each row I am also concatenating results to form an object, and from what I've experimented with you can't group_concatenate inside a group_concatenation. As follows:
knex('table').select(
'table.id',
'table.name',
knex.raw(
`CONCAT("{", GROUP_CONCAT(DISTINCT
'"',table.voo,'"',':','"',table.doo,'"'),
"}") AS object`
)
.groupBy('table.id')
Could GROUP BY be leveraged in any way to achieve this? Generally I'm inexperienced at SQL and don't know what's possible and what's not.

MariaDB COLUMN_JSON query returns binary

I've been trying to use dynamic columns with an instance of MariaDB v10.1.12.
First, I send the following query:
INSERT INTO savedDisplays (user, name, body, dataSource, params) VALUES ('Marty', 'Hey', 'Hoy', 'temp', COLUMN_CREATE('type', 'tab', 'col0', 'champions', 'col1', 'averageResults'));
Where params' type was defined as a blob, just like the documentation suggests.
The query is accepted, the table updated. If I COLUMN_CHECK the results, it tells me it's fine.
But when I try to select:
"SELECT COLUMN_JSON(params) AS params FROM savedDisplays;
I get a {type: "Buffer", data: Array} containing binary returned to me, instead of the {"type":"tab", "col0":"champions", "col1":"averageResults"} I expect.
EDIT: I can use COLUMN_GET just fine, but I need every column inside the params field, and I need to check the type property first to know what kind of and how many columns there are in the JSON / params field. I could probably make it work still, but that would require multiple queries, as opposed to only one.
Any ideas?
Try:
SELECT CONVERT(COLUMN_JSON(params) USING utf8) AS params FROM savedDisplays
In MariaDB 10 this works at every table:
SELECT CONVERT(COLUMN_JSON(COLUMN_CREATE('t', text, 'v', value)) USING utf8)
as json FROM test WHERE 1 AND value LIKE '%12345%' LIMIT 10;
output in node.js
[ TextRow { json: '{"t":"test text","v":"0.5339044212345805"}' } ]