How do I query Arrays in Usergrid collections? - apigee

I'm successfully able to GET data from
GET /mycollection?ql=select data.visitor.badges where data.visitor._id = 'f33498'
Which returns
{
"action": "get",
"application": "313hhlkhj77080",
"params": {
"ql": [
"select data.visitor.badges where data.visitor._id = 'f33498'"
]
},
"path": "/mycollection",
"uri": "http://xxxx/appservices/xxxxxx/mycollection",
"list": [
[
[
"New Visitor",
"Cart Abandoner"
]
],
[
[
"New Visitor",
"Repeat Visitors",
"Cart Abandoner"
]
],
[
[
"New Visitor",
"Repeat Visitors",
"Browse Abandoner"
]
]
],
"timestamp": 1407968065207,
"duration": 35,
"organization": "visitor-baas",
"applicationName": "sandbox",
"count": 3
}
However, I cannot figure out how to modify the following query to allow me to narrow the result set to only those containing a "Cart Abandoner" value in the data.user.badges array.
Is this possible? I've tried:
GET /mycollection?ql=select data.visitor.badges where data.visitor.badges = 'Cart Abandoner'
This appears to return data.visitor.badges arrays where 'Cart Abandoner' is the last position of the array.
GET /mycollection?ql=select data.visitor.badges where data.visitor.badges contains 'Cart Abandoner'
This appears to return nothing.
What am I missing?

Unfortunately there's currently no way to query arrays. Your best option is to store it as an object instead.

Couple things: I query elements of arrays all the time, but the ql query string is a little temperamental. element = 'string' should return the entire JSON payload if the 'string' is contained anywhere in the array 'element' so the fact you're getting mixed results may be due to the complexity of your nested arrays.
That said, The ql query string allows you to restrict the resources that get returned (like your first example where id = 'xxx'). There isn't any way to return anything other than the entire JSON payload from that resource (such as truncating your array based on the query restriction).
So, if what you're trying to do is pull just the times that your customer returned, I would suggest creating a separate resource called something like "visitorbadges" and connect it to the user record. So instead of querying with the id and trying t query the array you'd have something like:
https://api.usergird.org/{yourorg}/{yourapp}/users/{userid}/vistorbadges
If you use the BaaS userid rather than your own you can go to /users/uuid or, you could also store the userid with the label 'name' ({"name" : "f33498"}) which will let you go to /users/f33498/visitorbadges
See the Apige docs for how to connect resources:
http://apigee.com/docs/app-services/content/connecting-users-other-data

Related

Complex object in a query string

How can I have this structure in a query string?
"properties": {
"list": [
{
"label": "bye",
"value": "world"
},
{
"label": "hello",
"value": "mars"
}
]
}
I've tried it with properties[][list][label]=bye&properties[][list][value]=world&properties[0][label]=hello&properties[0][value]=mars and also with properties[][list][label]=bye&properties[][list][value]=world&properties[][list][label]=hello&properties[][list][value]=mars, none of them worked. I built them in php with http_build_query.
I need to have this structure in a query string because I have to send the data along with some other stuff with POST to a PHP site.
I see two errors in your query string:
properties is an object, so there's no need to use [] to add elements.
list is an array, so you must use numeric indexes in the query string.
The correct query string is:
?properties[list][0][label]=bye
&properties[list][0][value]=world
&properties[list][1][label]=hello
&properties[list][1][value]=mars
(multi-lined for readability)

How to update a nested object inside an array in DynamoDB

Consider the following document item / syntax in a DynamoDB table:
{
"id": "0f00b15e-83ee-4340-99ea-6cb890830d96",
"name": "region-1",
"controllers": [
{
"id": "93014cf0-bb05-4fbb-9466-d56ff51b1d22",
"routes": [
{
"direction": "N",
"cars": 0,
"sensors": [
{
"id": "e82c45a3-d356-41e4-977e-f7ec947aad46",
"light": true,
},
{
"id": "78a6883e-1ced-4727-9c94-2154e0eb6139",
}
]
}
]
}
]
}
My goal is to update a single attribute in this JSON representation, in this case cars.
My approach
I know all the sensors IDs. So, the easiest way to reach that attribute is to find, in the array, the route which has a sensor with any of the ids. Having found that sensor, Dynamo should know which object in the routes array he has to update. However, I cannot run this code without my condition being rejected.
In this case, update attribute cars, where the route has a sensor with id e82c45a3-d356-41e4-977e-f7ec947aad46 or 78a6883e-1ced-4727-9c94-2154e0eb6139.
var params = {
TableName: table,
Key:{
"id": "0f00b15e-83ee-4340-99ea-6cb890830d96",
"name": "region-1"
},
UpdateExpression: "set controllers.intersections.routes.cars = :c",
ConditionExpression: ""controllers.intersections.routes.sensors.id = :s",
ExpressionAttributeValues:{
":c": 1,
":s": "e82c45a3-d356-41e4-977e-f7ec947aad46"
},
ReturnValues:"UPDATED_NEW"
};
docClient.update(params, ...);
How can I achieve this?
Unfortunately, you can't achieve this in DynamoDB without knowing the array index. You have very complex nested structure. The DynamoDB API doesn't have a feature to handle this scenario.
I think you need the array index for controllers, routes and sensors to get the update to work.
Your approach may work in other databases like MongoDB. However, it wouldn't work on DynamoDB. Generally, it is not recommended to have this complex structure in DynamoDB especially if your use case has update scenario.
TableName : 'tablename',
Key : { id: id},
ReturnValues : 'ALL_NEW',
UpdateExpression : 'set someitem['+`index`+'].somevalue = :reply_content',
ExpressionAttributeValues : { ':reply_content' : updateddata }
For updating nested array element need to fing out array index . Then you can update nested array element in dynamo db.

