Skip to content

Commit ae80fd0

Browse files
committed
Support deleting without providing full primary key
1 parent 804e0bb commit ae80fd0

File tree

2 files changed

+31
-17
lines changed

2 files changed

+31
-17
lines changed

lib/mapping/object-selector.js

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -255,22 +255,33 @@ class ObjectSelector {
255255
throw new Error(`Table "${info.tables[i].name}" could not be retrieved`);
256256
}
257257

258-
// All partition and clustering keys from the table should be included in the document
259-
const keyNames = table.partitionKeys.concat(table.clusteringKeys).map(k => k.name);
260-
const columns = propertiesInfo.map(p => p.columnName);
258+
if (keysAreIncluded(table.partitionKeys, propertiesInfo) !== keyMatches.all) {
259+
return false;
260+
}
261261

262-
for (let i = 0; i < keyNames.length; i++) {
263-
if (columns.indexOf(keyNames[i]) === -1) {
262+
const clusteringKeyNames = table.clusteringKeys.map(k => k.name);
263+
const queriedClusteringKeyNames = propertiesInfo.filter(k => clusteringKeyNames.includes(k.columnName)).map(k => k.columnName);
264+
for (const queriedClusteringKeyName of queriedClusteringKeyNames) {
265+
if (clusteringKeyNames.indexOf(queriedClusteringKeyName) >= queriedClusteringKeyNames.length) {
266+
// One of the clustering columns preceding queriedClusteringKeyName is omitted
264267
return false;
265268
}
266269
}
267270

271+
// The filter makes sure that the column doesn't appear in `doc` (but it may appear in `docInfo`)
272+
const queriedColumnNames = propertiesInfo.filter(p => p.value).map(p => p.columnName);
273+
const primaryKeyColumnNames = table.partitionKeys.concat(table.clusteringKeys).map(k => k.name);
274+
if (queriedColumnNames.some(queriedColumnName => !primaryKeyColumnNames.includes(queriedColumnName))) {
275+
// Only primary key columns are allowed
276+
return false;
277+
}
278+
268279
// "when" conditions should be contained in the table
269280
return when.reduce((acc, p) => acc && table.columnsByName[p.columnName] !== undefined, true);
270281
});
271282

272283
if (filteredTables.length === 0) {
273-
let message = `No table matches (all PKs have to be specified) fields: [${
284+
let message = `No table matches (must specify all partition key and top-level clustering columns) fields: [${
274285
propertiesInfo.map(p => p.columnName)}]`;
275286

276287
if (when.length > 0) {

test/unit/mapping/model-mapper-mutation-tests.js

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -273,43 +273,46 @@ describe('ModelMapper', () => {
273273
it('should throw an error when filter or conditions are not valid', () => testErrors('remove', [
274274
{
275275
doc: { id1: 'x', notAValidProp: 'y' },
276-
message: 'No table matches (all PKs have to be specified) fields: [id1,notAValidProp]'
276+
message: 'No table matches (must specify all partition key and top-level clustering columns) fields: [id1,notAValidProp]'
277277
}, {
278278
doc: { id1: 'x'},
279279
docInfo: { fields: ['notAValidProp'] },
280-
message: 'No table matches (all PKs have to be specified) fields: [notAValidProp]'
280+
message: 'No table matches (must specify all partition key and top-level clustering columns) fields: [notAValidProp]'
281281
}, {
282282
doc: { id1: 'x', name: 'y' },
283-
message: 'No table matches (all PKs have to be specified) fields: [id1,name]'
283+
message: 'No table matches (must specify all partition key and top-level clustering columns) fields: [id1,name]'
284284
}, {
285-
doc: { id1: 'x', id2: 'y', name: 'z'},
285+
doc: { id1: 'x', id2: 'y'},
286286
docInfo: { when: { notAValidProp: 'm'} },
287-
message: 'No table matches (all PKs have to be specified) fields: [id1,id2,name]; condition: [notAValidProp]'
287+
message: 'No table matches (must specify all partition key and top-level clustering columns) fields: [id1,id2]; condition: [notAValidProp]'
288288
}, {
289289
doc: {},
290290
message: 'Expected object with keys'
291+
}, {
292+
doc: { id1: 'x', id3: 'y' },
293+
message: 'No table matches (must specify all partition key and top-level clustering columns) fields: [id1,id3]'
291294
}
292295
]));
293296

294297
it('should generate the query, params and set the idempotency', () => testQueries('remove', [
295298
{
296-
doc: { id1: 'x', 'id2': 'y' },
299+
doc: { id1: 'x', id2: 'y' },
297300
query: 'DELETE FROM ks1.table1 WHERE "id1" = ? AND "id2" = ?',
298301
params: [ 'x', 'y' ]
299302
}, {
300-
doc: { id1: 'x', 'id2': 'y' },
303+
doc: { id1: 'x', id2: 'y' },
301304
docInfo: { when: { name: 'a' }},
302305
query: 'DELETE FROM ks1.table1 WHERE "id1" = ? AND "id2" = ? IF "name" = ?',
303306
params: [ 'x', 'y', 'a' ],
304307
isIdempotent: false
305308
}, {
306-
doc: { id1: 'x', 'id2': 'y' },
309+
doc: { id1: 'x', id2: 'y' },
307310
docInfo: { ifExists: true },
308311
query: 'DELETE FROM ks1.table1 WHERE "id1" = ? AND "id2" = ? IF EXISTS',
309312
params: [ 'x', 'y' ],
310313
isIdempotent: false
311314
}, {
312-
doc: { id1: 'x', 'id2': 'y' },
315+
doc: { id1: 'x', id2: 'y' },
313316
docInfo: { fields: [ 'id1', 'id2', 'name' ], deleteOnlyColumns: true },
314317
query: 'DELETE "name" FROM ks1.table1 WHERE "id1" = ? AND "id2" = ?',
315318
params: [ 'x', 'y' ]
@@ -347,8 +350,8 @@ describe('ModelMapper', () => {
347350

348351
function testErrors(methodName, items) {
349352
return Promise.all(items.map(item => {
350-
const columns = [ 'id1', 'id2', 'name'];
351-
const clientInfo = mapperTestHelper.getClient(columns, [ 1, 1 ], 'ks1');
353+
const columns = [ 'id1', 'id2', 'id3', 'name'];
354+
const clientInfo = mapperTestHelper.getClient(columns, [ 1, 2 ], 'ks1');
352355
const modelMapper = mapperTestHelper.getModelMapper(clientInfo);
353356

354357
let catchCalled = false;

0 commit comments

Comments
 (0)