ResponseError: Keyspace 'ks1' does not exist - node.js

I am new in Cassandra and just installed and wanted to try it. So I tried the following code:
import cassandra from 'cassandra-driver';
const client = new cassandra.Client({
contactPoints: ['localhost'],
localDataCenter: 'datacenter1',
keyspace: 'ks1'
});
const query1 = "CREATE TABLE users(name text , emailtext, PRIMARY_KEY(email, name)) VALUES('someone' , 's#s.com')";
const query2 = 'SELECT name, email FROM users WHERE name = ?';
await client.execute(query1);
await client.execute(query2)
.then(result => console.log('User with email %s', result.rows[0].email));
But I get this result:
> cassandra_test#1.0.0 start
> ts-node-esm src/index.ts
/home/a/bars/cassandra_test/node_modules/cassandra-driver/lib/readers.js:389
const err = new errors.ResponseError(code, message);
^
ResponseError: Keyspace 'ks1' does not exist
at FrameReader.readError (/home/a/bars/cassandra_test/node_modules/cassandra-driver/lib/readers.js:389:17)
at Parser.parseBody (/home/a/bars/cassandra_test/node_modules/cassandra-driver/lib/streams.js:209:66)
at Parser._transform (/home/a/bars/cassandra_test/node_modules/cassandra-driver/lib/streams.js:152:10)
at Parser.Transform._write (node:internal/streams/transform:175:8)
at writeOrBuffer (node:internal/streams/writable:392:12)
at _write (node:internal/streams/writable:333:10)
at Parser.Writable.write (node:internal/streams/writable:337:10)
at Protocol.ondata (node:internal/streams/readable:766:22)
at Protocol.emit (node:events:513:28)
at Protocol.emit (node:domain:489:12) {
info: 'Represents an error message from the server',
code: 8704
}
What is the problem and how can I fix it
PS: I use Ubuntu 22.04 and NodeJS version 9.
EDIT: I tried to create the KEYSPACE then edited my code like following:
import cassandra from 'cassandra-driver';
const client = new cassandra.Client({
contactPoints: ['localhost'],
localDataCenter: 'datacenter1',
keyspace: 'ks1'
});
const query0 = "CREATE TABLE users(name text, email text, PRIMARY_KEY(email, name))";
const query1 = "INSERT INTO users(name, email) VALUES ('someone' , 's#s.com')";
const query2 = "SELECT name, email FROM users WHERE name = someone";
await client.execute(query0 );
await client.execute(query1);
await client.execute(query2)
.then(result => console.log('User with email %s', result.rows[0].email));
But still get another error:
> cassandra_test#1.0.0 start
> ts-node-esm src/index.ts
/home/a/bars/cassandra_test/node_modules/cassandra-driver/lib/readers.js:389
const err = new errors.ResponseError(code, message);
^
ResponseError: line 1:53 no viable alternative at input '(' (... text, email text, PRIMARY_KEY[(]...)
at FrameReader.readError (/home/a/bars/cassandra_test/node_modules/cassandra-driver/lib/readers.js:389:17)
at Parser.parseBody (/home/a/bars/cassandra_test/node_modules/cassandra-driver/lib/streams.js:209:66)
at Parser._transform (/home/a/bars/cassandra_test/node_modules/cassandra-driver/lib/streams.js:152:10)
at Parser.Transform._write (node:internal/streams/transform:175:8)
at writeOrBuffer (node:internal/streams/writable:392:12)
at _write (node:internal/streams/writable:333:10)
at Parser.Writable.write (node:internal/streams/writable:337:10)
at Protocol.ondata (node:internal/streams/readable:766:22)
at Protocol.emit (node:events:513:28)
at Protocol.emit (node:domain:489:12) {
info: 'Represents an error message from the server',
code: 8192,
coordinator: '127.0.0.1:9042',
query: 'CREATE TABLE users(name text, email text, PRIMARY_KEY(email, name))'
}
I don't know what is the problem with the new error?

So the ks1 keyspace doesn't exist. You'll need to create it.
You can use the cqlsh tool (found in Cassandra's bin/ directory) for this.
As it looks like your cluster is a single, local node and doesn't have security active, you should be able to get into it simply by doing this:
$ bin/cqlsh
Once inside, you can create the keyspace with this command:
CREATE KEYSPACE ks1 WITH replication = {'class':'SimpleStrategy', 'replication_factor' : 1};
That's the quickest way to solve this.
EDIT
After looking at the CQL queries, there are some errors up there, as well. A CREATE TABLE statement will not allow values to be inserted at creation time. So those will need to be two, separate queries:
const query1 = "CREATE TABLE users(name text , email text, PRIMARY KEY(email, name))"
const query1b = "INSERT INTO users (name,email) VALUES('someone' , 's#s.com')";
await client.execute(query1);
await client.execute(query1b);
Note that in the CREATE TABLE statement, I've also corrected the spacing between email text and PRIMARY KEY.
Also, query2 looks like it is using a question mark ? to bind a variable in a prepared statement. But I don't see where that's happening. When you get this point, have a look at the DataStax documentation for the Node.js driver on Parameterized Queries.
Also note that with the way this table's PRIMARY KEY is structured, it can only be queried by email or email and name. But it cannot be queried by name alone.
Thank you! Is there a way that I can also create the KEYSPACE using NodeJS instead of cqlsh?
So the short answer is "I don't recommend that."
The long answer is that Cassandra replicates schema out to each node, and programatic schema changes can be susceptible to network issues. That being said, as the keyspace is a part of your connection properites, it will probably fail before you have a chance to run the CREATE KEYSPACE statement.

Related

#mysql/xdevapi won't allow array parameters for WHERE field IN (?) query

I'm using the #mysql/xdevapi npm package with a local installation of mysql-8.0.15-winx64.
I'm trying to execute a SQL string with parameters. The query is looking for records where the id is IN an array.
const sql = "SELECT * FROM table WHERE id IN (?)";
const params = [[1,2]];
const result = await session.sql(sql).bind(params).execute();
and I'm getting the following error
Error: Invalid data, expecting scalar
at SqlResultHandler.BaseHandler.(anonymous function) (C:\...\#mysql\xdevapi\lib\Protocol\ResponseHandlers\BaseHandler.js:110:19)
at Array.entry (C:\...\#mysql\xdevapi\lib\Protocol\ResponseHandlers\BaseHandler.js:87:29)
at WorkQueue.process (C:\...\#mysql\xdevapi\lib\WorkQueue.js:75:19)
at Client.handleServerMessage (C:\...\#mysql\xdevapi\lib\Protocol\Client.js:198:21)
at Client.handleNetworkFragment (C:\...\#mysql\xdevapi\lib\Protocol\Client.js:242:14)
at TLSSocket.stream.on.data (C:\...\#mysql\xdevapi\lib\Protocol\Client.js:89:36)
at emitOne (events.js:116:13)
at TLSSocket.emit (events.js:211:7)
at addChunk (_stream_readable.js:263:12)
at readableAddChunk (_stream_readable.js:250:11) info: [Object] } ],
It works if I just pass the sql SELECT * FROM table WHERE id IN (1,2), returning two records.
If I pass a single parameter with a comma separated string, so const params = ["1,2"] then it returns just the first record.
I can't find anything in the documentation to support this, and I can't find a way to work around it.
You need to provide a placeholder for each value in the array. In your case, the SQL statement should eventually be:
const sql = "SELECT * FROM table WHERE id IN (?, ?)";
const params = [1, 2];
const result = await session.sql(sql).bind(params).execute();
You can find more details here.
Disclaimer: I'm the lead developer of the MySQL X DevAPI connector for Node.js

How to get inserted row immediately after query execution in cassandra?

I am trying to get last inserted row in cassandra but I am getting undefined
Here is how code looks for insert:
const cassandra = require('cassandra-driver');
const client = new cassandra.Client({ contactPoints: ['h1', 'h2'], keyspace: 'ks1' });
let query = "INSERT INTO users (id, name, email ) values (uuid(), ?, ?)";
client.execute(query, [ 'badis', 'badis#badis.com' ])
.then(result => console.log('User with id %s', result.rows[0].id));
Remember you are dealing with NoSQL ("non relational")
If I were to run a SELECT * FROM users LIMIT 1 on this table, my
result set would contain a single row. That row would be the one
containing the lowest hashed value of username (my partition key),
because that's how Cassandra stores data in the cluster. I would have
no way of knowing if it was the last one added or not, so this
wouldn't be terribly useful to you.
The workings are very different from those that you might be used to in MySQL.
If obtaining the last inserted row's information is needed for your system, then you will have to know it before you insert it.
Generating UUID
const Uuid = require('cassandra-driver').types.Uuid;
const id = Uuid.random();
Then you can use the id as another value for the prepared insert query
const cassandra = require('cassandra-driver');
const client = new cassandra.Client({ contactPoints: ['h1', 'h2'], keyspace: 'ks1' });
let query = "INSERT INTO users (id, name, email ) values (?, ?, ?)";
client.execute(query, [id, 'badis', 'badis#badis.com' ])
.then(result => console.log('User with id %s', id));

Inserting timestamp into Cassandra

I have a table created as follows:
CREATE TABLE my_table (
date text,
id text,
time timestamp,
value text,
PRIMARY KEY (id));
CREATE INDEX recordings_date_ci ON recordings (date);
I'm able to simply add a new row to the table using the following Node code:
const cassandra = require('cassandra-driver');
const client = new cassandra.Client({ contactPoints: ['localhost'], keyspace: 'my_keyspace'});
const query = 'INSERT INTO my_table (date, id, time, url) VALUES (?, ?, ?, ?)';
client.execute(query, ['20160901', '0000000000', '2016-09-01 00:00:00+0000', 'random url'], function(err, result) {
if (err){
console.log(err);
}
console.log('Insert row ended:' + result);
});
However, I get the following error:
'Error: Expected 8 or 0 byte long for date (24)
When I change the timestamp to epoc time:
client.execute(query, ['20160901', '0000000000', 1472688000, 'random url']
I get:
d
OverflowError: normalized days too large to fit in a C int
I'm able to insert new rows via cqlsh so I'm probably missing something with the node.js driver.
Any idea?
Thanks
Where you have a string 2016-09-01 00:00:00+0000, instead use new Date('2016-09-01 00:00:00+0000').

How can I do Multiple Queries (build out a DB) in Node.JS?

Really new to node.js and relatively new to PostgreSQL. For some reason this only creates the first table, not the second. I am probably totally doing this wrong. I can't seem to find any code examples that are similar enough to extrapolate an answer from. I want to create two tables. It seems I can only run one query and that's it. No more. It's suggested that my connection ends before the second can be executed. I don't know why. I can't find a single example of doing this in a dozen different Google searches.
var client = new pg.Client(connectionString);
client.connect();
var query = client.query('CREATE TABLE preferences(id SERIAL PRIMARY KEY, food VARCHAR(40) not null, preferred BOOLEAN)');
client.query('CREATE TABLE foods(id SERIAL PRIMARY KEY, food VARCHAR(40) not null, type VARCHAR(40) not null, spicy BOOLEAN, spiciness VARCHAR(10)');
query.on('end', function() { client.end(); });
I had doubts that I could just do another client.query and have it all execute at the end. I took this example out of a tutorial but the second table I added. So I'm just playing with it and trying to learn here with this test.
Just to state my ultimate end goal: I wanna ultimately create a whole script for building out the database completely full of all the necessary tables and data.
Error I get no matter what I try:
$ node models/database.js
events.js:85
throw er; // Unhandled 'error' event
^
error: syntax error at end of input
at Connection.parseE (~/Documents/test01/node_modules/pg/lib/connection.js:534:11)
at Connection.parseMessage (~/Documents/test01/node_modules/pg/lib/connection.js:361:17)
at Socket. (~/Documents/test01/node_modules/pg/lib/connection.js:105:22)
at Socket.emit (events.js:107:17)
at readableAddChunk (_stream_readable.js:163:16)
at Socket.Readable.push (_stream_readable.js:126:10)
at TCP.onread (net.js:538:20)
The best way is to place all your requests into a transaction and execute it within the same connection.
Here's how you can do this with the help of pg-promise:
var pgp = require('pg-promise')(/*options*/);
var db = pgp(connection);
db.tx(t => {
return t.batch([
t.none('CREATE TABLE preferences(id SERIAL, etc...'),
t.none('CREATE TABLE foods(id SERIAL PRIMARY KEY, etc...')
]);
})
.then(data => {
// success;
})
.catch(error => {
console.log(error); // print error;
});
Better yet, is to place all SQL into external SQL files, for which the library provides automatic support.
You are trying to perform two asynchronous actions, so you'll need to wait until both actions are complete before you continue. One way of doing this is with a count variable.
var client = new pg.Client(connectionString);
client.connect();
var query1 = client.query('CREATE TABLE preferences(id SERIAL PRIMARY KEY, food VARCHAR(40) not null, preferred BOOLEAN)');
var query2 = client.query('CREATE TABLE foods(id SERIAL PRIMARY KEY, food VARCHAR(40) not null, type VARCHAR(40) not null, spicy BOOLEAN, spiciness VARCHAR(10))');
var count = 2;
function endHandler () {
count--; // decrement count by 1
if (count === 0) {
// two queries have ended, lets close the connection.
client.end();
}
}
query1.on('end', endHandler);
query2.on('end', endHandler);
Note that in this case you could likely just combine the two statements into one, assuming postgres supports that and that functionality is enabled in your system.
client.query('CREATE TABLE preferences(id SERIAL PRIMARY KEY, food VARCHAR(40) not null, preferred BOOLEAN);CREATE TABLE foods(id SERIAL PRIMARY KEY, food VARCHAR(40) not null, type VARCHAR(40) not null, spicy BOOLEAN, spiciness VARCHAR(10));')
You need to manage multiple 'query' variables. In your case the code could look like this:
var client = new pg.Client(connectionString);
client.connect();
var queryPreferences = client.query('CREATE TABLE preferences(id SERIAL PRIMARY KEY, food VARCHAR(40) not null, preferred BOOLEAN)');
var foods = function() {
var queryFoods = client.query('CREATE TABLE foods(id SERIAL PRIMARY KEY, food VARCHAR(40) not null, type VARCHAR(40) not null, spicy BOOLEAN, spiciness VARCHAR(10)');
queryFoods.on('end', function() { client.end(); });
}
queryPreferences.on('end', foods);
In this way you are running the second query until you finish the first one and then you are closing the connection.

Cassandra - NodeJS - Issue while retrieving map type values

I am using helenus in my node-js project to get/set values in cassandra. I have a MapType field inside my Table, but when I retrieve the value from the table, I get an empty key-value set.
Below is the schema for my table
CREATE TABLE datapoints (
id uuid PRIMARY KEY,
created_at timestamp,
properties map<text,text>
);
I have inserted the values from cql using the query below
INSERT INTO datapoints (id, properties) VALUES (24053e20-63e9-11e3-8d81-0002a5d5c51b, { 'fruit' : 'apple', 'band' : 'Beatles' });
Below is my nodejs code:
var helenus = require('/usr/local/lib/node_modules/helenus')
var pool = new helenus.ConnectionPool({
hosts : ['localhost:9160'],
keyspace : 'mykeyspace',
timeout : 3000
});
pool.connect(function(err, keyspace){
if(err){
console.log("connect me error")
throw(err);
} else {
pool.cql("SELECT * FROM datapoints", [], function(err,results){
console.log("results", results)
results.forEach(function(row){
props = row.get('properties').value;
var id = row.get('id').value;
console.log("properties", props);
console.log("id", id);
});
})
}
});
The line console.log("properties", props); returns me a function, and when I call that function, I get an empty key value set. Please help.
It seems there was an issue with the deserialization of the collection types. The pull request that was made in the past broke the deserialization. I just pushed a fix to version 0.6.8 that should take care of this.

Resources