Stranger
Stranger

Reputation: 10610

How to search within MySQL JSON object array?

Consider the following JSON object,

[
  {
    "id": 5964460916832,
    "name": "Size",
    "value": "Small",
    "position": 1,
    "product_id": 4588516409440
  },
  {
    "id": 5964460916833,
    "name": "Size",
    "value": "Medium",
    "position": 2,
    "product_id": 4588516409440
  },
  {
    "id": 5964460916834,
    "name": "Size",
    "value": "Large",
    "position": 3,
    "product_id": 4588516409440
  }
]

This is a value present in a table field called custom_attrs of JSON data type in a MySQL 8.0 table. I wanted to search the JSON data to match with multiple fields in the same object.

For example,

I wanted to see if there's a match for name "Size" and value "Medium" within the same object. It should not match the name in the first object and the value in the second object.

While we can always use JSON table, I don't prefer that due to the complexities it brings during the JOINs.

JSON_SEARCH supports LIKE operator, but it cannot ensure if it's from the same object JSON_CONTAINS supports multiple fields but not LIKE as follows,

SET @doc = CAST('[{"id":5964460916832,"name":"Size","value":"Small","position":1,"product_id":4588516409440},{"id":5964460916833,"name":"Size","value":"Medium","position":2,"product_id":4588516409440},{"id":5964460916834,"name":"Size","value":"Large","position":3,"product_id":4588516409440}]' AS JSON);
SELECT JSON_CONTAINS(@doc, '{"name":"Size", "value":"Small"}')

Is there any way to get the same JSON_CONTAINS like functionality with partial search like, {"name":"Size", "value":"%sma%"}

Any help on this would be greatly helpful.

Upvotes: 0

Views: 2946

Answers (1)

Bill Karwin
Bill Karwin

Reputation: 562230

JSON_CONTAINS() only works with equality, not with pattern matching.

The JSON_TABLE() function is the solution intended to address the task you are trying to do. But you said you don't want to use it.

You can simulate JSON_TABLE() using other functions.

select * from (
  select 
   json_unquote(json_extract(col, concat('$[',n.i,'].id'))) as `id`,
   json_unquote(json_extract(col, concat('$[',n.i,'].name'))) as `name`,
   json_unquote(json_extract(col, concat('$[',n.i,'].value'))) as `value`
  from (select @doc as col) j
  cross join (select 0 as i union select 1 union select 2 union select 3 union select 4 union select 5 ...) as n
) as t
where t.`id` is not null
order by id, `name`;

Output:

+---------------+------+--------+
| id            | name | value  |
+---------------+------+--------+
| 5964460916832 | Size | Small  |
| 5964460916833 | Size | Medium |
| 5964460916834 | Size | Large  |
+---------------+------+--------+

You could then easily add a condition like AND value LIKE '%sma%'.

As you can see, this query is even more complex than if you had used JSON_TABLE().

Really, any solution is going to be complex when you store your data in JSON format, then try to use SQL expressions and relational operations to query them as if they are normalized data. This is because you're practically implementing a mini-database within the functions of a real database. This is sometimes called the Inner-Platform Effect:

The inner-platform effect is the tendency of software architects to create a system so customizable as to become a replica, and often a poor replica, of the software development platform they are using. This is generally inefficient and such systems are often considered to be examples of an anti-pattern.

If you want simple queries, you should store data in normal rows and columns, not in JSON. Then you could get your result using quite ordinary SQL:

SELECT id, name, value FROM MyTable WHERE name = 'Size' AND value LIKE '%sma%';

Upvotes: 1

Related Questions