我是 Cassandra 的新手,我不太清楚我的数据模型是否正确。我试图根据我想在我的应用程序中进行的查询来创建它。我想创建和更新书籍对象,我想按作者和出版日期查找书籍。我正在使用适用于 Cassandra 的 DataStax Node.js 驱动程序(使用 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);
上面的代码运行良好;我可以按作者和在出版时指定日期范围来获取书籍列表。bookMapper 正在映射两个表(books_by_author、books),所以我使用它来进行所有数据库查询。
然后我遇到了问题。我在我的应用程序中创建了一本书,但我给了它错误的发布日期,我想更改它。所以,为了看看它是如何完成的,我创建了一个单元测试,将一本书保存到数据库中,然后尝试使用 bookMapper.update 来更新这本书的 datePublished 属性。这是我试图实现的一些伪代码:
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!
使用批处理来删除和插入似乎有效,因此 remove 是对 DB 的最后一次调用,我将这些语句添加到我的更改数组中的顺序并不重要,它会删除导致我最后一个 get 语句返回 null 的书。
我想使用批处理来使操作原子化。我不想最终陷入这样一种情况:我首先删除现有书籍,然后在没有批处理的情况下将新书插入单独的数据库调用中,因为如果在删除之后但插入之前发生一些错误,那么我将丢失我的书籍数据数据库。
我的问题:当更新的属性恰好是主键的一部分时,更新书籍数据的正确方法是什么?谢谢你。