Я новичок в Cassandra и не совсем уверен, верна ли моя модель данных. Я попытался создать его на основе запросов, которые хочу сделать в своем приложении. Я хочу создавать и обновлять объекты книг, и я хочу найти книги по автору и по дате публикации sh. Я использую драйвер 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);
Приведенный выше код работает хорошо; Я могу получить список книг по автору и указав диапазон дат при публикации. BookMapper отображает обе таблицы (books_by_author, books), поэтому я использую его для выполнения всех запросов к БД.
Затем я столкнулся с проблемами. Я создал книгу в своем приложении, но указал неверную дату публикации sh, и я хотел бы это изменить. Итак, чтобы увидеть, как это можно сделать, я создал модульный тест, который сохраняет книгу в БД, а затем пытается использовать 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 - это последний вызов БД, не имеет значения, в каком порядке я добавляю эти операторы в свой массив изменений , и он удаляет книгу, в результате чего мой последний оператор get возвращает значение null.
Я хотел использовать пакетную обработку, чтобы выполнить операцию atomi c. Я не хочу попадать в ситуацию, когда я сначала удаляю существующую книгу, а затем вставляю новую книгу в отдельные вызовы БД без пакетной обработки, потому что если после удаления, но до вставки произойдет какая-то ошибка, я потеряю данные моей книги из БД.
Мой вопрос: как правильно обновить данные книги, когда обновленное свойство оказывается частью первичного ключа? Спасибо.