|
| 1 | +--- |
| 2 | +Title: Restructure JSON or hash objects |
| 3 | +alwaysopen: false |
| 4 | +categories: |
| 5 | +- docs |
| 6 | +- integrate |
| 7 | +- rs |
| 8 | +- rdi |
| 9 | +description: null |
| 10 | +group: di |
| 11 | +linkTitle: Restructure objects |
| 12 | +summary: Redis Data Integration keeps Redis in sync with a primary database in near |
| 13 | + real time. |
| 14 | +type: integration |
| 15 | +weight: 40 |
| 16 | +--- |
| 17 | + |
| 18 | +By default, RDI adds fields to |
| 19 | +[hash]({{< relref "/develop/data-types/hashes" >}}) or |
| 20 | +[JSON]({{< relref "/develop/data-types/json" >}}) objects in the target |
| 21 | +database that closely match the columns of the source table. |
| 22 | +The examples below show how you can create a completely new object structure |
| 23 | +from existing fields using the |
| 24 | +[`map`]({{< relref "/integrate/redis-data-integration/reference/data-transformation/map" >}}) |
| 25 | +transformation. |
| 26 | + |
| 27 | +## Map to a new JSON structure |
| 28 | + |
| 29 | +The first |
| 30 | +[job file]({{< relref "/integrate/redis-data-integration/data-pipelines/data-pipelines#job-files" >}}) |
| 31 | +example creates a new [JSON]({{< relref "/develop/data-types/json" >}}) |
| 32 | +object structure to write to the target. |
| 33 | +The `source` section selects the `employee` table of the |
| 34 | +[`chinook`](https://github.com/Redislabs-Solution-Architects/rdi-quickstart-postgres) |
| 35 | +database (the optional `db` value here corresponds to the |
| 36 | +`sources.<source-name>.connection.database` value defined in |
| 37 | +[`config.yaml`]({{< relref "/integrate/redis-data-integration/data-pipelines/data-pipelines#the-configyaml-file" >}})). |
| 38 | + |
| 39 | +In the `transform` section, the `map` transformation uses a [JMESPath](https://jmespath.org/) |
| 40 | +expression to specify the new JSON format. (Note that the vertical bar "|" in the `expression` |
| 41 | +line indicates that the following indented lines should be interpreted as a single string.) |
| 42 | +The expression resembles JSON notation but with data values supplied from |
| 43 | +table fields and |
| 44 | +[JMESPath functions]({{< relref "/integrate/redis-data-integration/reference/jmespath-custom-functions" >}}). |
| 45 | + |
| 46 | +Here, we rename the |
| 47 | +`employeeid` field to `id` and create two nested objects for the `address` |
| 48 | +and `contact` information. The `name` field is the concatenation of the existing |
| 49 | +`firstname` and `lastname` fields, with `lastname` converted to uppercase. |
| 50 | +In the `contact` subobject, the `email` address is obfuscated slightly, using the |
| 51 | +`replace()` function to hide the '@' sign and dots. |
| 52 | + |
| 53 | +In the `output` section of the job file, we specify that we want to write |
| 54 | +to a JSON object with a custom key. Note that in the `output` section, you must refer to |
| 55 | +fields defined in the `map` transformation, so we use the new name `id` |
| 56 | +for the key instead of `employeeid`. |
| 57 | + |
| 58 | +The full example is shown below: |
| 59 | + |
| 60 | +```yaml |
| 61 | +source: |
| 62 | + db: chinook |
| 63 | + table: employee |
| 64 | +transform: |
| 65 | + - uses: map |
| 66 | + with: |
| 67 | + expression: | |
| 68 | + { |
| 69 | + "id": employeeid, |
| 70 | + "name": concat([firstname, ' ', upper(lastname)]), |
| 71 | + "address": { |
| 72 | + "street": address, |
| 73 | + "city": city, |
| 74 | + "state": state, |
| 75 | + "postalCode": postalcode, |
| 76 | + "country": country |
| 77 | + }, |
| 78 | + "contact": { |
| 79 | + "phone": phone, |
| 80 | + "safeEmail": replace(replace(email, '@', '_at_'), '.', '_dot_') |
| 81 | + } |
| 82 | + } |
| 83 | + language: jmespath |
| 84 | +output: |
| 85 | + - uses: redis.write |
| 86 | + with: |
| 87 | + connection: target |
| 88 | + data_type: json |
| 89 | + key: |
| 90 | + expression: concat(['emp:', id]) |
| 91 | + language: jmespath |
| 92 | +``` |
| 93 | +
|
| 94 | +If you query one of the new JSON objects, you see output like the following: |
| 95 | +
|
| 96 | +```bash |
| 97 | +> JSON.GET emp:1 $ |
| 98 | +"[{\"id\":1,\"name\":\"Andrew ADAMS\",\"address\":{\"street\":\"11120 Jasper Ave NW\",\"city\":\"Edmonton\",\"state\":\"AB\",\"postalCode\":\"T5K 2N1\",\"country\":\"Canada\"},\"contact\":{\"phone\":\"+1 (780) 428-9482\",\"safeEmail\":\"andrew_at_chinookcorp_dot_com\"}}]" |
| 99 | +``` |
| 100 | + |
| 101 | +Formatted in the usual JSON style, the output looks like the sample below: |
| 102 | + |
| 103 | +```json |
| 104 | +{ |
| 105 | + "id": 1, |
| 106 | + "name": "Andrew ADAMS", |
| 107 | + "address": { |
| 108 | + "street": "11120 Jasper Ave NW", |
| 109 | + "city": "Edmonton", |
| 110 | + "state": "AB", |
| 111 | + "postalCode": "T5K 2N1", |
| 112 | + "country": "Canada" |
| 113 | + }, |
| 114 | + "contact": { |
| 115 | + "phone": "+1 (780) 428-9482", |
| 116 | + "safeEmail": "andrew_at_chinookcorp_dot_com" |
| 117 | + } |
| 118 | +} |
| 119 | +``` |
| 120 | + |
| 121 | +## Map to a hash structure |
| 122 | + |
| 123 | +This example creates a new [hash]({{< relref "/develop/data-types/hashes" >}}) |
| 124 | +object structure for items from the `track` table. Here, the `map` transformation uses |
| 125 | +[SQL](https://en.wikipedia.org/wiki/SQL) for the expression because this is often |
| 126 | +more suitable for hashes or "flat" |
| 127 | +JSON objects without subobjects or arrays. The expression renames some of the fields. |
| 128 | +It also calculates more human-friendly representations for the track duration (originally |
| 129 | +stored in the `milliseconds` field) and the storage size (originally stored in the |
| 130 | +`bytes` field). |
| 131 | + |
| 132 | +The full example is shown below: |
| 133 | + |
| 134 | +```yaml |
| 135 | +source: |
| 136 | + db: chinook |
| 137 | + table: track |
| 138 | +transform: |
| 139 | + - uses: map |
| 140 | + with: |
| 141 | + expression: |
| 142 | + id: trackid |
| 143 | + name: name |
| 144 | + duration: concat(floor(milliseconds / 60000), ':', floor(mod(milliseconds / 1000, 60))) |
| 145 | + storagesize: concat(round(bytes / 1048576.0, 2), 'MB') |
| 146 | + language: sql |
| 147 | +output: |
| 148 | + - uses: redis.write |
| 149 | + with: |
| 150 | + connection: target |
| 151 | + data_type: hash |
| 152 | + key: |
| 153 | + expression: concat('track:', id) |
| 154 | + language: sql |
| 155 | +``` |
| 156 | +
|
| 157 | +If you query the data for one of the `track` hash objects, you see output |
| 158 | +like the following: |
| 159 | + |
| 160 | +```bash |
| 161 | +> hgetall track:16 |
| 162 | +1) "id" |
| 163 | +2) "16" |
| 164 | +3) "name" |
| 165 | +4) "Dog Eat Dog" |
| 166 | +5) "duration" |
| 167 | +6) "3:35.0" |
| 168 | +7) "storagesize" |
| 169 | +8) "6.71MB" |
| 170 | +``` |
0 commit comments