Skip to content

Commit

Permalink
Merge pull request #38 from citrusvanilla/issue_27
Browse files Browse the repository at this point in the history
issue #27
  • Loading branch information
citrusvanilla committed Mar 27, 2023
2 parents 7f78397 + 58cfbed commit 7bd0b38
Show file tree
Hide file tree
Showing 9 changed files with 244 additions and 26 deletions.
9 changes: 7 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,18 @@ Quick Links
Recent Updates
**************

v0.4.0 - March 27, 2023
=======================

* Tags and Fields can be removed from individual points. See `the documentation <https://tinyflux.readthedocs.io/en/latest/updating-data.html#removing-tags-and-fields-with-update>`__ for more (resolves issue #27).

v0.3.1 - March 27, 2023
=========================
=======================

* Fixed bug that allowed user to delete key/field tags with `.update()` and `.update_all()` (resolves issue #36).

v0.3.0 - March 23, 2023
=========================
=======================

* Tag and field keys can be compacted when using CSVStorage, saving potentially many bytes per Point (resolves issue #32).
* Fixed bug that causes tag values of `""` to be serialized as `"_none"` (resolves issue #33).
Expand Down
6 changes: 6 additions & 0 deletions docs/source/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
Changelog
=========

v0.4.0 - March 27, 2023
^^^^^^^^^^^^^^^^^^^^^^^

* Tags and Fields can be removed from individual points. See `the documentation <https://tinyflux.readthedocs.io/en/latest/updating-data.html#removing-tags-and-fields-with-update>`__ for more (resolves issue #27).


v0.3.1 (2023-3-27)
^^^^^^^^^^^^^^^^^^

Expand Down
1 change: 0 additions & 1 deletion docs/source/querying-data.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

Querying Data
=============

Expand Down
36 changes: 34 additions & 2 deletions docs/source/removing-data.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Removing Points
===============

TinyFlux supports the removal of data with two methods. To remove by query, the ``remove()`` method is provided, and to remove all, use the ``remove_all()`` method. See below for examples.
TinyFlux supports the removal of points with two methods. To remove by query, the ``remove()`` method is provided, and to remove all, use the ``remove_all()`` method. See below for examples.

.. note::

Expand Down Expand Up @@ -29,7 +29,7 @@ To remove everything in the database , invoke ``remove_all()``:
Like all other operations in TinyFlux, you cannot roll back the actions of ``remove()`` or ``remove_all()``. There is no confirmation step, no access-control mechanism that prevents non-admins from performing this action, nor are there automatic snapshots stored anywhere. If you need these kinds of features, TinyFlux is not for you.


to recap, these are the two methods supporting the removal of data.
To recap, these are the two methods supporting the removal of data.

+------------------------+-----------------------------------------------+
| **Methods** |
Expand All @@ -38,3 +38,35 @@ to recap, these are the two methods supporting the removal of data.
+------------------------+-----------------------------------------------+
| ``db.remove_all()`` | Remove all points. |
+------------------------+-----------------------------------------------+

Removing Tags and Fields
========================

TinyFlux supports the removal of individual tag and field key/values through the `unset_tags` and `unset_fields` arguments to `.update()` and `.update_all()`. The values can be either individual strings, or lists of strings. See below for examples.

The following will remove all tags with the key of "city" from the database:

>>> db.update_all(unset_tags="city")

The following will remove all tags with the keys of "state" and "country" from the database:

>>> db.update_all(unset_tags=["state", "country"])

The following will remove all tags with the key of "temperature" from all Points in the "bedroom" measurement:

>>> db.update(MeasurementQuery() == "bedroom", unset_tags=["temperature"])

.. warning::

Like all other operations in TinyFlux, you cannot roll back the actions of ``update()`` or ``update_all()``. There is no confirmation step, no access-control mechanism that prevents non-admins from performing this action, nor are there automatic snapshots stored anywhere. If you need these kinds of features, TinyFlux is not for you.


To recap, these are the two methods supporting the removal of individual tags and fields from points.

+------------------------------------------------------------+------------------------------------------------------------+
| **Methods** |
+------------------------------------------------------------+------------------------------------------------------------+
| ``db.update(query, unset_tags=..., unset_fields=...)`` | Remove the tags and fields from points matching the query. |
+------------------------------------------------------------+------------------------------------------------------------+
| ``db.update_all(query, unset_tags=..., unset_fields=...)`` | Remove specified tags and fields from all points. |
+------------------------------------------------------------+------------------------------------------------------------+
34 changes: 33 additions & 1 deletion docs/source/updating-data.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Field updates occur much the same way as tags. To update all items in the datab

.. note::

Updating data with `.update()` or `.update_all()` will not remove fields or tags, even if they are not returned when using a Callable as the updater. This is consistent with the Python `dict API <https://docs.python.org/3/library/stdtypes.html#dict.update>`_, in which keys can be overwritten, but not deleted.
Updating data with `.update()` or `.update_all()` through the `tags` or `fields` arguments will not remove tags or fields, even if they are not returned when using a Callable as the updater. This is consistent with the Python `dict API <https://docs.python.org/3/library/stdtypes.html#dict.update>`_, in which keys can be overwritten, but not deleted. To remove tags and fields completely, see :ref:`Removing Tags and Fields with Update` below.

.. warning::

Expand All @@ -56,3 +56,35 @@ to recap, these are the two methods supporting the updating of data.
+------------------------------------------+-----------------------------------------------------+
| ``db.update_all(...)`` | Update all points. |
+------------------------------------------+-----------------------------------------------------+

Removing Tags and Fields with Update
====================================

TinyFlux supports the removal of individual tag and field key/values through the `unset_tags` and `unset_fields` arguments to `.update()` and `.update_all()`. The values can be either individual strings, or lists of strings. See below for examples.

The following will remove all tags with the key of "city" from the database:

>>> db.update_all(unset_tags="city")

The following will remove all tags with the keys of "state" and "country" from the database:

>>> db.update_all(unset_tags=["state", "country"])

The following will remove all tags with the key of "temperature" from all Points in the "bedroom" measurement:

>>> db.update(MeasurementQuery() == "bedroom", unset_tags=["temperature"])

.. warning::

Like all other operations in TinyFlux, you cannot roll back the actions of ``update()`` or ``update_all()``. There is no confirmation step, no access-control mechanism that prevents non-admins from performing this action, nor are there automatic snapshots stored anywhere. If you need these kinds of features, TinyFlux is not for you.


To recap, these are the two methods supporting the removal of individual tags and fields from points.

+------------------------------------------------------------+------------------------------------------------------------+
| **Methods** |
+------------------------------------------------------------+------------------------------------------------------------+
| ``db.update(query, unset_tags=..., unset_fields=...)`` | Remove the tags and fields from points matching the query. |
+------------------------------------------------------------+------------------------------------------------------------+
| ``db.update_all(query, unset_tags=..., unset_fields=...)`` | Remove specified tags and fields from all points. |
+------------------------------------------------------------+------------------------------------------------------------+
5 changes: 1 addition & 4 deletions tests/test_measurement.py
Original file line number Diff line number Diff line change
Expand Up @@ -788,10 +788,7 @@ def test_update():
assert len(db) == 3

# Bad invocation.
with pytest.raises(
ValueError,
match="Must include time, measurement, tags, and/or fields.",
):
with pytest.raises(ValueError):
m1.update(TagQuery().tk1 == "tv1")

with pytest.raises(
Expand Down
62 changes: 54 additions & 8 deletions tests/test_tinyflux.py
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,10 @@ def test_search():
db.insert_multiple([p1, p2])
db.reindex()

# Bad invocation.
with pytest.raises(ValueError):
db.search(True)

# Valid index, no candidates.
assert not db.search(TagQuery().b == "B")

Expand Down Expand Up @@ -925,10 +929,7 @@ def test_update():
with pytest.raises(ValueError, match="Measurement must be a string."):
db.update(TagQuery().tk1 == "tv1", measurement={"blehh"})

with pytest.raises(
ValueError,
match="Must include time, measurement, tags, and/or fields.",
):
with pytest.raises(ValueError):
db.update(TagQuery().tk1 == "tv1")

with pytest.raises(
Expand All @@ -948,10 +949,7 @@ def test_update():
db.update(TagQuery().noop(), fields={"a": "a"})

# Missing updates.
with pytest.raises(
ValueError,
match="Must include time, measurement, tags, and/or fields.",
):
with pytest.raises(ValueError):
db.update(TagQuery().city == "la")

# Bad selector.
Expand Down Expand Up @@ -1089,6 +1087,54 @@ def f1(x: dict):
assert "a" in p.fields


def test_update_unset():
"""Test update unset removes fields and tags."""
db = TinyFlux(storage=MemoryStorage)
p1 = Point(tags={"a": "1", "b": "2"}, fields={"a": 1, "b": 2})
p2 = Point(tags={"b": "2", "c": "3"}, fields={"b": 2, "c": 3})
db.insert_multiple([p1, p2])

# bad invocations.
with pytest.raises(ValueError):
db.update_all(unset_fields=True)

with pytest.raises(ValueError):
db.update_all(unset_tags=True)

with pytest.raises(ValueError):
db.update_all(unset_fields=("1", 2))

with pytest.raises(ValueError):
db.update_all(unset_tags=("1", 2))

# Test string param.
assert db.search(FieldQuery().a.exists())
db.update_all(unset_fields="a")
assert not db.search(FieldQuery().a.exists())

assert db.search(TagQuery().a.exists())
db.update_all(unset_tags="a")
assert not db.search(TagQuery().a.exists())

# Test iterable param.
assert db.search(FieldQuery().b.exists())
db.update_all(unset_fields=["b"])
assert not db.search(FieldQuery().b.exists())

assert db.search(TagQuery().b.exists())
db.update_all(unset_tags=["b"])
assert not db.search(TagQuery().b.exists())

assert db.get(TagQuery().c.exists())
db.update(TagQuery().c.exists(), unset_tags=["c"])
assert not db.search(TagQuery().c.exists())

db.insert(Point(tags={"d": "4", "e": "5"}))
assert db.get(TagQuery().d.exists() & TagQuery().e.exists())
db.update_all(unset_tags=["d", "e"])
assert not db.get(TagQuery().d.exists() | TagQuery().e.exists())


def test_multipledbs():
"""Test inserting points into multiple DBs."""
db1 = TinyFlux(storage=MemoryStorage)
Expand Down
Loading

0 comments on commit 7bd0b38

Please sign in to comment.