Apollo pagination and cache-and-network fetch policy - apollo

The apollo docs suggest a way to implement the merge function in a field policy when implementing pagination logic.
merge(existing = [], incoming) {
return [...existing, ...incoming];
}
However, when I use 'cache-and-network' fetch policy for the query, that means that first it loads data from the cache, then it goes out to the network, and will append the existing list with the incoming data, so every item will exist in the cache twice, if the incoming data is the same as what was in the cache before.
What is the correct way to solve this? Can I differentiate between an initial load and a fetchmore request in the merge function? The merge function should obviously work differently for an initial fetch that should overwrite what we have loaded from the cache, and for a pagination fetch more.

In case anyone stumbled into this issue, solution as of Apollo 3 is:
fetchPolicy: 'cache-and-network',
nextFetchPolicy: 'cache-first',

Related

Aws amplify shows much less data then in dynamodb exist

I have following problem. In my amplify studio i see 10k datapoints
But if i take a closer look into the corresponding database i see this:
I have over 200k+ data but it shows only 10k inside amplify studio. Why is it like that?
When i try this code in my frontend:
let p = await DataStore.query(Datapoint, Predicates.ALL, {
limit: 1000000000,
})
console.log(p.length)
I get 10000 back. The same number like in amplify studio.
Other questions: Whats the best way to store dozens of datapoints? I need it for chart visualizing.
A DynamoDB Query or Scan request does not return all items in one huge list. Instead, it returns just a single "page" of results, whose size defaults to 1MB. A client library like amplify could call these Query or Scan requests repeatedly to collect all pages into one huge array in memory, but that doesn't make too much sense once it grows very big. So applications usually want to iterate over all the results, not to collect them into one huge array in memory.
So most DynamoDB APIs provide a pagination interface for the query and scan operations - which provides you with one page of results, and a way to get the next page. Alternatively, some APIs can give you an iterator over results - and internally do this pagination. I'm not familiar with Amplify so I don't know which API to recommend. But certainly an API that returns all results as one big array must have its limits, and apparently you've found them.

Ember store.findAll is reloading view and store.query is not

