One of the notable enhancements introduced in Oracle
Database 26ai (23.26.1) is the ability to rebuild an existing JSON Search Index
with path subsetting. This new capability gives database administrators much
finer control over what gets indexed and, more importantly, what does not. As
JSON workloads continue to grow in size and complexity, controlling index
footprint has become increasingly important. Oracle's latest enhancement
addresses this challenge by allowing index definitions to evolve without requiring
the index to be dropped and recreated.
To understand the value of this feature, consider a JSON Search Index created using the traditional SEARCH_ON TEXT_VALUE_STRING option. Such an index provides excellent query performance because it indexes text values, numeric values, and timestamp values across all supported JSON paths. As a result, both full-text searches and value-based predicates can be efficiently answered through the same index. Queries that search for a product identifier, a quantity value, or a text attribute such as a product title can all take advantage of index access paths.
2 on my_demo(c2)
3 for json parameters(' search_on TEXT_VALUE_STRING ');
demo@ADB26AI> set autotrace traceonly exp
demo@ADB26AI>
demo@ADB26AI> select *
2 from my_demo
3 where json_exists( c2, '$.products[*]?(@.id.number() == 72)' );
----------------------------------------------------------
Plan hash value: 2005405343
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 25 | 18200 | 488 (0)| 00:00:01 |
|* 1 | TABLE ACCESS BY INDEX ROWID | MY_DEMO | 25 | 18200 | 488 (0)| 00:00:01 |
| 2 | SORT CLUSTER BY ROWID BATCHED| | | | 4 (0)| 00:00:01 |
|* 3 | DOMAIN INDEX | MY_DEMO_IDX | | | 4 (0)| 00:00:01 |
---------------------------------------------------
'$.products[*]?(@.id.number() == 72)' /* json_path_str $.products[*]?(@.id.number()
== 72) */ FALSE ON ERROR TYPE(LAX) )=1)
3 - access("CTXSYS"."CONTAINS"("MY_DEMO"."C2" /*+ LOB_BY_VALUE */
,'(sdata(FNUM_6AA7829B12A1ECA7231B4272D111B8B4_id = 72 ))')>0)
demo@ADB26AI> select *
2 from my_demo
3 where json_exists( c2, '$.products[*]?(@.quantity.number() == 42)' );
----------------------------------------------------------
Plan hash value: 2005405343
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 25 | 18200 | 488 (0)| 00:00:01 |
|* 1 | TABLE ACCESS BY INDEX ROWID | MY_DEMO | 25 | 18200 | 488 (0)| 00:00:01 |
| 2 | SORT CLUSTER BY ROWID BATCHED| | | | 4 (0)| 00:00:01 |
|* 3 | DOMAIN INDEX | MY_DEMO_IDX | | | 4 (0)| 00:00:01 |
---------------------------------------------------
'$.products[*]?(@.quantity.number() == 42)' /* json_path_str
$.products[*]?(@.quantity.number() == 42) */ FALSE ON ERROR TYPE(LAX) )=1)
3 - access("CTXSYS"."CONTAINS"("MY_DEMO"."C2" /*+ LOB_BY_VALUE */
,'(sdata(FNUM_409E93841EA61DD3F60D03041A92157A_quantity = 42 ))')>0)
2 from my_demo
3 where json_textcontains( c2,'$.products.title','Charger SXT RWD');
----------------------------------------------------------
Plan hash value: 2005405343
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2500 | 1777K| 488 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID | MY_DEMO | 2500 | 1777K| 488 (0)| 00:00:01 |
| 2 | SORT CLUSTER BY ROWID BATCHED| | | | 4 (0)| 00:00:01 |
|* 3 | DOMAIN INDEX | MY_DEMO_IDX | | | 4 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
---------------------------------------------------
RWD) INPATH (/products/title)')>0)
2 from user_segments
3 where segment_name like '%MY_DEMO_IDX%';
----------
2483.63
2 parameters( ' ADD search_on value(number) include ($.products.id) ' );
2 from user_segments
3 where segment_name like '%MY_DEMO_IDX%';
----------
2556.69
2 from my_demo
3 where json_exists( c2, '$.products[*]?(@.id.number() == 72)' );
----------------------------------------------------------
Plan hash value: 2005405343
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 25 | 18200 | 488 (0)| 00:00:01 |
|* 1 | TABLE ACCESS BY INDEX ROWID | MY_DEMO | 25 | 18200 | 488 (0)| 00:00:01 |
| 2 | SORT CLUSTER BY ROWID BATCHED| | | | 4 (0)| 00:00:01 |
|* 3 | DOMAIN INDEX | MY_DEMO_IDX | | | 4 (0)| 00:00:01 |
---------------------------------------------------
'$.products[*]?(@.id.number() == 72)' /* json_path_str $.products[*]?(@.id.number()
== 72) */ FALSE ON ERROR TYPE(LAX) )=1)
3 - access("CTXSYS"."CONTAINS"("MY_DEMO"."C2" /*+ LOB_BY_VALUE */
,'(sdata(FNUM_6AA7829B12A1ECA7231B4272D111B8B4_id = 72 ))')>0)
2 from my_demo
3 where json_exists( c2, '$.products[*]?(@.quantity.number() == 42)' );
----------------------------------------------------------
Plan hash value: 3804406768
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 50000 | 34M| 738 (40)| 00:00:01 |
| 1 | RESULT CACHE | g9xkr5g0q79r37y3xw9y29gvmm | 50000 | 34M| 738 (40)| 00:00:01 |
|* 2 | TABLE ACCESS FULL| MY_DEMO | 50000 | 34M| 738 (40)| 00:00:01 |
-------------------------------------------------------------------------------------------------
---------------------------------------------------
'$.products[*]?(@.quantity.number() == 42)' /* json_path_str
$.products[*]?(@.quantity.number() == 42) */ FALSE ON ERROR TYPE(LAX) )=1)
------------------------------------------------------
from my_demo
where json_exists( c2, '$.products[*]?(@.quantity.number() == 42)' )"
2 from my_demo
3 where json_textcontains( c2,'$.products.title','Charger SXT RWD');
----------------------------------------------------------
Plan hash value: 2005405343
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2500 | 1777K| 488 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID | MY_DEMO | 2500 | 1777K| 488 (0)| 00:00:01 |
| 2 | SORT CLUSTER BY ROWID BATCHED| | | | 4 (0)| 00:00:01 |
|* 3 | DOMAIN INDEX | MY_DEMO_IDX | | | 4 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
---------------------------------------------------
2 parameters( ' REMOVE search_on text_value(varchar2) ' );
2 from user_segments
3 where segment_name like '%MY_DEMO_IDX%';
----------
388.81
2 from my_demo
3 where json_exists( c2, '$.products[*]?(@.id.number() == 72)' );
----------------------------------------------------------
Plan hash value: 2005405343
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 25 | 18200 | 488 (0)| 00:00:01 |
|* 1 | TABLE ACCESS BY INDEX ROWID | MY_DEMO | 25 | 18200 | 488 (0)| 00:00:01 |
| 2 | SORT CLUSTER BY ROWID BATCHED| | | | 4 (0)| 00:00:01 |
|* 3 | DOMAIN INDEX | MY_DEMO_IDX | | | 4 (0)| 00:00:01 |
---------------------------------------------------
'$.products[*]?(@.id.number() == 72)' /* json_path_str $.products[*]?(@.id.number()
== 72) */ FALSE ON ERROR TYPE(LAX) )=1)
3 - access("CTXSYS"."CONTAINS"("MY_DEMO"."C2" /*+ LOB_BY_VALUE */
,'(sdata(FNUM_6AA7829B12A1ECA7231B4272D111B8B4_id = 72 ))')>0)
2 from my_demo
3 where json_exists( c2, '$.products[*]?(@.quantity.number() == 42)' );
----------------------------------------------------------
Plan hash value: 3804406768
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 50000 | 34M| 738 (40)| 00:00:01 |
| 1 | RESULT CACHE | g9xkr5g0q79r37y3xw9y29gvmm | 50000 | 34M| 738 (40)| 00:00:01 |
|* 2 | TABLE ACCESS FULL| MY_DEMO | 50000 | 34M| 738 (40)| 00:00:01 |
-------------------------------------------------------------------------------------------------
---------------------------------------------------
'$.products[*]?(@.quantity.number() == 42)' /* json_path_str
$.products[*]?(@.quantity.number() == 42) */ FALSE ON ERROR TYPE(LAX) )=1)
------------------------------------------------------
from my_demo
where json_exists( c2, '$.products[*]?(@.quantity.number() == 42)' )"
2 from my_demo
3 where json_textcontains( c2,'$.products.title','Charger SXT RWD');
*
ERROR at line 2:
ORA-40467: JSON_TEXTCONTAINS() cannot be evaluated without a JSON-enabled context index
Help: https://docs.oracle.com/error-help/db/ora-40467/
2 parameters( ' REPLACE search_on value(number) include ($.products.id) ' );
2 from user_segments
3 where segment_name like '%MY_DEMO_IDX%';
----------
383.44
2 from my_demo
----------------------------------------------------------
Plan hash value: 2005405343
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 25 | 18200 | 488 (0)| 00:00:01 |
|* 1 | TABLE ACCESS BY INDEX ROWID | MY_DEMO | 25 | 18200 | 488 (0)| 00:00:01 |
| 2 | SORT CLUSTER BY ROWID BATCHED| | | | 4 (0)| 00:00:01 |
|* 3 | DOMAIN INDEX | MY_DEMO_IDX | | | 4 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
---------------------------------------------------
'$.products[*]?(@.id.number() == 72)' /* json_path_str $.products[*]?(@.id.number()
== 72) */ FALSE ON ERROR TYPE(LAX) )=1)
3 - access("CTXSYS"."CONTAINS"("MY_DEMO"."C2" /*+ LOB_BY_VALUE */
2 from my_demo
3 where json_exists( c2, '$.products[*]?(@.quantity.number() == 42)' );
----------------------------------------------------------
Plan hash value: 3804406768
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 50000 | 34M| 738 (40)| 00:00:01 |
| 1 | RESULT CACHE | g9xkr5g0q79r37y3xw9y29gvmm | 50000 | 34M| 738 (40)| 00:00:01 |
|* 2 | TABLE ACCESS FULL| MY_DEMO | 50000 | 34M| 738 (40)| 00:00:01 |
-------------------------------------------------------------------------------------------------
---------------------------------------------------
'$.products[*]?(@.quantity.number() == 42)' /* json_path_str
$.products[*]?(@.quantity.number() == 42) */ FALSE ON ERROR TYPE(LAX) )=1)
------------------------------------------------------
from my_demo
where json_exists( c2, '$.products[*]?(@.quantity.number() == 42)' )"
2 from my_demo
3 where json_exists( c2,'$.products[*]?(@.title == "Charger SXT RWD")');
----------------------------------------------------------
Plan hash value: 3804406768
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 50000 | 34M| 738 (40)| 00:00:01 |
| 1 | RESULT CACHE | bdzdkjsz7xptdbx5kjyaykf3yf | 50000 | 34M| 738 (40)| 00:00:01 |
|* 2 | TABLE ACCESS FULL| MY_DEMO | 50000 | 34M| 738 (40)| 00:00:01 |
-------------------------------------------------------------------------------------------------
---------------------------------------------------
'$.products[*]?(@.title == "Charger SXT RWD")' /* json_path_str
$.products[*]?(@.title.string() == "Charger SXT RWD") */ FALSE ON ERROR TYPE(LAX) )=1)
------------------------------------------------------
from my_demo
where json_exists( c2,'$.products[*]?(@.title == "Charger SXT RWD")')"