Skip to content

Commit ae4fbd3

Browse files
committed
Add map item deletion in cqlengine
1 parent a16f5f7 commit ae4fbd3

2 files changed

Lines changed: 21 additions & 4 deletions

File tree

cassandra/cqlengine/query.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,6 +1252,9 @@ class Row(Model):
12521252
12531253
# add items to a map
12541254
Row.objects(row_id=5).update(map_column__update={1: 2, 3: 4})
1255+
1256+
# remove items from a map
1257+
Row.objects(row_id=5).update(map_column__remove={1, 2})
12551258
"""
12561259
if not values:
12571260
return
@@ -1270,8 +1273,14 @@ class Row(Model):
12701273
if col.is_primary_key:
12711274
raise ValidationError("Cannot apply update to primary key '{0}' for {1}.{2}".format(col_name, self.__module__, self.model.__name__))
12721275

1273-
# we should not provide default values in this use case.
1274-
val = col.validate(val)
1276+
if col_op == 'remove' and isinstance(col, columns.Map):
1277+
if not isinstance(val, set):
1278+
raise ValidationError(
1279+
"Cannot apply update operation '{0}' on column '{1}' with value '{2}'. A set is required.".format(col_op, col_name, val))
1280+
val = {v: None for v in val}
1281+
else:
1282+
# we should not provide default values in this use case.
1283+
val = col.validate(val)
12751284

12761285
if val is None:
12771286
nulled_columns.add(col_name)

cassandra/cqlengine/statements.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -359,10 +359,13 @@ class MapUpdateClause(ContainerUpdateClause):
359359
col_type = columns.Map
360360

361361
_updates = None
362+
_removals = None
362363

363364
def _analyze(self):
364365
if self._operation == "update":
365366
self._updates = self.value.keys()
367+
elif self._operation == "remove":
368+
self._removals = {v for v in self.value.keys()}
366369
else:
367370
if self.previous is None:
368371
self._updates = sorted([k for k, v in self.value.items()])
@@ -373,12 +376,14 @@ def _analyze(self):
373376
def get_context_size(self):
374377
if self.is_assignment:
375378
return 1
376-
return len(self._updates or []) * 2
379+
return int((len(self._updates or []) * 2) + int(bool(self._removals)))
377380

378381
def update_context(self, ctx):
379382
ctx_id = self.context_id
380383
if self.is_assignment:
381384
ctx[str(ctx_id)] = {}
385+
elif self._removals is not None:
386+
ctx[str(ctx_id)] = self._removals
382387
else:
383388
for key in self._updates or []:
384389
val = self.value.get(key)
@@ -390,14 +395,17 @@ def update_context(self, ctx):
390395
def is_assignment(self):
391396
if not self._analyzed:
392397
self._analyze()
393-
return self.previous is None and not self._updates
398+
return self.previous is None and not self._updates and not self._removals
394399

395400
def __unicode__(self):
396401
qs = []
397402

398403
ctx_id = self.context_id
399404
if self.is_assignment:
400405
qs += ['"{0}" = %({1})s'.format(self.field, ctx_id)]
406+
elif self._removals is not None:
407+
qs += ['"{0}" = "{0}" - %({1})s'.format(self.field, ctx_id)]
408+
ctx_id += 1
401409
else:
402410
for _ in self._updates or []:
403411
qs += ['"{0}"[%({1})s] = %({2})s'.format(self.field, ctx_id, ctx_id + 1)]

0 commit comments

Comments
 (0)