1

I have the following table in Cassandra:

CREATE TABLE my_table (
    id int,
    map_bool_int_col MAP<boolean, int>,
    PRIMARY KEY (id)
);

According to cassandra Node.js driver documentation I can use js object to update columns with map type.
So I tried to send the following query:

client.execute("UPDATE my_table SET map_bool_int_col = ? WHERE id = ?", [{"false": 1, "true": 2}, 1], {prepare: true});

After executing the code above the column has value

{true: 2}

, false key just disappeared.
I tried to send {false: 1, true: 2} object, the result is the same. I suppose because all object keys are still strings.
I kinda managed to update column with false values by sending this object {"": 1, "true": 2} - so it converts empty strings to false. But sending empty strings instead of boolean doesn't seem right.
Is there another way to update column with such type with false keys using cassandra nodejs driver?

Erick Ramirez
  • 13,964
  • 1
  • 18
  • 23
Liberator
  • 75
  • 7

3 Answers3

2

Gave this a shot on my local, and you're right. There's something that's not working consistently right with maps that are keyed on booleans. I wonder if maybe it's something intrinsic to JavaScript? <shrug/>

I was able to get this to work by creating the map as a map<text,int>, instead:

CREATE TABLE string_map (
    id int PRIMARY KEY,
    map_string_int map<text,int>);

Then, this line worked:

client.execute("UPDATE string_map SET map_string_int = ? WHERE id = ?", [{'false': 1, 'true': 2}, 1], {prepare: true});


SELECT * FROm stackoverflow.string_map;


 id | map_string_int
----+-------------------------
  1 | {'false': 1, 'true': 2}

(1 rows)

Link to GitHub repo: https://github.com/aar0np/nodejsCassandraTest

I guess that this is the problem with cassandra db itself not with the driver?

No, because the CQL itself works and produces the correct results:

> UPDATE boolean_map SET map_bool_int_col = {false: 1, true: 2} WHERE id=1;
> SELECT * FROM boolean_map ;

 id | map_bool_int_col
----+---------------------
  1 | {False: 1, True: 2}

(1 rows)
Aaron
  • 55,518
  • 11
  • 116
  • 132
  • Yes, with a type map it will work)) But then there is a huge possibility that keys would not be boolean values at all)). I would expect something more sophisticated when converting values to boolean than simple `Boolean(stringValue)`. I guess that this is the problem with cassandra db itself not with the driver? Because with flag `{prepare: true}` queries are generated on the cassandra nodes. – Liberator May 02 '23 at 07:10
  • @Liberator Edit made. Running the CQL directly works when the map keys are booleans. – Aaron May 02 '23 at 12:31
2

There's something strange going on with the encoding of the map in this case. I've filed NODEJS-650 to look into this issue further.

absurdfarce
  • 181
  • 2
  • Note: this has now been resolved. You can find a full explanation about what's going on (as well as some sample code containing a fix) at NODEJS-650. – absurdfarce May 04 '23 at 20:10
  • Thank you very much! To sum up: when keys in a map are not of type string, it's better to use js Map instead of js object. And in order to enable this feature the following config should be specified while creating cassandra client: `new cassandra.Client(..., { encoding: { map: Map } });` – Liberator May 05 '23 at 14:36
1

For posterity, I'm documenting the solution that @absurdfarce identified in NODEJS-650.


Root cause

The boolean key in values supplied as:

[{"false": 1, "true": 2}, 1]

to the CQL statement:

UPDATE my_table SET map_bool_int_col = ? WHERE id = ?

is a regular JS object and passed to the encoder as a String, not a boolean type.

Solution

The fix is to encode the input value as a Map rather than an Object.

Here is the example code that explicitly sets encoding: { map: Map }:

const cassandra = require('cassandra-driver');

const client = new cassandra.Client({
    contactPoints: ['127.0.0.1'],
    localDataCenter: 'datacenter1',
    encoding: {
        map: Map
    }
}).on('log',(level, loggerName, msg, info) => {
    console.log(`${level} - ${loggerName}:  ${msg} (${info.stack})`);
});

let foo = new Map([[false,1],[true,2]])

client.connect();
client.execute("truncate nodejs.map_update")
    .then(() => client.execute("UPDATE nodejs.map_update SET map_bool_int_col = ? WHERE id = ?", [foo, 1], {prepare: true}))
    .then(() => client.execute("select * from nodejs.map_update where id = 1"))
    .then(rs => rs.rows.forEach(r => {
        let theMap = r['map_bool_int_col'];
        theMap.forEach((k,v,m) => console.log(`${k} => ${v}`))
    }))
    .then(() => client.shutdown());

Huge thanks to @absurdfarce for digging in to the Node.js driver code to figure this out. Cheers!

Erick Ramirez
  • 13,964
  • 1
  • 18
  • 23