At the moment, when an article is added to the store, my view is not updated when I use store.query(), filtering server side, in my route but it's updated when I use store.findAll() with filtering client side.
With findAll, filtering client side
//route.js
model() {
return this.get('store').findAll('article');
}
//controller.js
articleSorted: computed.filterBy('model', 'isPublished', true),
and with query filtering server side
//route.js
model() {
return this.get('store').query('article', { q: 'isPublished' }),
}
The fact is that findAll is reloading and query is not.
I've found this but did not understand
https://github.com/emberjs/ember.js/issues/15256
thanks for the question. I'll try to answer it the best I can but it would seem like some more documentation should be added to the Ember Guides to explain this situation 🤔
Essentially this.store.findAll() and this.store.query() do two very different things. findAll() is designed to be a representation of all of the entities (articles in your case) so it makes sense that the result will automatically update as the store finds more articles it should care about. It does this because it doesn't return an array of articles, it returns a DS.RecordArray that will automatically update.
query() on the other hand is designed to ask the backend every time what it expects the result to be, and you are usually passing a number of parameters to the query() call that the backend is using to find or filter results. It would be impossible for the frontend to know exactly how the backend interprets these query parameters so it is not possible for it to "auto-update" when a new article is added that would satisfy the same query.
Does that make sense? Would you like me to go into any more detail?
When using store.query to fetch data from the server, the view can still be auto-updated with new client-created store data before it's saved to the server, by using a "live" record array for it.
While data from store.query isn't live, data from store.peekAll is, so you can query first but then leverage store.peekAll for display. You can query before setting your model to the peeked data, or keep your query as the model but use some other property of peeked data for display. The important part is to ensure the query is resolved before peeking at the store.
Example based on the current code in your question:
// route.js
beforeModel() {
// using return ensures this hook waits for the promise to resolve before moving on
return this.store.query('article', { q: 'isPublished' });
}
model() {
// Queried server data should now be available to peek at locally,
// so we can set the model to a live version of it. Don't append filterBy here,
// otherwise it will no longer be live.
return this.store.peekAll('article');
}
// controller.js
// seemingly redundant filter since using query, but needed if there are other records
// in the store that shouldn't be displayed, and is recomputed when
// a record is added or removed from the store-based model
articleSorted: filterBy('model', 'isPublished', true) // filterBy imported from '#ember/object/computed'
// template.hbs
{{#each articleSorted as |article|}}
{{!-- this displayed list should update as new records are added to the store --}}
{{article}}
{{/each}}
Note that after a new record is saved to the server, the query can be updated via its update method or via a route refresh. This will re-run the query and get the updated results from the server. If the query is the model, that would look like model.update(). If it was saved to someOtherProperty, then someOtherProperty.update(). In either case, route.refresh() could be used instead to re-run all route hooks.
Some specific comments/examples that I think are helpful:
https://github.com/emberjs/ember.js/issues/15256#issuecomment-302894768
https://github.com/emberjs/ember.js/issues/15256#issuecomment-302906077
https://github.com/pouchdb-community/ember-pouch/issues/232#issuecomment-428927114

Refresh model with new params

I have a model that calls a query on my store. When something happens that causes the desired model to be a different query, I need to reload the data on the active route by firing the same query just with different param values.
To illustrate, when I come to my items route it fires a query that looks like this
return this.store.query('item',{auction_id: this.get('localStorage.user.user_active_auction'),user_id:this.get('localStorage.user.user_id')}).then((result)=>{
if(result){
return result;
} else {
this.get('notification').notify({message: "An error occurred trying to get items.",success:false});
this.transitionTo('index');
}
});
The localStorage pieces are from a service. When the .user property of the service changes, I need to reload this query with the new .user_active_auction and .user_id values so new data gets placed in the template. (and thusly replaced what was there)
Can't seem to find how to do this as the .refresh() method reloads the cached url it used before rather than creating a new one. Not sure if maybe there is an adapter/serializer method I can implement that will allow me to tell it not to use the cached one?

Correct way to query the server for all records, but filter the cached ones in Ember.js

I need to use the filter method in order to prevent unsaved new records (!isNew) from being listed/shown before being saved.
So, The official Ember.js guide, says, the filter method signature is filter (type, query, filter) and that the query is an optional argument.
The thing is, When I don't specify the query, I don't get any results, and nothing is shown.
Further digging, And I found out (correct me if I am wrong), That the filter method filters the cached records, So, This means I have to query the back-end the first time I visit the route?
My question is, Is this the correct way to do it? I feel that there is something wrong with just leaving the query argument blank, Or HAVING to put a blank argument in the first place!
Here is my Route (which works perfectly and as expected by the way) :
SalesRepsRoute = Ember.Route.extend
model: ->
# the query left blank in order to get all salesReps from the server.
#store.filter('sales-rep', {} ,(sr) ->
!sr.get('isNew')
)
Thanks in advance, And please let me know if I should post any more code/info .
filter is meant to be used for querying the store, but in the event that you'd like to trigger a call to the server you can specify a hash as the second parameter.
If you feel hacky (which you shouldn't) about it, you can just call find before it and exclude the hash in the filter call. find will asynchronously call your backend and the filter will stay up to date with records as they are added to the store.
#store.find('sales-rep')
#store.filter('sales-rep', (sr) ->
!sr.get('isNew')
)

REST and Filtering records

I currently have a .NET method that looks like this - GetUsers(Filter filter) and this is invoked from the client by sending a SOAP request. As you can probably guess, it takes a bunch of filters and returns a list of users that match those filters. The filters aren't too complicated, they're basically just a set of from date, to date, age, sex etc. and the set of filters I have at any point is static.
I was wondering what the RESTful way of doing this was. I'm guessing I'll have a Users resource. Will it be something like GET /Users?fromDate=11-1-2011&toDate=11-2-2011&age=&sex=M ? Is there a way to pass it a Filter without having to convert it into individual attributes?
I'm consuming this service from a mobile client, so I think the overhead of an extra request that actually creates a filter: POST filters is bad UX. Even if we do this, what does POST filters even mean? Is that supposed to create a filter record in the database? How would the server keep track of the filter that was just created if my sequence of requests is as follows?
POST /filters -- returns a filter
{
"from-date" : "11-1-2011",
"to-date" : "11-2-2011",
"age" : "",
"sex" : "M"
}
GET /Users?filter=filter_id
Apologies if the request came off as being a little confused. I am.
Thanks,
Teja
We are doing it just like you had it
GET /Users?fromDate=11-1-2011&toDate=11-2-2011&age=&sex=M
We have 9 querystring values.
I don't see any problem with that
The way I handle it is I do a POST with the body containing the parameters and then I return a redirect to a GET. What the GET URL looks like is completely up to the server. If you want to convert the filter into separate query params you can, or if you want to persist a filter and then add a query param that points to the saved filter that's ok too. The advantage is that you can change your mind at any time and the client doesn't care.
Also, because you are doing a GET you can take advantage of caching which should more than make up for doing the extra retquest.