Reputation: 1510
My optic query gives back a single row with one column named "activeIngredient," which holds a list of IDs separated by commas. Check out the JSON below.
[
{
"productId": 1234567899,
"productName": "Test Product",
"languageCode": "EN",
"activeIngredient": "24622176,37690963,78121460",
"updatedDate": "2021-08-12T10:42:21.6181006Z"
}
]
I need to break down the ids from "activeIngredient" by using a comma to create separate rows.
I attempted to use op:map
and loop through it, but it keeps giving me the last id, which is 78121460, for every row.
This is how I want my output to appear.
[
{
"productId": 1234567899,
"productName": "Test Product",
"languageCode": "EN",
"activeIngredient": "24622176",
"updatedDate": "2021-08-12T10:42:21.6181006Z"
},
{
"productId": 1234567899,
"productName": "Test Product",
"languageCode": "EN",
"activeIngredient": "37690963",
"updatedDate": "2021-08-12T10:42:21.6181006Z"
},
{
"productId": 1234567899,
"productName": "Test Product",
"languageCode": "EN",
"activeIngredient": "78121460",
"updatedDate": "2021-08-12T10:42:21.6181006Z"
}
]
I am using MarkLogic v10.0-6.1.
I really appreciate any help you can offer.
Upvotes: 1
Views: 81
Reputation: 21
Edit: Updated with suggestion from Mads
For ML 10, if you know an upper limit to the number of ingredients per product, you can use a join-cross-product to expand out the rows. Here is an example that supports up to 100 ingredients per product. Note that making this too big can affect query scaling and memory usage, so you may have to play with it.
xquery version "1.0-ml";
import module namespace op="http://marklogic.com/optic"
at "/MarkLogic/optic.xqy";
import module namespace ofn="http://marklogic.com/optic/expression/fn"
at "/MarkLogic/optic/optic-fn.xqy";
let $products := op:from-literals(
map:new()
=> map:with("productId", 1234567899)
=> map:with("productName", "Test Product")
=> map:with("languageCode", "EN")
=> map:with("activeIngredient", "24622176,37690963,78121460")
=> map:with("updatedDate", "2021-08-12T10:42:21.6181006Z")
)
let $pivot := op:from-literals((1 to 100)! map:entry("index", .))
return op:join-cross-product($products, $pivot)
=> op:bind((
op:as("activeIngredient", ofn:subsequence(ofn:tokenize(op:col("activeIngredient"), ","), op:col("index"), 1))
))
=> op:where(ofn:exists(op:col("activeIngredient")))
=> op:result()
languageCode | index | productName | updatedDate | activeIngredient | productId |
---|---|---|---|---|---|
EN | 3 | Test Product | 2021-08-12T10:42:21.6181006Z | 78121460 | 1234567899 |
EN | 2 | Test Product | 2021-08-12T10:42:21.6181006Z | 37690963 | 1234567899 |
EN | 1 | Test Product | 2021-08-12T10:42:21.6181006Z | 24622176 | 1234567899 |
Upvotes: 1
Reputation: 66781
You can use op:bind()
to modify the activeIngredient
column and change it from a CSV to a tokenized set of values, and then use op:unnest-inner()
to produce a row for each of those values:
xquery version "1.0-ml";
import module namespace op="http://marklogic.com/optic"
at "/MarkLogic/optic.xqy";
import module namespace ofn="http://marklogic.com/optic/expression/fn"
at "/MarkLogic/optic/optic-fn.xqy";
op:from-literals(
map:new()
=> map:with("productId", 1234567899)
=> map:with("productName", "Test Product")
=> map:with("languageCode", "EN")
=> map:with("activeIngredient", "24622176,37690963,78121460")
=> map:with("updatedDate", "2021-08-12T10:42:21.6181006Z")
)
=> op:bind((
op:as("activeIngredient", ofn:tokenize(op:col("activeIngredient"), ","))
))
=> op:unnest-inner('activeIngredient','activeIngredient')
=> op:result()
Which produces:
languageCode | productName | updatedDate | activeIngredient | productId |
---|---|---|---|---|
EN | Test Product | 2021-08-12T10:42:21.6181006Z | 24622176 | 1234567899 |
EN | Test Product | 2021-08-12T10:42:21.6181006Z | 37690963 | 1234567899 |
EN | Test Product | 2021-08-12T10:42:21.6181006Z | 78121460 | 1234567899 |
Upvotes: 1