Search a JSON column in a database - sql

I'm looking to see if it's possible to search multiple database rows for a specific value that's stored in a json string. For instance I have a table called stashitems that contains a json column items that stores all of a players items. I would like to search the 1500 rows of data for a specific label or name value. Below is a snippet of one players stashitems.
How could I accomplish this for the entire table? Thanks for any help!
{
"8": {
"type": "item",
"slot": 8,
"amount": 948,
"weight": 100,
"name": "glass",
"label": "Glass",
"image": "glass.png",
"useable": false,
"unique": false,
"info": ""
},
"23": {
"type": "item",
"slot": 23,
"amount": 1,
"weight": 200,
"name": "crack_baggy",
"label": "Bag of Crack",
"image": "crack_baggy.png",
"useable": true,
"unique": false,
"info": ""
},
"47": {
"type": "item",
"slot": 47,
"amount": 1,
"weight": 20000,
"name": "diving_gear",
"label": "Diving Gear",
"image": "diving_gear.png",
"useable": true,
"unique": true,
"info": []
},
"48": {
"type": "item",
"slot": 48,
"amount": 1,
"weight": 20000,
"name": "diving_gear",
"label": "Diving Gear",
"image": "diving_gear.png",
"useable": true,
"unique": true,
"info": []
}
}
MariaDB 10.4.22

In SQL Server, you can very easily traverse JSON with the following syntax:
SELECT 'stashitems'
JSON_VALUE(json_column, '$.8.type') AS type
see this documentation.
It's also possible in MySQL, though the syntax is different.
Unfortunately, SQLite stores only text values, so a workaround is needed.

