Skip to content

Commit

Permalink
feat: add update_multiple operation
Browse files Browse the repository at this point in the history
  • Loading branch information
msiemens committed Nov 14, 2020
1 parent 77fa426 commit ccf1c3f
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 1 deletion.
15 changes: 15 additions & 0 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,21 @@ Of course you also can write your own operations:
...
>>> db.update(your_operation(arguments), query)

In order to perform multiple update operations at once, you can use the
``update_multiple`` method like this:

>>> db.update_muliple([
... ({'int': 2}, where('char') == 'a'),
... ({'int': 4}, where('char') == 'b'),
... ])

You also can use mix normal updates with update operations:

>>> db.update_muliple([
... ({'int': 2}, where('char') == 'a'),
... ({delete('int'), where('char') == 'b'),
... ])

Data access and modification
----------------------------

Expand Down
13 changes: 13 additions & 0 deletions tests/test_tinydb.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,19 @@ def test_update_ids(db: TinyDB):
assert db.count(where('int') == 2) == 2


def test_update_multiple(db: TinyDB):
assert len(db) == 3

db.update_muliple([
({'int': 2}, where('char') == 'a'),
({'int': 4}, where('char') == 'b'),
])

assert db.count(where('int') == 1) == 1
assert db.count(where('int') == 2) == 1
assert db.count(where('int') == 4) == 1


def test_upsert(db: TinyDB):
assert len(db) == 3

Expand Down
55 changes: 54 additions & 1 deletion tinydb/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
Mapping,
Optional,
Union,
cast
cast,
Tuple
)

from .storages import Storage
Expand Down Expand Up @@ -389,6 +390,58 @@ def updater(table: dict):

return updated_ids

def update_multiple(
self,
updates: Iterable[
Tuple[Union[Mapping, Callable[[Mapping], None]], Query]
],
) -> List[int]:
"""
Update all matching documents to have a given set of fields.
:returns: a list containing the updated document's ID
"""

# Define the function that will perform the update
def perform_update(fields, table, doc_id):
if callable(fields):
# Update documents by calling the update function provided
# by the user
fields(table[doc_id])
else:
# Update documents by setting all fields from the provided
# data
table[doc_id].update(fields)

# Perform the update operation for documents specified by a query

# Collect affected doc_ids
updated_ids = []

def updater(table: dict):
# We need to convert the keys iterator to a list because
# we may remove entries from the ``table`` dict during
# iteration and doing this without the list conversion would
# result in an exception (RuntimeError: dictionary changed size
# during iteration)
for doc_id in list(table.keys()):
for fields, cond in updates:
_cond = cast('Query', cond)

# Pass through all documents to find documents matching the
# query. Call the processing callback with the document ID
if _cond(table[doc_id]):
# Add ID to list of updated documents
updated_ids.append(doc_id)

# Perform the update (see above)
perform_update(fields, table, doc_id)

# Perform the update operation (see _update_table for details)
self._update_table(updater)

return updated_ids

def upsert(self, document: Mapping, cond: Query) -> List[int]:
"""
Update a document, if it exist, insert it otherwise.
Expand Down

0 comments on commit ccf1c3f

Please sign in to comment.