Skip to content

Commit 614cd79

Browse files
author
Chris Rossi
committed
Store strings as strings in Datastore.
Fixes issue #32. Note that strings were not being stored as base64. That's just how the Google Console represents binary data in the UI. Strings were being stored as binary data, however, and that has been fixed. Strings were also being stored as binary data in legacy NDB. Legacy databases with strings stored as binary will still work with this version of NDB as we can handle getting ``bytes`` or ``str`` from ``google.cloud.datastore``.
1 parent e62936f commit 614cd79

File tree

5 files changed

+28
-16
lines changed

5 files changed

+28
-16
lines changed

packages/google-cloud-ndb/MIGRATION_NOTES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ The primary differences come from:
125125
This method shouldn't generally be called by user code, anyway.
126126
- `Future.state` is omitted as it is redundant. Call `Future.done()` or
127127
`Future.running()` to get the state of a future.
128+
- `StringProperty` properties were previously stored as blobs
129+
(entity_pb2.Value.blob_value) in Datastore. They are now properly stored as
130+
strings (entity_pb2.Value.string_value). At read time, a `StringProperty`
131+
will accept either a string or blob value, so compatibility is maintained
132+
with legacy databases.
128133

129134
## Privatization
130135

packages/google-cloud-ndb/src/google/cloud/ndb/model.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2074,7 +2074,7 @@ def _db_get_value(self, v, unused_p):
20742074
raise exceptions.NoLongerImplementedError()
20752075

20762076

2077-
class TextProperty(BlobProperty):
2077+
class TextProperty(Property):
20782078
"""An unindexed property that contains UTF-8 encoded text values.
20792079
20802080
A :class:`TextProperty` is intended for values of unlimited length, hence
@@ -2172,12 +2172,12 @@ def _to_base_type(self, value):
21722172
value (Union[bytes, str]): The value to be converted.
21732173
21742174
Returns:
2175-
Optional[bytes]: The converted value. If ``value`` is a
2176-
:class:`str`, this will return the UTF-8 encoded bytes for it.
2175+
Optional[str]: The converted value. If ``value`` is a
2176+
:class:`bytes`, this will return the UTF-8 decoded ``str`` for it.
21772177
Otherwise, it will return :data:`None`.
21782178
"""
2179-
if isinstance(value, str):
2180-
return value.encode("utf-8")
2179+
if isinstance(value, bytes):
2180+
return value.decode("utf-8")
21812181

21822182
def _from_base_type(self, value):
21832183
"""Convert a value from the "base" value type for this property.

packages/google-cloud-ndb/tests/system/test_system.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,19 @@ def client_context():
5151
@pytest.mark.usefixtures("client_context")
5252
def test_retrieve_entity(ds_entity):
5353
entity_id = test_utils.system.unique_resource_id()
54-
ds_entity("SomeKind", entity_id, foo=42, bar="none")
54+
ds_entity("SomeKind", entity_id, foo=42, bar="none", baz=b"night")
5555

5656
class SomeKind(ndb.Model):
5757
foo = ndb.IntegerProperty()
5858
bar = ndb.StringProperty()
59+
baz = ndb.StringProperty()
5960

6061
key = ndb.Key("SomeKind", entity_id)
6162
entity = key.get()
6263
assert isinstance(entity, SomeKind)
6364
assert entity.foo == 42
6465
assert entity.bar == "none"
66+
assert entity.baz == "night"
6567

6668

6769
@pytest.mark.usefixtures("client_context")
@@ -137,6 +139,11 @@ class SomeKind(ndb.Model):
137139
assert retrieved.foo == 42
138140
assert retrieved.bar == "none"
139141

142+
# Make sure strings are stored as strings in datastore
143+
ds_client = datastore.Client()
144+
ds_entity = ds_client.get(key._key)
145+
assert ds_entity["bar"] == "none"
146+
140147

141148
@pytest.mark.usefixtures("client_context")
142149
def test_update_entity(ds_entity):

packages/google-cloud-ndb/tests/unit/test_model.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1741,13 +1741,13 @@ def test__validate_bad_type():
17411741
@staticmethod
17421742
def test__to_base_type():
17431743
prop = model.TextProperty(name="text")
1744-
assert prop._to_base_type(b"abc") is None
1744+
assert prop._to_base_type("abc") is None
17451745

17461746
@staticmethod
17471747
def test__to_base_type_converted():
17481748
prop = model.TextProperty(name="text")
17491749
value = "\N{snowman}"
1750-
assert prop._to_base_type(value) == b"\xe2\x98\x83"
1750+
assert prop._to_base_type(b"\xe2\x98\x83") == value
17511751

17521752
@staticmethod
17531753
def test__from_base_type():
@@ -3001,9 +3001,9 @@ class ThisKind(model.Model):
30013001
assert entity_pb.properties["b"].null_value == 0
30023002
assert pickle.loads(entity_pb.properties["c"].blob_value) == gherkin
30033003
d_values = entity_pb.properties["d"].array_value.values
3004-
assert d_values[0].blob_value == b"foo"
3005-
assert d_values[1].blob_value == b"bar"
3006-
assert d_values[2].blob_value == b"baz"
3004+
assert d_values[0].string_value == "foo"
3005+
assert d_values[1].string_value == "bar"
3006+
assert d_values[2].string_value == "baz"
30073007
e_values = entity_pb.properties["e"].array_value.values
30083008
assert pickle.loads(e_values[0].blob_value) == gherkin
30093009
assert pickle.loads(e_values[1].blob_value) == dill
@@ -3018,7 +3018,7 @@ class ThisKind(model.Model):
30183018
entity = ThisKind(key="not the key", _key=key)
30193019

30203020
entity_pb = model._entity_to_protobuf(entity)
3021-
assert entity_pb.properties["key"].blob_value == b"not the key"
3021+
assert entity_pb.properties["key"].string_value == "not the key"
30223022
assert entity_pb.key.path[0].kind == "ThisKind"
30233023
assert entity_pb.key.path[0].id == 123
30243024

@@ -3053,9 +3053,9 @@ class ThisKind(ThatKind):
30533053
assert entity_pb.properties["b"].null_value == 0
30543054
assert pickle.loads(entity_pb.properties["c"].blob_value) == gherkin
30553055
d_values = entity_pb.properties["d"].array_value.values
3056-
assert d_values[0].blob_value == b"foo"
3057-
assert d_values[1].blob_value == b"bar"
3058-
assert d_values[2].blob_value == b"baz"
3056+
assert d_values[0].string_value == "foo"
3057+
assert d_values[1].string_value == "bar"
3058+
assert d_values[2].string_value == "baz"
30593059
e_values = entity_pb.properties["e"].array_value.values
30603060
assert pickle.loads(e_values[0].blob_value) == gherkin
30613061
assert pickle.loads(e_values[1].blob_value) == dill

packages/google-cloud-ndb/tests/unit/test_polymodel.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ class Cat(Animal):
9696

9797
assert Animal._default_filters() == ()
9898
assert Cat._default_filters() == (
99-
query.FilterNode("class", "=", b"Cat"),
99+
query.FilterNode("class", "=", "Cat"),
100100
)
101101

102102
@staticmethod

0 commit comments

Comments
 (0)