4

In Aerospike, how can I add a new key/value pair in nested object stored in bins of type map?

For ex, I have a bins of type map against which I need to store below key/value pairs.

{
  "a" : "apple",
  "b" : "ball",
  "c" : { "d" : "dog", "e" : "elephant" }, 
  "f" : { "g" : { "h" : "horse" } },
  "i" : { "j" : "jackal", "k" : { "l" : "lion", "m" : "monkey" } }
}  

Now, I want to update an existing nested object against key "k" to add one more key value pair like below.

"k" : { "l" : "lion", "m" : "monkey", "n" : "nest" }

Final result should be like below.

{
  "a" : "apple",
  "b" : "ball",
  "c" : { "d" : "dog", "e" : "elephant" }, 
  "f" : { "g" : { "h" : "horse" } },
  "i" : { "j" : "jackal", "k" : { "l" : "lion", "m" : "monkey", "n" : "nest" } }
}  

Any suggestions on how to achieve this? It's a NodeJS (10.6.0) application & I'm using NodeJS aerospike client (3.6.1) to interact with Aerospike (4.3.0.7).

Ayaz Pasha
  • 1,045
  • 6
  • 13
  • 28

2 Answers2

8

Updates on nested CDT maps and lists are possible now, using Aerospike server version 4.6 or later and Aerospike Node.js client version 3.12 or later. That update introduced the withContext() function on list and map operations that lets you specify the context in which the list/map operation is to be executed. You can find more information in the documentation for the new CdtContext class.

Here is how you would perform the update given in your example:

const Aerospike = require('aerospike')
const maps = Aerospike.maps

Aerospike.connect().then(async (client) => {
  const key = new Aerospike.Key('test', 'test', 'test')

  const map = {
    "a" : "apple",
    "b" : "ball",
    "c" : { "d" : "dog", "e" : "elephant" },
    "f" : { "g" : { "h" : "horse" } },
    "i" : { "j" : "jackal", "k" : { "l" : "lion", "m" : "monkey" } }
  }
  console.log('BEFORE:', map)
  await client.put(key, map)

  await client.operate(key, [
    maps.put('i', 'n', 'nest')
        .withContext((ctx) => ctx.addMapKey('k'))
  ])
  const record = await client.get(key)
  console.log('AFTER:', record.bins)

  client.close()
}).catch((error) => {
  console.error(error)
  if (error.client) error.client.close()
})

Here is what you would get, when you run the example:

$ node nested-map-update-example.js
BEFORE: {
  a: 'apple',
  b: 'ball',
  c: { d: 'dog', e: 'elephant' },
  f: { g: { h: 'horse' } },
  i: { j: 'jackal', k: { l: 'lion', m: 'monkey' } }
}
AFTER: {
  a: 'apple',
  b: 'ball',
  c: { d: 'dog', e: 'elephant' },
  f: { g: { h: 'horse' } },
  i: { j: 'jackal', k: { l: 'lion', m: 'monkey', n: 'nest' } }
}
Jan Hecking
  • 927
  • 5
  • 12
3

You will have to update the full value for the key "i".

pgupta
  • 5,130
  • 11
  • 8
  • In that case I need to first do a get & then update. Is there any other alternative? – Ayaz Pasha Oct 17 '18 at 08:29
  • 2
    You can flatten out your nested map - use keys such as "i-j", "i-k", or do a read-modify-write with write policy GEN_EQUAL so you make sure that you did not get a write from another client between your read and write. – pgupta Oct 17 '18 at 14:57
  • 1
    This is no longer true, as of Aerospike server version 4.6. Updates on nested lists/maps can now be performed without fetching/updating the full list/map bin value first. See my answer for more details. – Jan Hecking Nov 26 '19 at 02:53