There are some possibilities like
SELECT JSON_SEARCH(#json, 'all', 'glass');
| JSON_SEARCH(#json, 'all', 'glass') |
| :--------------------------------- |
| ["$.8.name", "$.8.label"] |
SELECT #json Like '%glass%'
| #json Like '%glass%' |
| -------------------: |
| 1 |
db<>fiddle here

Related

Find authors with age under 40 in my postgresql table

I have the following table with one row. I have tried to query this table to return authors under the age of 40 and have been unable to do so.
CREATE TABLE bookstuff (
data JSON
);
insert into bookstuff(data)
VALUES('
{
"the_books": {
"publishers": [
{
"name": "Dunder Mifflin",
"address": "Scranton, PA",
"country": "USA",
"CEO": "David Wallace"
},
{
"name": "Sabre",
"address": "Tallahassee, FL",
"country": "USA",
"CEO": "Jo Bennett"
},
{
"name": "Michael Scott Paper company",
"address": "Scranton, PA",
"country": "USA",
"CEO": "Michael Gary Scott"
},
{
"name": "Vance Refrigeration",
"address": "Scranton, PA",
"country": "USA",
"CEO": "Bob Vance"
}
],
"authors": [
{
"id": 1,
"name": "Michael Scott",
"age": 45,
"country": "USA",
"agentname": "Jan Levinson",
"books_written": "book1"
},
{
"id": 2,
"name": "Jim Halpert",
"age": 35,
"country": "USA",
"agentname": "Pam Beesly",
"books_written": "book3"
},
{
"id": 3,
"name": "Dwight Schrute",
"age": 40,
"country": "USA",
"agentname": "Angela Martin",
"books_written": "book2"
},
{
"id": 4,
"name": "Pam Halpert",
"age": 35,
"country": "USA",
"agentname": "Angela Martin",
"books_written": "book4"
}
],
"books": [
{
"isbn": "book1",
"title": "The Dundies",
"price": 10.99,
"year": 2005,
"publishername": "Dunder Mifflin"
},
{
"isbn": "book2",
"title": "Bears, Beets, Battlestar Galactica",
"price": 8.99,
"year": 2006,
"publishername": "Dunder Mifflin"
},
{
"isbn": "book3",
"title": "The Sabre Store",
"price": 12.99,
"year": 2007,
"publishername": "Sabre"
},
{
"isbn": "book4",
"title": "Branch Wars",
"price": 14.99,
"year": 2015,
"publishername": "Sabre"
}
]
}
}');
I have tried the following query to get the author's age
SELECT data->'the_books'->'authors'
FROM bookstuff
WHERE (data->'the_books'->'authors'->>'age')::integer > 40;
I expect it to return two values 'Jim halpert' and 'pam halpert' but instead I get no result back, not even null.
I have also tried this query, just to see if i could get anything back at all from the table and still no results:
SELECT data->'the_books'->'authors'
FROM bookstuff
where (data->'the_books'->'authors'->'name')::jsonb ? 'Michael Scott';
I'm new to postgresql, is there a different way I should be going about this?
Using json_array_elements:
select (v -> 'name')#>>'{}' from bookstuff b
cross join json_array_elements(b.data -> 'the_books' -> 'authors') v
where ((v -> 'age')#>>'{}')::int < 40
See fiddle
Another option, slightly more verbose:
select distinct(author->>'name') as author_name from
(select json_array_elements(b.data->'the_books'->'authors') author from bookstuff b) author
where (author->>'age')::int < 40
The distinct might be unnecessary if you really just have one database row and no duplicates in the authors array of that row.
Three considerations of why your final solution doesn't work
where filters out rows - this happens before the 'select'. the row contains everything in this case.
'?' predicate matches an array containing your choosen value "Does the key/element string exist within the JSON value?" You don't have a simple array here array->'key' doesn't pick that attribute into a new array
your select projection isn't called however it was it would contain the whole array (remember where doesn't transform just filters out rows)

Laravel Eloquent Collection filter by pivot id

This is my Apartment integrated with services
"id": 26,
"user_id": 1,
"title": "SAN MARINO",
"slug": "san-marino",
"rooms": 1,
"bathrooms": 1,
"beds": 1,
"squared_meters": 12,
"address": "San Marino, Carpi",
"latitude": "44.80924",
"longitude": "10.91565",
"image": "apartment_image/BMRQZSXLdWviqDwmHgqLzrmzG1hJzJGOq7DujnRB.jpg",
"is_visible": 1,
"floor": 1,
"price": 120,
"description": "2",
"created_at": "2022-02-21T21:41:53.000000Z",
"updated_at": "2022-02-21T21:41:53.000000Z",
"services": [
{
"id": 2,
"name": "Posto Macchina",
"slug": "posto-macchina",
"created_at": "2022-02-21T08:59:53.000000Z",
"updated_at": "2022-02-21T08:59:53.000000Z",
"pivot": {
"apartment_id": 26,
"service_id": 2
}
}
]
}
I use to filter my collection( $apartment_list = ApartmentResource::collection(Apartment::with(['services'])->get());)
using multiple WHERE, like this: $apartment_list = $apartment_list->where('rooms', '>=', $rooms);
How can i filter my Apartment refering to the pivot column service_id or services[id]?
There is filter method for the collection. You may create a callback for a single apartament and filter in any way you want:
$apartment_list->filter(function ($apartament) {
// ...
});
you can use try with this:
$serviceId = value_of_service_id;
$apartment_list = ApartmentResource::collection(Apartment:: whereHas('services', function ($q) use ($serviceId) {
$q->where('service_id', $serviceId);
})->get());

azure search exact match of file name not returning exact results

I am indexing all the file names into the index. But when I search with exact file name in the search query it is returning all other file names also. below is my index definition.
{
"fields": [
{
"name": "id",
"type": "Edm.String",
"facetable": true,
"filterable": true,
"key": true,
"retrievable": true,
"searchable": false,
"sortable": false,
"analyzer": null,
"indexAnalyzer": null,
"searchAnalyzer": null,
"synonymMaps": [],
"fields": []
},
{
"name": "FileName",
"type": "Edm.String",
"facetable": false,
"filterable": false,
"key": false,
"retrievable": true,
"searchable": true,
"sortable": false,
"analyzer": "keyword-analyzer",
"indexAnalyzer": null,
"searchAnalyzer": null,
"synonymMaps": [],
"fields": []
}
],
"scoringProfiles": [],
"defaultScoringProfile": null,
"corsOptions": null,
"analyzers": [
{
"name": "keyword-analyzer",
"#odata.type": "#Microsoft.Azure.Search.CustomAnalyzer",
"charFilters": [],
"tokenizer": "keyword_v2",
"tokenFilters": ["lowercase", "my_asciifolding", "my_word_delimiter"]
}
],
"tokenFilters": [
{
"#odata.type": "#Microsoft.Azure.Search.AsciiFoldingTokenFilter",
"name": "my_asciifolding",
"preserveOriginal": true
},
{
"#odata.type": "#Microsoft.Azure.Search.WordDelimiterTokenFilter",
"name": "my_word_delimiter",
"generateWordParts": true,
"generateNumberParts": false,
"catenateWords": false,
"catenateNumbers": false,
"catenateAll": false,
"splitOnCaseChange": true,
"preserveOriginal": true,
"splitOnNumerics": true,
"stemEnglishPossessive": false,
"protectedWords": []
}
],
"#odata.etag": "\"0x8D6FB2F498F9AD2\""
}
Below is my sample data
{
"value": [
{
"id": "1",
"FileName": "SamplePSDFile_1psd2680.psd"
},
{
"id": "2",
"FileName": "SamplePSDFile-1psd260.psd"
},
{
"id": "3",
"FileName": "SamplePSDFile_1psd2689.psd"
},
{
"id": "4",
"FileName": "SamplePSDFile-1psdxx2680.psd"
}
]
}
Below is the Analyze API results
{
"tokens": [
{
"token": "samplepsdfile_1psd2689.psd",
"startOffset": 0,
"endOffset": 26,
"position": 0
},
{
"token": "samplepsdfile",
"startOffset": 0,
"endOffset": 13,
"position": 0
},
{
"token": "psd",
"startOffset": 15,
"endOffset": 18,
"position": 1
},
{
"token": "psd",
"startOffset": 23,
"endOffset": 26,
"position": 2
}
]
}
When I search with the keyword "SamplePSDFile_1psd2689.psd", Azure search returning three records in the results instead of only document 3. Below is my search query and the results.
?search="SamplePSDFile_1psd2689.psd"&api-version=2019-05-06&$count=true&queryType=full&searchMode=All
{
"#odata.count": 3,
"value": [
{
"#search.score": 2.3387241,
"id": "2",
"FileName": "SamplePSDFile-1psd260.psd"
},
{
"#search.score": 2.2493405,
"id": "3",
"FileName": "SamplePSDFile_1psd2689.psd"
},
{
"#search.score": 2.2493405,
"id": "1",
"FileName": "SamplePSDFile_1psd2680.psd"
}
]
}
How I can achieve my expected results. I tried with and without double quotes around the keyword all other options, but no luck. What I am doing wrong here in this case?
Some body suggested to use $filter, but that field wasn't filterable in our case.
Please help me on this.
If you are looking for exact match then you probably don't want any analyzer involved. Give it a try with this line
"analyzer": "keyword-analyzer"
changed to
"analyzer": null
If you need to be able to do exact match on the field and also support partial keyword searches then you need to index the field twice with different names. Maybe append “Exact” to the exact match field name and don’t use an analyzer for that one. The name without exact can have an analyzer. Then search on the field using the right field name index depending on the type of search.

SQL to get count in JSON data

i am new to sql language. Could anyone sharing some point or solution for my cases?
i'll show the JSON data below, could it possible return some value in the column how many time appear in my json data. look like the table below:
+--------------------------------+
|column |value |totalCount |
+--------------------------------+
|brand |top-brand| (2) |
|brand |low | (1) |
|type |Bobtail | (1) |
|type |Snowshoe | (2) |
+--------------------------------+
[
{
"id": 1,
"name": "cat",
"type": {
"id": 2,
"name": "Snowshoe",
},
"brand": {
"id": 3,
"name": "top-brand",
}
},
{
"id": 2,
"name": "cat",
"type": {
"id": 2,
"name": "Snowshoe",
},
"brand": {
"id": 2,
"name": "low",
}
},
{
"id": 3,
"name": "cat",
"type": {
"id": 1,
"name": "Bobtail",
},
"brand": {
"id": 3,
"name": "top-brand",
}
}
]

User selected values from JSON

When a user fills out a form in a mobile application a json is created. I load this json into a postgres database and wanting to pull is apart and select the inputs that the user has selected.
I find this hard to explain you really need to see the json and the expected results. The json looks like this...
{
"iso_created_at":"2019-06-25T14:50:59+10:00",
"form_fields":[
{
"field_type":"DateAndTime",
"mandatory":false,
"form_order":0,
"editable":true,
"visibility":"public",
"label":"Time & Date Select",
"value":"2019-06-25T14:50:00+10:00",
"key":"f_10139_64_14",
"field_visibility":"public",
"data":{
"default_to_current":true
},
"id":89066
},
{
"field_type":"Image",
"mandatory":false,
"form_order":6,
"editable":true,
"visibility":"public",
"label":"Photos",
"value":[
],
"key":"f_10139_1_8",
"field_visibility":"public",
"data":{
},
"id":67682
},
{
"field_type":"DropDown",
"mandatory":true,
"form_order":2,
"editable":true,
"visibility":"public",
"label":"Customer ID",
"value":"f_10139_35_13_35_1",
"key":"f_10139_35_13",
"field_visibility":"public",
"data":{
"options":[
{
"is_default":false,
"display_order":0,
"enabled":true,
"value":"f_10139_35_13_35_1",
"label":"27"
}
],
"multi_select":false
},
"id":86039
},
{
"field_type":"CheckBox",
"mandatory":true,
"form_order":3,
"editable":true,
"visibility":"public",
"label":"Measure",
"value":[
"f_7422_10_7_10_1",
"f_7422_10_7_10_2"
],
"key":"f_10139_1_5",
"field_visibility":"public",
"data":{
"options":[
{
"is_default":true,
"display_order":0,
"enabled":true,
"value":"f_7422_10_7_10_1",
"label":"Kg"
},
{
"is_default":true,
"display_order":0,
"enabled":true,
"value":"f_7422_10_7_10_2",
"label":"Mm"
}
],
"multi_select":true
},
"id":67679
},
{
"field_type":"ShortTextBox",
"mandatory":true,
"form_order":4,
"editable":true,
"visibility":"public",
"label":"Qty",
"value":"1000",
"key":"f_10139_9_9",
"field_visibility":"public",
"data":{
},
"id":85776
}
],
"address":"Latitude: -37.811812 Longitude: 144.971745",
"shape_id":6456,
"category_id":75673,
"id":345,
"account_id":778
}
Can anyone help me?
Expected results:
account_id | report_id | field_label | field_value
------------------------------------------------------------------------
778 | 345 | Time & Date Select | 2019-06-25T14:50:00+10:00
778 | 345 | Photos | []
778 | 345 | Customer ID | 27
778 | 345 | Measure | Kg
778 | 345 | Measure | Mm
778 | 345 | Qty | 1000
like say #Amadan, might need to hardcode each field separately instead of making a clever loop ,especially with the fields "Customer ID" "and" "Measure" or any other that requires it.
You can use the json function: json_array_elements_text, here you have a example, you can adjust to your case, i am trying with your case:
select account_id::text,report_id::text,field_label::text,
--case ""Customer ID"" and ""Measure""
case
when field_label::text='"Customer ID"' then ((todo->'data'->'options')->0->'label')::text
when field_label::text='"Measure"' then ((todo->'data'->'options')->0->'label')::text ||',' ||((todo->'data'->'options')->1->'label')::text
else
field_value::text
end as field_value
from
(
select dato->'account_id' as account_id,dato->'id' as report_id,
(json_array_elements_text(dato->'form_fields')::json)->'label' as field_label,
(json_array_elements_text(dato->'form_fields')::json)->'value' as field_value,
(json_array_elements_text(dato->'form_fields')::json) as todo
from (
select '{"iso_created_at": "2019-06-25T14:50:59+10:00", "form_fields": [
{"field_type": "DateAndTime", "mandatory": false, "form_order": 0, "editable": true, "visibility": "public", "label": "Time & Date Select", "value": "2019-06-25T14:50:00+10:00", "key": "f_10139_64_14", "field_visibility": "public", "data":
{"default_to_current": true}, "id": 89066},
{"field_type": "Image", "mandatory": false, "form_order": 6, "editable": true, "visibility": "public", "label": "Photos", "value": [], "key": "f_10139_1_8", "field_visibility": "public", "data": {}, "id": 67682},
{"field_type": "DropDown", "mandatory": true, "form_order": 2, "editable": true, "visibility": "public", "label": "Customer ID", "value": "f_10139_35_13_35_1", "key": "f_10139_35_13", "field_visibility": "public", "data": {"options": [{"is_default": false, "display_order": 0, "enabled": true, "value": "f_10139_35_13_35_1", "label": "27"}], "multi_select": false}, "id": 86039},
{"field_type": "CheckBox", "mandatory": true, "form_order": 3, "editable": true, "visibility": "public", "label": "Measure", "value": ["f_7422_10_7_10_1","f_7422_10_7_10_2"], "key": "f_10139_1_5", "field_visibility": "public", "data": {"options": [{"is_default": true, "display_order": 0, "enabled": true, "value": "f_7422_10_7_10_1", "label": "Kg"},{"is_default": true, "display_order": 0, "enabled": true, "value": "f_7422_10_7_10_2", "label": "Mm"}], "multi_select": true}, "id": 67679},
{"field_type": "ShortTextBox", "mandatory": true, "form_order": 4, "editable": true, "visibility": "public", "label": "Qty", "value": "1000", "key": "f_10139_9_9", "field_visibility": "public", "data": {}, "id": 85776}
], "address": "Latitude: -37.811812 Longitude: 144.971745", "shape_id": 6456, "category_id": 75673, "id": 345, "account_id": 778}'::json as dato) as dat
) dat2
and i get this result, similar to you:
take this example and ajust to you
regards
You need to unnest the values in form_fields and then pick the label and the value from that JSON object:
select fd.account_id,
fd.report_id,
ff.field ->> 'label' as field_label,
ff.field ->> 'value' as field_value
from form_data fd
left join jsonb_array_elements(data -> 'form_fields') as ff(field) on true;
The left join is needed to still see the row from form_data even if no form_fields is available in the main JSON column.
The above assumes a table form_data with the columns account_id, report_id and data (which contains the JSON)
Online example: https://rextester.com/RNIBSB94484