Cassandra nodejs驱动,如何正确更新数据

Cassandra nodejs driver, how to update data correctly

我是 Cassandra 的新手,我不太清楚我的数据模型是否正确。我试图根据我想在我的应用程序中进行的查询来创建它。我想创建和更新书籍对象,我想按作者和出版日期查找书籍。我正在使用 DataStax Node.js Cassandra 驱动程序(使用 Typescript),这是我目前的模式:

CREATE TABLE IF NOT EXISTS books_by_author (
    author_id UUID,
    book_id UUID,
    book_name TEXT,
    date_published TIMESTAMP,
    PRIMARY KEY (author_id, date_published);

CREATE TABLE IF NOT EXISTS books (
    book_id uuid PRIMARY KEY,
    book_name text,
    book_description TEXT,
    date_published TIMESTAMP,
    author_id uuid,
    author_name TEXT,
 + many more columns for book details);

将 author_id 和 date_published 作为主键我能够使用 nodejs 驱动程序并在 DataStax 文档的帮助下进行查询:

const q = cassandra.mapping.q;

const results = await this.bookMapper.find(
          {
            authorId: '1', datePublished: q.and(q.gte(start), q.lte(end)), // given timerange for publish date, works fine
          },
          docInfo,
          options);

以上代码运行良好;我可以按作者获取书籍列表,并在出版时指定 运行ge 日期。 bookMapper 正在映射两个表(books_by_author,书籍),所以我用它来进行我所有的数据库查询。

然后我运行进入问题。我在我的应用程序中创建了一本书,但我给了它错误的发布日期,我想更改它。因此,为了了解它是如何完成的,我创建了一个将书籍保存到数据库的单元测试,然后尝试使用 bookMapper.update 更新书籍的出版日期 属性。这是我试图实现的一些伪代码:

const bookId = '123uuid';

const existingBook = new Book({
    id: bookId,
    name: 'The Book',
    datePublished: '2020-07-03T13:00:00.000Z',
    description: 'Book description',
    author: {
      id: '1',
      name: 'A. Author',
    }
});
... // insert existingBook to DB and read book details from DB using bookMapper.get({bookId})

const modifiedBook = new Book({
    id: bookId,
    name: 'The Book',
    datePublished: '2020-07-02T13:00:00.000Z', // modified publish date
    description: 'Modified book description', // modified the book description as well
    author: {
      id: '1',
      name: 'A. Author',
    }
});

await this.bookMapper.update(modifiedBook); // update the book

await this.bookMapper.get({bookId}); // returns the book with data from existingBook, not modifiedBook

await this.bookMapper.find(
          {
            authorId: '1', datePublished: q.and(q.gte(start), q.lte(end)),
          },
          docInfo,
          options); 
// query with author id, returns a list of 2 books, both the existingBook and modifiedBook ??

如您所见,更新实际上在数据库中创建了一个新的书籍行,现在我有 2 本书而不是 1 本书。而且我不知道更新该数据的正确方法是什么。我尝试使用批处理:

let changes = [];
changes.push(this.bookMapper.batching.remove(exisitingBook));
changes.push(this.bookMapper.batching.insert(modifiedBook));
await this.mapper.batch(changes);

const book = await this.bookMapper.get({bookId});
--> book is null!

使用批处理删除和插入似乎有效,因此删除是对数据库的最后一次调用,我将这些语句添加到我的更改数组中的顺序无关紧要,它删除了导致我最后一次获取的书语句 return null.

我想使用批处理使操作原子化。我不想在我首先删除现有书籍然后在单独的数据库调用中插入新书而不进行批处理的情况下结束,因为如果在删除之后但插入之前发生错误,那么我将丢失我的书籍数据数据库。

我的问题:当更新的 属性 恰好是主键的一部分时,更新书籍数据的正确方法是什么?谢谢。

这是 Cassandra 的一个众所周知的“功能”——在批处理中,两个语句获得相同的时间戳,因此 DELETE 操作胜过 INSERT。解决该问题的唯一解决方案是为每个操作显式设置时间戳,DELETE 的时间戳低于 INSERT。我不是 Node.js 开发人员,所以它应该如何在 pseudo-code/CQL 中查看(Node.js 映射器应该支持在语句上设置自定义时间戳):

TS=currentTimestampInMicroseconds
BEGIN BATCH
DELETE FROM table USING TIMESTAMP TS-1 WHERE PK = ... US;
INSERT INTO table (....) VALUES (....) USING TIMESTAMP TS;
APPLY BATCH;