问题
I'm trying to use Mobile Backend Starter in my Android application. In order to do that I need to store some data in the Datastore.
I'm using the provided object CloudEntity
but I can only consistently insert and read String
.
That's the example code I used to send data:
CloudEntity entity = new CloudEntity(TEST_KIND_NAME);
entity.put(KEY_DATE, new Date(System.currentTimeMillis()));
entity.put(KEY_CALENDAR, Calendar.getInstance());
entity.put(KEY_LONG, Long.valueOf(Long.MAX_VALUE));
entity.put(KEY_INTEGER, Integer.valueOf(Integer.MAX_VALUE));
getCloudBackend().insert(entity, simpleHandler);
and this is how I read the data back (next code goes in the onComplete
in the CloudBackendHandler
:
StringBuffer strBuff = new StringBuffer();
strBuff.append("Inserted: \n");
strBuff.append("\tId = " + result.getId() + "\n");
Object o;
o = result.get(KEY_DATE);
strBuff.append("\tDate was retrieved as : " + ((o == null)? "null" : o.getClass().getName()) + "\n");
o = result.get(KEY_CALENDAR);
strBuff.append("\tCalendar was retrieved as : " + ((o == null)? "null" : o.getClass().getName()) + "\n");
o = result.get(KEY_LONG);
strBuff.append("\tLong was retrieved as : " + ((o == null)? "null" : o.getClass().getName()) + "\n");
o = result.get(KEY_INTEGER);
strBuff.append("\tInteger was retrieved as : " + ((o == null)? "null" : o.getClass().getName()) + "\n");
o = result.get(KEY_BOOLEAN);
strBuff.append("\tBoolean was retrieved as : " + ((o == null)? "null" : o.getClass().getName()) + "\n");
mTvInfo.setText(strBuff);
And what I get as result is:
Data inserted as Date
and Calendar
returns null
.
Data inserted as Integer
returns BigDecimal
.
Data inserted as Long
returns a String
.
My question is: Can I send (and read back) other data than `String? And if so. How?
回答1:
After some time experimenting with the Android Mobile Backed Starter I found a link to a "sort of" (very limited) documentation: Mobile Backend Starter.
What I found is that if you send an Integer
(and if I trust de documentation a Float
or a Double
) it is stored in the DataStore as a numeric field. And is returned as a BigDecimal
when you send a query (through ClouldQuery
).
Nevertheless, if you pass a Long
as a property in your CloudEntity
, it will be stored as a String
in the DataStore and returned as such. And this is not trivial, as String
fields has limitations on allowed comparisons.
If you send a DateTime, the documentation tells you that you will get back a String
, but don't tells you that it will be stored in the DataStore as a String
too.
This is important because you can't do all the comparisons with String
s. It is only allowed the equality (and ne) comparison (you can't test a greater than filter over a String
property in your queries). So you can't store timestamps as Long
(will be converted to String
and you won't be able to compare them) and you can't set timestamps as DateTime
for the same reason. And you just can't store Date nor Calendar objects.
Anyway every CloudEntity
has two DateTime
porperties by default CloudEntity.PROP_CREATED_AT
and CloudEntity.PROP_UPDATED_AT
. You can set a query filter with this fields. To do so you need to set the filter as
CloudQuery myQuery = new CloudQuery(<your kind name>);
myQuery.set<things>...
DateTime dateTime = new DateTime(<the time you want>);
myQuery.setFilter(F.gt(CloudEntity.PROP_UPDATED_AT, dateTime));
You need to use a DateTime
for the comparison. If you are courious, you can NOT use Date
instead of DateTime
for this comparation. You would get this error:
com.google.api.client.googleapis.json.GoogleJsonResponseException: 400 Bad Request
{
"code": 400,
"errors": [
{
"domain": "global",
"message": "java.lang.IllegalArgumentException: _updatedAt:java.util.LinkedHashMap is not a supported property type.",
"reason": "badRequest"
}
],
"message": "java.lang.IllegalArgumentException: _updatedAt: java.util.LinkedHashMap is not a supported property type."
...
Other weird thing is that, aparently, you can not do comparisons with dateTime = new DateTime(0)
(i.e. 1970-01-01T01:00:00.000+01:00) as the backend receives the query as:
query: (_kindName:"Data") AND ( _updatedAt > "1970-01-01T01:00:00.000+01:00" ), schema: {_kindName=STRING, _updatedAt=STRING}
Won't give any error but will return nothing list: result: null
. Looks like it treats the comparison as a String
. If you use DateTime dateTime = new DateTime(1)
instead, you will send a query as:
list: executing query: {filterDto={operator=GT, values=[_updatedAt, 1970-01-01T01:00:00.001+01:00]},
looks the same as before but the backend will execute the query as:
query: (_kindName:"Data") AND ( _updatedAt > 1 ), schema: {_kindName=STRING, _updatedAt=DOUBLE}
As I see a DOUBLE
I tried submit a Double
instead a DateTime
, but it doesn't work (no error but not result).
But, GOOD NEWS EVERYONE! We can do the comparison with an Integer
as:
Integer anInteger = Integer.valueOf(0);
myQuery.setFilter(F.gt(CloudEntity.PROP_UPDATED_AT, anInteger));
the backend sees:
query: (_kindName:"Data") AND ( _updatedAt > 0 ), schema: {_kindName=STRING, _updatedAt=INT32}
and we'll get the expected value (all the entities updated since 1970-01-01T01:00:00.000+01:00)
Sorry for my mistakes (my english isn't good) and I hope this can help someone and, at least, save him some time.
来源:https://stackoverflow.com/questions/18150297/sending-and-retreaving-data-from-datastore-with-mobile-backend-starter