Conditional query in Freebase API

I am looking to extract list of tourist attractions and their city,state and country information from Freebase. The property that has location is
"/location/location/containedby". There are different types for this object, "location/location" or "/base/biblioness/bibs_location". If the object has "/base/biblioness/bibs_location" i can get the value of "city", "state" etc. however if the object only has the type "/location/location" i need to go and get its "containedby" field and redo the above logic.
My question is can i perform a conditional query in Freebase like if type == "/location/location/" get xyz. if type== "/base/biblioness/bibs_location" get abc
MQL:
[{
"type": "/travel/tourist_attraction",
"id": null,
"name": null,
"name~=": "^San Diego",
"/location/location/containedby": {
"type": "/base/biblioness/bibs_location",
"name": null,
"id": null
},
"/location/location/geolocation": [{
"id": null,
"latitude": null,
"longitude": null
}]
}]
MQL doesn't support conditional logic, but you can query all the information that you're potentially interested in, making the subqueries optional so they don't filter the results, and then look at what you get back. It'll require conditional code in your result processor, but you won't have to make multiple queries. For example, you could query multiple levels of /location/location/contained by as well as /base/biblioness/bibs_location/state and whatever else you want.
Before you go spending too much time on this though, you might want to check how well populated /base/biblioness/bibs_location is. It looks to me like it's got less than 2K entities.

Freebase MQL - Don't show parent object if a value in array element is present?

Trying to get some movies and their genres but leave out any records that contain the genre "Thriller" in the array of genres.
How do I not only ignore the genre key itself for "Thriller", but squelch that entire movie result? With my current query, Thriller is removed from the array of genres, but the parent object (film) is still displayed.
Here's my current workup in the query editor:
http://tinyurl.com/d2g54lj
[{
'type':'/film/film',
'limit':5,
'name':null,
'/film/film/genre': [],
'/film/film/genre!=': "Thriller",
}]​
The answer provided is correct, but changes some other stuff in the query too. Here's the direct equivalent to the original query:
[{
"type": "/film/film",
"limit": 5,
"name": null,
"genre": [],
"x:genre": {"name":"Thriller",
"optional":"forbidden"},
}]​
The important part is the "optional":"forbidden". The default property used is "name", but we need to specify it explicitly when we use a subclause (to allow us to specify the "optional" keyword). Using ids instead of names, as #kook did, is actually more reliable, so that's an improvement, but I wanted people to be able to see the minimum necessary to fix the broken query.
We can abbreviate the property name to "genre" from "/film/film/genre" since "type":"/film/film" is included (we also never need to use /type/object for properties like /type/object/name).
Answering my own question.
So the trick is to not use the != (but not) operator, but to actually flip it on its head and use the "|=" (one of) operator with 'forbid', like so:
[{
'type':'/film/film',
'limit':5,
'name':null,
'/film/film/genre': [{
"id": null,
"optional": true
}],
"forbid:/film/film/genre": {
"id|=": [
"/en/thriller",
"/en/slapstick"
],
"optional": "forbidden"
}
}]​
Thanks to the following post:
Freebase query - exclusion of certain values

Freebase MQL filter where value == null?

Can get all triples with value null in specific field?
All people with date_of_birth equal null?
[
"type": "/people/person",
"date_of_birth":null,
"name":null
]
You need to use "optional":"forbidden" directive:
[{
"type": "/people/person",
"date_of_birth": {
"value": null,
"optional": "forbidden"
},
"name": null,
"id": null
}]​
(I added "id":null so that the Query Editor gives clickable links)
Note that query has a default "limit":100, if you want more results then add an explicit limit clause. If that times out, then you'll need to use a MQL cursor.
If you need to deal with lots of results, the undocumented envelope parameter "page" provides more flexibility than "cursor", allowing you to move forward, back, or access a page at random, as opposed to just going forward like you can with the cursor.
The "optional": "forbidden" clause is the key to lots of useful queries. The "!everything" == "nothing" equivalency is just one of the most common ones.
Tom

Resources