I'm trying the new Android Mobile Backend. I did all the basic samples and now I want to set one or multiple filters. I tried only with one but the getCloudBackend().list returns a null list.
I have in the entity that I filter the parameter with the value. And if I remove the line where I filter, the service returns a list with data.
What I have to do if I want to filter by one parameter? and if I want to filter by the owner? and if I want to put more than one filter?
This is my code:
getCloudBackend().clearAllSubscription();
CloudQuery cq = new CloudQuery("MyEntity");
cq.setSort(CloudEntity.PROP_UPDATED_AT, Order.DESC);
cq.setLimit(100);
q.setFilter(F.eq("myparameter", "myvalue"));
cq.setScope(Scope.FUTURE_AND_PAST);
getCloudBackend().list(cq, handler);
You should post the error (or response) you get in the LogCat and also in the backend log. Filters are a bit tricky. I suggest you to check CloudQuery documentation to see the filter limitations, and CloudEntity to check the data you can send, retrieve and how.
About queries the documentation says:
Queries in the Cloud Backend API have some restrictions that originate
from the App Engine Datastore. By default, App Engine automatically
creates an index for each user defined property (with the exception of
List/Map properties). This index supports the following forms of
queries: - Kind name specified, no filters, no sorting - Queries with
only equality filters (F.eq) on indexed properties with no sorting -
Queries with only inequality filters, such as F.lt, F.ge, on a single
indexed property - Queries with one sort order on one property with no
filters
For example, for String properties you can only check for equality (eq/ne). You can't mix equaltiy filters with inequality ones. i.e. :
F.and(F.eq(PROPERTY_NAME, "Manuel"), F.gt(PROPERTY_AGE, Integer.valueOf(18)));
Will give you an error DatastoreNeedIndexException.
The documentation also says:
All other query forms will raise a DatastoreNeedIndexException on the
backend. To use a more complex form of queries, you need to add an
Index Configuration on the backend.
To do such you need edit datastore-indexes.xml in your backend. In order to do that you can use the command ./appengine-java-sdk/bin/appcfg.sh update_indexes myapp/war.
Looks promising though I haven't tested it yet...
Filters are case-sensitive, in the Guestbook example:
cq.setFilter(F.eq("MESSAGE","fabulous")); -> This won't work... don't let Google Cloud Console fool you.
cq.setFilter(F.eq("message","fabulous")); -> This will work... don't know why is case sensitive.
Let me know if this was the problem.
What I have to do if I want to filter by one parameter?
I think it is better to use getCloudBackend().listByProperty() method instead of getCloudBackend().list()
Example of this method usage:
getCloudBackend().listByProperty("yourKindName", "yourPropertyName", Op.EQ,
yourPropertyValueObject, null, 1, Scope.PAST, yourHandler);
and if I want to filter by the owner?
You can use the listed above method. Just put your owner property name instead of yourPropertyName
and if I want to put more than one filter?
To use multiple filter for one property I created listByPropertyAnd() method in CloudBackendAsync class:
public void listByPropertyAnd(String kindName, String propertyName,
CloudQuery.Order order, int limit, Scope scope,
CloudCallbackHandler<List<CloudEntity>> handler, F... filters) {
CloudQuery cq = new CloudQuery(kindName);
cq.setFilter(F.and(filters));
cq.setSort(propertyName, order);
cq.setLimit(limit);
cq.setScope(scope);
this.list(cq, handler);
}
Example of this method usage:
getCloudBackend().listByPropertyAnd("yourKindName", "yourKindName", Order.DESC,
1000, Scope.FUTURE_AND_PAST, yourHandler,
F.gt("yourKindName", yourFirstValue),
F.lt("yourKindName", yourSecondValue), F.eq("date", mToday));
In this case you can use any number of filters for one property but it should be compliant with datastore indexing rules.
Also it might be useful to read this Java Datastore Filters and this one Mobile Backend Starter - API Guide
Related
I am using Androidviewclient version 11.5.6 and I want to search for multiple names at a time in function findVeiwwithtext function. Is it possible with AVC??
Yes, findViewWithTextOrRaise() accepts a RegexType (see re) as parameter so you can use patterns if your intention is to match more than one View.
As a starting point you can invoke culebra --use-regexps ... and it will generate statements like this
no_id26 = self.vc.findViewWithTextOrRaise(re.compile(u'Phone'))
which you can easily adapt to your needs. For example
no_id26 = self.vc.findViewWithTextOrRaise(re.compile(u'(Phone)|(Settings)'))
this will match Phone or Settings.
I am new to couchbase and I am trying to implement couchbase lite in one of my Android applications. What i am struggling with in particular is the concept of views and the rules for the map function as stated in the docs.
In the database the app stores documents with various doc types. In one query i need to get the entire document by document type ("payments")
and by value of an attribute of the document (doc["approved"] = true)
Hence I would create a view like so:
com.couchbase.lite.View view = database.getView("payments");
if (view.getMap() == null) {
Mapper map = new Mapper() {
#Override
public void map(Map<String, Object> doc, Emitter emitter) {
if (doc.get("type").equals("payments") && doc.get("approved") == true) {
emitter.emit(doc.get("name"), doc);
}
}
};
view.setMap(map, "1");
}
Note that the doc["approved"] value can be updated over time. In one of the rules about map functions in the docs it says:
It must be a "pure" function: ... That means any time it's called with the
same input, it must produce exactly the same output.
Would the implementation of the map function as show above violate that rule?
In the docs it further says :
In particular, avoid these common mistakes: ... Don't make any assumptions
about when the map function is called. That's an implementation detail
of the indexer. (For example, it's not called every time a document
changes.).
Does that mean when the approved status of one of the documents is updated from false to true that the following query not nessesarily contains the updated document? If so what would I need to do to achieve this? I am quite uncertain about what rule that exacly means? Could anyone try to open my eyes please?
What "pure" means is that you cannot use outside state in your map function. All your determinations must be based solely on the parameters that were passed into it. Your map function does not violate this.
I think the missing piece in your understanding is the difference between storage and indexing. You can store revisions of a document to the database, right? That in and of itself will not cause the view's index to be updated. That's what the documentation means by "not called every time a document changes." The index will be updated by default when the next query is run, so the newest state of the document will be output. It could realistically have been changed many times since the last query was run.
How do I pass the arraylist to google cloud endpoint? It doesn't seem to work.
Edit start ----
Here is the signature of the endpoint with arraylist as input
public CollectionResponse<String> listDevices(#Named("devices") ArrayList<String> devices)
However when I iterate over this arraylist, I get all the records condensed into one. So even though I pass 10 strings, I get only one in my endpoint.
Edit end ----
I read somewhere that I should create a wrapper entity for arraylist and then pass it.
Edit start ----
So I created the entity containing the arraylist
#Entity
public class DEviceList {
ArrayList<String> devices;
}
and modified the signature as -
public CollectionResponse<String> listDevices(DeviceList devices)
Is it possible to pass object of DeviceList from client even though it not #Named? Can you provide the syntax?
My understanding is that since it's an entity it cannot be #Named, so while calling I need to inject it.
But google allows only three injected types -
1. com.google.appengine.api.users.User
2. javax.servlet.http.HttpServletRequest
3. javax.servlet.ServletContext
So above signature would not work.
So I changed the signature to -
public CollectionResponse<String> listDevices(HttpServletRequest request)
and inside I could get the entity as
DeviceList deviceList = (DeviceList)request.getAttribute("deviceList");
However I am not sure how to call this endpoint from the android client?
How I do pass the entity object using HTTPServletRequest?
Edit end ----
How do I do that? Can anyone cite an example?
The way you word your question, it seems the call is silently failing. That can't be. You must be receiving some kind of exception or log somewhere which could help you identify your issue better. You could read this article to refresh on cloud endpoint APIs and android.
If you are having trouble passing an arraylist of objects from your client to the API, I would suggest checking some things:
does the argument type in the API match what is being sent from the client? Do they both have access to the class definition?
if the data type inside the arraylist is a primitive, and it still fails, perhaps the advice you read "somewhere" was referring to the need (although I don't think this is the case) to use a wrapper object which simply contains one field - the arraylist, and pass that along the line?
If either the reminder to check your logs/error messages or any other info in this answer helped you resolve the issue, please remember to accept it, but not without first editing your post to explain how you resolved your issue.
I'm looking into using greenDAO for my Android app, but I noticed it doesn't seem to support any kind of data validation other than "not null", "unique", and foreign keys, either on the SQL level (constraints defined when creating tables) or the Java level (validation logic in setter methods). "Keep sections" don't seem like they would be helpful in this case because you can't have them within individual methods. Am I missing something, or would I really need to add yet another layer on top of the generated Java objects if I wanted to validate input data? (I'm somewhat confused how the framework could be useful without providing any place to include validation logic.)
1.
You can write a method
boolean check ();
in KEEP-SECTION of the entity which you call manually before INSERT or UPDATE.
2.
Another possibility is to extend the sourcecode of greendao generator to support checks: In property.java you could add a method to Property.Builder
public Property.Builder check (String expr) {
property.checkConditon = expr;
}
Of course you would have to introduce the String checkCondition = ""; and use it for generating the dao in the dao-template.
Problem:
With new versions of greendao your changes would be lost (but then again new version may already contain such a feature)
3.
A third possibility is to copy the generated CREATE TABLE statement, modify it to fit your needs and call your modified statement instead of the original one or to drop the original table and call your statement.
Problem:
If your table changes you will have to repeat this.
I am porting an app from Android Java to iPhone.
In Android I used Lists/ArrayLists alot.
On iPhone I plan to use NSMutableArray.
Is there any way to define or even indicate the type of objects in an NSMutableArray.
I know one can put any type of object there, but I would like to make it more visible and transparent.
Many thanks
It's not clear exactly what you're asking.
If you just want to make it clear to the reader what sorts of object of are in the array, just name it appropriately (you can't enforce it at the language level):
NSMutableArray *arrayOfMyClasses;
If, on the other hand, you want to find out the type of an object that you're reading back from the array then you can get the underlying class using:
[obj class]
Or easily compare to other class types:
if ([obj isKindOfClass:[MyClass class]) { ... }
Tim
I assume you are looking for template pattern in Objective C. Unfortunately, it is not available in Objective C (at least directly).
You might find this question of StackOverflow.com interesting
You can only indicate a type.
for(id obj in _assets) {
NSString *className = NSStringFromClass([obj class]);
NSLog(#"%#", className);
}
Arrays are ordered collections of any sort of object. For example, the
objects contained by the array in Figure 1 can be any combination of
cat and dog objects, and if the array is mutable you can add more dog
objects. The collection does not have to be homogeneous.
Collections Programming Topics - Arrays: Ordered Collections