on
Joining in Jolt
I stumbled across a question on stackoverflow that was asking how to perform lookup based on ids in a JSON document. After some initial thought and trial and error (because thats the only way you can develop JOLT), I came up with a solution that is repeatable allowing you to perform multiple joins.
Firstly lets look at the input and outputs:
Input JSON
Contains a data array and relationships array.
{
"data": [
{
"id": "1112245810",
"title": "Introduction to JavaScript Object Notation",
"author": "54256",
"publisher": "57756",
"edition": "first",
"published": "2012"
},
{
"id": "1156464683",
"title": "JSON at Work",
"author": "15467",
"publisher": "57756",
"edition": "second",
"published": "2014"
},
{
"id": "1004467968",
"title": "A Tiny Bit Mortal",
"author": "54256",
"publisher": "56465",
"edition": "first",
"published": "2018"
}
],
"relationships": [
{
"id": "54256",
"type": "author",
"attributes": {
"first-name": "Lindsay",
"last-name": "Bassett",
"city": "Michigan"
}
},
{
"id": "15467",
"type": "author",
"attributes": {
"first-name": "Tom",
"last-name": "Marrs",
"city": "Cologne"
}
},
{
"id": "57756",
"type": "publisher",
"attributes": {
"name": "O'REILLY",
"established": "1984"
}
},
{
"id": "56465",
"type": "publisher",
"attributes": {
"name": "APRESS",
"established": "1979"
}
}
]
}
Output JSON
Which is essentially taking copying the relationships based on type and id.
{
"data": [
{
"id": "1112245810",
"title": "Introduction to JavaScript Object Notation",
"author": {
"first-name": "Lindsay",
"last-name": "Bassett",
"city": "Michigan"
},
"publisher": {
"name": "O'REILLY",
"established": "1984"
},
"edition": "first",
"published": "2012"
},
{
"id": "1156464683",
"title": "JSON at Work",
"author": {
"first-name": "Tom",
"last-name": "Marrs",
"city": "Cologne"
},
"publisher": {
"name": "O'REILLY",
"established": "1984"
},
"edition": "second",
"published": "2014"
},
{
"id": "1004467968",
"title": "A Tiny Bit Mortal",
"author": {
"first-name": "Lindsay",
"last-name": "Bassett",
"city": "Michigan"
},
"publisher": {
"name": "APRESS",
"established": "1979"
},
"edition": "first",
"published": "2018"
}
]
}
Transformation
In order to achieve this i wrote a mutli stage shift, the main principle behind it is keying JSON objects on the ID, then copying the object across based on the ID, and shifting back out of it.
This produces the desired result (and possibly could be refactored further).
The main idea around it is setting the JSON to be keyed by something it can then be “joined” by. See the outputs of each shift and brief explanation below.
The shifts work as follows:
- Shift
relationshipsby theretypeandids - Shift
databytypeid - Shift
relationshipsfor atypeintodatabytypeids - Shift
typeinto eachdataelement key bydataid - Repeat 2-4 as much as needed
- Shift to final output
Shift relationships by there type and ids
{
"operation": "shift",
"spec": {
"*": "&",
"relationships": {
"*": {
"type": {
"*": {
"@(2,attributes)": "@2.@(3,id)"
}
}
}
}
}
}
{
"author" : {
"54256" : {
"first-name" : "Lindsay",
"last-name" : "Bassett",
"city" : "Michigan"
},
"15467" : {
"first-name" : "Tom",
"last-name" : "Marrs",
"city" : "Cologne"
}
},
"publisher" : {
"57756" : {
"name" : "O'REILLY",
"established" : "1984"
},
"56465" : {
"name" : "APRESS",
"established" : "1979"
}
},
"data" : [ {
"id" : "1112245810",
"title" : "Introduction to JavaScript Object Notation",
"author" : "54256",
"publisher" : "57756",
"edition" : "first",
"published" : "2012"
}, {
"id" : "1156464683",
"title" : "JSON at Work",
"author" : "15467",
"publisher" : "57756",
"edition" : "second",
"published" : "2014"
}, {
"id" : "1004467968",
"title" : "A Tiny Bit Mortal",
"author" : "54256",
"publisher" : "56465",
"edition" : "first",
"published" : "2018"
} ]
}
Shift data by type id
{
"operation": "shift",
"spec": {
"*": "&",
"data": {
"*": {
"author": {
"*": {
"@(2)": "data.@2.data.[]"
}
}
}
}
}
}
{
"data" : {
"54256" : {
"data" : [ {
"id" : "1112245810",
"title" : "Introduction to JavaScript Object Notation",
"author" : "54256",
"publisher" : "57756",
"edition" : "first",
"published" : "2012"
}, {
"id" : "1004467968",
"title" : "A Tiny Bit Mortal",
"author" : "54256",
"publisher" : "56465",
"edition" : "first",
"published" : "2018"
} ]
},
"15467" : {
"data" : [ {
"id" : "1156464683",
"title" : "JSON at Work",
"author" : "15467",
"publisher" : "57756",
"edition" : "second",
"published" : "2014"
} ]
}
},
"author" : {
"54256" : {
"first-name" : "Lindsay",
"last-name" : "Bassett",
"city" : "Michigan"
},
"15467" : {
"first-name" : "Tom",
"last-name" : "Marrs",
"city" : "Cologne"
}
},
"publisher" : {
"57756" : {
"name" : "O'REILLY",
"established" : "1984"
},
"56465" : {
"name" : "APRESS",
"established" : "1979"
}
}
}
Shift relationships for a type into data by type ids
{
"operation": "shift",
"spec": {
"*": "&",
"author": {
"*": {
"@": "data.&.author"
}
}
}
}
{
"data" : {
"54256" : {
"data" : [ {
"id" : "1112245810",
"title" : "Introduction to JavaScript Object Notation",
"author" : "54256",
"publisher" : "57756",
"edition" : "first",
"published" : "2012"
}, {
"id" : "1004467968",
"title" : "A Tiny Bit Mortal",
"author" : "54256",
"publisher" : "56465",
"edition" : "first",
"published" : "2018"
} ],
"author" : {
"first-name" : "Lindsay",
"last-name" : "Bassett",
"city" : "Michigan"
}
},
"15467" : {
"data" : [ {
"id" : "1156464683",
"title" : "JSON at Work",
"author" : "15467",
"publisher" : "57756",
"edition" : "second",
"published" : "2014"
} ],
"author" : {
"first-name" : "Tom",
"last-name" : "Marrs",
"city" : "Cologne"
}
}
},
"publisher" : {
"57756" : {
"name" : "O'REILLY",
"established" : "1984"
},
"56465" : {
"name" : "APRESS",
"established" : "1979"
}
}
}
Shift type into each data element key by data id
{
"operation": "shift",
"spec": {
"*": "&",
"data": {
"*": {
"data": {
"*": {
"@(id)": "data.@(id).id",
"@(title)": "data.@(id).title",
"@(2,author)": "data.@(id).author",
"@(publisher)": "data.@(id).publisher",
"@(edition)": "data.@(id).edition",
"@(published)": "data.@(id).published"
}
}
}
}
}
}
{
"data" : {
"1112245810" : {
"id" : "1112245810",
"title" : "Introduction to JavaScript Object Notation",
"author" : {
"first-name" : "Lindsay",
"last-name" : "Bassett",
"city" : "Michigan"
},
"publisher" : "57756",
"edition" : "first",
"published" : "2012"
},
"1004467968" : {
"id" : "1004467968",
"title" : "A Tiny Bit Mortal",
"author" : {
"first-name" : "Lindsay",
"last-name" : "Bassett",
"city" : "Michigan"
},
"publisher" : "56465",
"edition" : "first",
"published" : "2018"
},
"1156464683" : {
"id" : "1156464683",
"title" : "JSON at Work",
"author" : {
"first-name" : "Tom",
"last-name" : "Marrs",
"city" : "Cologne"
},
"publisher" : "57756",
"edition" : "second",
"published" : "2014"
}
},
"publisher" : {
"57756" : {
"name" : "O'REILLY",
"established" : "1984"
},
"56465" : {
"name" : "APRESS",
"established" : "1979"
}
}
}
Repeat 2-4 as much as needed
The same as above but for publisher.
[
{
"operation": "shift",
"spec": {
"*": "&",
"data": {
"*": {
"publisher": {
"*": {
"@(2)": "data.@2.data.[]"
}
}
}
}
}
},
{
"operation": "shift",
"spec": {
"*": "&",
"publisher": {
"*": {
"@": "data.&.publisher"
}
}
}
},
{
"operation": "shift",
"spec": {
"*": "&",
"data": {
"*": {
"data": {
"*": {
"@(id)": "data.@(id).id",
"@(title)": "data.@(id).title",
"@(author)": "data.@(id).author",
"@(2,publisher)": "data.@(id).publisher",
"@(edition)": "data.@(id).edition",
"@(published)": "data.@(id).published"
}
}
}
}
}
}
]
{
"data" : {
"1112245810" : {
"id" : "1112245810",
"title" : "Introduction to JavaScript Object Notation",
"author" : {
"first-name" : "Lindsay",
"last-name" : "Bassett",
"city" : "Michigan"
},
"publisher" : {
"name" : "O'REILLY",
"established" : "1984"
},
"edition" : "first",
"published" : "2012"
},
"1156464683" : {
"id" : "1156464683",
"title" : "JSON at Work",
"author" : {
"first-name" : "Tom",
"last-name" : "Marrs",
"city" : "Cologne"
},
"publisher" : {
"name" : "O'REILLY",
"established" : "1984"
},
"edition" : "second",
"published" : "2014"
},
"1004467968" : {
"id" : "1004467968",
"title" : "A Tiny Bit Mortal",
"author" : {
"first-name" : "Lindsay",
"last-name" : "Bassett",
"city" : "Michigan"
},
"publisher" : {
"name" : "APRESS",
"established" : "1979"
},
"edition" : "first",
"published" : "2018"
}
}
}
Shift to final output
Remove data id as key:
{
"operation": "shift",
"spec": {
"*": "&",
"data": {
"*": {
"@": "data.[]"
}
}
}
}
{
"data" : [ {
"id" : "1112245810",
"title" : "Introduction to JavaScript Object Notation",
"author" : {
"first-name" : "Lindsay",
"last-name" : "Bassett",
"city" : "Michigan"
},
"publisher" : "57756",
"edition" : "first",
"published" : "2012"
}, {
"id" : "1004467968",
"title" : "A Tiny Bit Mortal",
"author" : {
"first-name" : "Lindsay",
"last-name" : "Bassett",
"city" : "Michigan"
},
"publisher" : "56465",
"edition" : "first",
"published" : "2018"
}, {
"id" : "1156464683",
"title" : "JSON at Work",
"author" : {
"first-name" : "Tom",
"last-name" : "Marrs",
"city" : "Cologne"
},
"publisher" : "57756",
"edition" : "second",
"published" : "2014"
} ],
"publisher" : {
"57756" : {
"name" : "O'REILLY",
"established" : "1984"
},
"56465" : {
"name" : "APRESS",
"established" : "1979"
}
}
}
Conclusion
A repeatable pattern that is airing on the side of “if all you have is a hammer, everything looks like a nail”.
Full Spec
[
{
"operation": "shift",
"spec": {
"*": "&",
"relationships": {
"*": {
"type": {
"*": {
"@(2,attributes)": "@2.@(3,id)"
}
}
}
}
}
},
{
"operation": "shift",
"spec": {
"*": "&",
"data": {
"*": {
"author": {
"*": {
"@(2)": "data.@2.data.[]"
}
}
}
}
}
},
{
"operation": "shift",
"spec": {
"*": "&",
"author": {
"*": {
"@": "data.&.author"
}
}
}
},
{
"operation": "shift",
"spec": {
"*": "&",
"data": {
"*": {
"data": {
"*": {
"@(id)": "data.@(id).id",
"@(title)": "data.@(id).title",
"@(2,author)": "data.@(id).author",
"@(publisher)": "data.@(id).publisher",
"@(edition)": "data.@(id).edition",
"@(published)": "data.@(id).published"
}
}
}
}
}
},
{
"operation": "shift",
"spec": {
"*": "&",
"data": {
"*": {
"publisher": {
"*": {
"@(2)": "data.@2.data.[]"
}
}
}
}
}
},
{
"operation": "shift",
"spec": {
"*": "&",
"publisher": {
"*": {
"@": "data.&.publisher"
}
}
}
},
{
"operation": "shift",
"spec": {
"*": "&",
"data": {
"*": {
"data": {
"*": {
"@(id)": "data.@(id).id",
"@(title)": "data.@(id).title",
"@(author)": "data.@(id).author",
"@(2,publisher)": "data.@(id).publisher",
"@(edition)": "data.@(id).edition",
"@(published)": "data.@(id).published"
}
}
}
}
}
},
{
"operation": "shift",
"spec": {
"*": "&",
"data": {
"*": {
"@": "data.[]"
}
}
}
}
]