GraphQL: how to build a diff query - react-apollo

I'm new to GraphQL. I have a server API that only returns changes done to a list of objects since a certain timestamp given by the client. This is for performance reasons - so that polling will return a smaller result after the initial query. So the first query result would be something like:
{
data: [fullobj1, fullobj2, fullobj3, ...],
timestamp
}
and a subsequent query is sent with the previous query's timestamp would result only with objects that changed, and only the fields that changed in them:
{
data: [partialobj1, partialobj3],
timestamp
}
Since I define this in GQL as a "query", Apollo has no way of knowing that I want the results to merge rather than replace one another. What is the "proper" way of implementing this using Apollo client? (I'm using the React variant)

Related

GraphQL - limiting the number of subqueries to prevent Batching Attack

I want to understand if there is a mechanism to limit the number of subqueries within the GraphQL query to mitigate against the GraphQL batching attack. (It's possible to send more than one mutation request per HTTP request because of the GraphQL batching feature)
Eg.
{
first: changeTheNumber(newNumber: 1) {
theNumber
}
second: changeTheNumber(newNumber: 1) {
theNumber
}
third: changeTheNumber(newNumber: 1) {
theNumber
}
}
I'm using graphql-java-kickstart.
In graphql-java there are two instrumentations that can check the "depth" or "complexity" or your query:
MaxQueryDepthInstrumentation
MaxQueryComplexityInstrumentation
The first one checks the depth of a query (how many levels are requests) the second one counts the fields. You can configure the expected max depth/complexity and if a query is deeper/complexer than your configured number it is rejected.
You can customize the behaviour of the MaxQueryComplexityInstrumentation so that some fields count as "more complex" than others (for example you could say a plain string field is less complex than a field that requires it's own database request when processed).
Here is an example that uses a custom direcive (Complexity) in a schema description to determine the complexity of a field.
If you only want to avoid that a concrete field is requested more than once, you could write you own Instrumentation or use the DataFetchingEnvironment in your resolver function to count the number of that fields in the current query (getSelectionSet() gives access to all fields contained in the current query).

DynamoDB Query Returns Incomplete Data

Why does my query in Lambda not return everything between the two Timestamps? It consistently returns the same, incomplete data.
In DynamoDB Item Explorer, I can query recent Timestamps and find the appropriate items by Device.
When I query in my Lambda, much of that same data is missing.
var params = {
Statement : `SELECT * FROM temps WHERE "Timestamp" >= ${fromParam} AND "Timestamp" <= ${toParam}`,
}
dynamodb.executeStatement(params, function(err, data) { ...
My DynamoDB table looks like this:
DDB will only read (note read, not the same as return) 1MB of data at a time.
If you are doing any filtering, then the returned data will be less than the 1MB read.
If there's more data to read, DDB will include LastEvaluatedKey in it's response. You'll need to call Query() again passing in the returned LastEvaluatedKey as ExclusiveStartKey
Thus, unless you can guarantee you'll never have more than 1MB of data to read, you'll want to call Query() in a loop till you get back all the data.
EDIT
Yes, if nextToken is returned, you'll need to pass that back in the next call..
I've never used execute statement, but it appears you're doing a full table scan. Rather than a query. You need to include a where device = in order to use query behind the scenes.
If you really need records for all devices, consider a adding a GSI with a single value as the partition key and timestamp as the sort key. Then use FROM TEMPS.mytsidx

GraphQL + Relay Connection Optimization

Using Relay + GraphQL (graphql-relay-js) connections and trying to determine the best way to optimize queries to the data source etc.
Everything is working, though inefficient when connection results are sliced. In the below query example, the resolver on item will obtain 200+ records for sale 727506341339, when in reality we only need 1 to be returned.
I should note that in order to fulfill this request we actually make two db queries:
1. Obtain all items ids associated with a sale
2. Obtain item data for each item id.
In testing and reviewing of the graphql-relay-js src, it looks like the slice happens on the final connection resolver.
Is there a method provided, short of nesting connections or mutating the sliced results of connectionFromArray, that would allow us to slice the results provided to the connection (item ids) and then in the connection resolver fetch the item details against the already sliced id result set? This would optimize the second query so we would only need to query for 1 items details, not all items...
Obviously we can implement something custom or nest connections, though it seems this is something that would be avail, thus I feel like I am missing something here...
Example Query:
query ItemBySaleQuery {
viewer {
item (sale: 727506341339) {
items (first:1){
edges {
node {
dateDisplay,
title
}
}
}
}
}
}
Unfortunately the solution is not documented in the graphql-relay-js lib...
Connections can use resolveNode functions to work directly on an edge node. Example: https://github.com/graphql/graphql-relay-js/blob/997e06993ed04bfc38ef4809a645d12c27c321b8/src/connection/tests/connection.js#L64

Sesame caching common queries

I use Sesame in a JSP web based application and I would like to know if there is any way to cache some queries that are used consistently.
I assume that what you want to "cache" is the query result for a given query with specific value. You can very easily build such a cache yourself. Just create a class for the general query that internally keeps a reference to a HashMap that maps from a value key (e.g. the placeid for your example query) to a query result:
HashMap<URI, TupleQueryResult> cache = new HashMap<>();
Then all you do is check, for a given place id, if it is present in the cache. If it is not, you execute the query, get the result back and materialize it as a MutableTupleQueryResult which you can then put in that cache:
if (!cache.contains(placeId)) {
// reuse the prepared query with the specific binding for which we want a result
preparedQuery.setBinding("placeid", placeId);
// execute the query and add the result to a result object we can reuse multiple times
TupleQueryResult result = new MutableTupleQueryResult(preparedQuery.evaluate());
// put the result in the cache.
cache.put(placeId, result);
}
return cache.get(placeId);
If you want something a bit more sophisticated (e.g. something that throws out cached items after a certain time, or sets a size limit on your cache), I would have a look at using something like a Guava Cache instead of a simple HashMap, but the basic setup would remain the same.

Passing parameters to a couchbase view

I'm looking to search for a particular JSON document in a bucket and I don't know its document ID, all I know is the value of one of the sub-keys. I've looked through the API documentation but still confused when it comes to my particular use case:
In mongo I can do a dynamic query like:
bucket.get({ "name" : "some-arbritrary-name-here" })
With couchbase I'm under the impression that you need to create an index (for example on the name property) and use startKey / endKey but this feels wrong - could you still end up with multiple documents being returned? Would be nice to be able to pass a parameter to the view that an exact match could be performed on. Also how would we handle multi-dimensional searches? i.e. name and category.
I'd like to do as much of the filtering as possible on the couchbase instance and ideally narrow it down to one record rather than having to filter when it comes back to the App Tier. Something like passing a dynamic value to the mapping function and only emitting documents that match.
I know you can use LINQ with couchbase to filter but if I've read the docs correctly this filtering is still done client-side but at least if we could narrow down the returned dataset to a sensible subset, client-side filtering wouldn't be such a big deal.
Cheers
So you are correct on one point, you need to create a view (an index indeed) to be able to query on on the content of the JSON document.
So in you case you have to create a view with this kind of code:
function (doc, meta) {
if (doc.type == "youtype") { // just a good practice to type the doc
emit(doc.name);
}
}
So this will create a index - distributed on all the nodes of your cluster - that you can now use in your application. You can point to a specific value using the "key" parameter

Resources