I'm experiencing an issue with apollo client caching, and I'm not sure I understand why. I'm building an angular 6 app, using apollo-boost, all is working well. I have a scenario now where my a graphql query takes an id (of a user) and a filter string (used to filter records on the backend). The angular component code looks like:
ngOnInit() {
this.filter$.subscribe(filterValue => {
this.route.params.subscribe(this.getAppointments.bind(this, filterValue));
});
}
The getAppointments function looks like:
getAppointments(filter: string, params: {id: string}) {
this.artistAppointmentBookGQL.watch({artistId: user.artist._id, filter}).valueChanges
.pipe(
map(results => {
// THIS ALWAYS RUNS WHEN THE FILTER CHANGES
// HOWEVER THE RESULTS ARE ALWAYS THE LAST QUERY RUN
// IF THE FILTER HAS BEEN RUN BEFORE
console.log(user.artist._id, filter, results.data.artist.appointmentBook);
return results.data.artist.appointmentBook;
}));
}
The graphql query:
query artistAppointmentBook($artistId: ID!, $filter: String) {
artist(id: $artistId, appointmentType: $filter) {
_id
appointmentBook {
_id
created_at
firstName
lastName
date
price
stripe {
charge {
id
amount
}
}
}
}
The main issue:
I have 4 different possible filter values (all, unconfirmed, confirmed, paid). When I run these queries with each filter value, it works as expected, and I get back the proper result sets from the apollo server. However, as soon as I run the same query twice, I only get back the result of whatever the last query was, and no network call is made, presumably because it's using a cached version.
Shouldn't the cache be based on the variable inputs? It seems to run fine the first time I run with different variables, but as soon as one gets duplicated I only get back whatever the last call yielded. Thanks for any help!
This gif demonstrates the issue:
Figured out my issue. As it should, apollo is caching the artist record because it has an _id and a type. The filter was being passed into artist query, when it should have been passed in at the appointmentBook level. I updated my schema to accept the filter param and then passed it in there instead of into the artist query.
Originally I had:
query artistAppointmentBook($artistId: ID!, $filter: String) {
artist(id: $artistId, appointmentType: $filter) {
_id
appointmentBook {
_id
created_at
firstName
lastName
date
price
stripe {
charge {
id
amount
}
}
}
}
Which needed to be changed to:
query artistAppointmentBook($artistId: ID!, $filter: String) {
artist(id: $artistId) {
_id
appointmentBook(filter: $filter) {
_id
created_at
firstName
lastName
date
price
stripe {
charge {
id
amount
}
}
}
}
After this update, the queries are cached properly.
Related
How can I get only the data from menus which _id is 1?
I've tried:
db.collection("restaurants")
.find({ name : String(name), "menus._id": Number(id)} )
.toArray(function (err, result) {
But I still get the full result from restaurant and not only the menu
you have to use projection to retrieve only the menus field.
db.collection("restaurants").find( { name: String(name), "menus._id": Number(id) }, { _id: 0, menus: 1 } ).toArray( function(err,result) { } );
with or without projection the _id field is returned by default, you have to turn it off by setting the _id field to 0. 0 means don't show the value of this field, while 1 does the opposite
I have some Couchbase data in the following format
{
"id": "12343",
"transaction": {
"2018-01-11": 10,
"2017-12-01" : 20
},
"_type": "TransactionData"
}
I would like to get the ids whose transaction list contains key older than a given date ( for example, this object would not be retrieved for a value of "2017-11-01", but it does for "2017-12-12".
I made a view, but I would like to parameterise the date String:
function (doc, meta) {
if (doc._type == 'TransactionData') {
for (var key in doc.transaction) {
//I want to send the String value from java
if (key < "2018-02-21") {
emit(doc.id, null);
break;
}
}
}
}
I tried writing some N1QL query, but my server doesn't allow that, I can't change this configuration.
I don't think I can use the startKey, because I return a map of (id, null) pairs.
How can I filter the ids that have transactions older than a configurable date?
Thanks.
You can do like this:
function (doc, meta) {
if (doc._type == 'TransactionData') {
for (var key in doc.transaction) {
emit(doc.id, null);
}
}
}
user _count for Reduce function, then you can query using
query.range("2018-02-21", {}).reduce(true)
then you can take the value to see how many rows there are
Views are static indexes. Documents are processed once after each change, and any emitted results put into the index. You can't parameterize your function because it isn't rerun for every query. So you can't solve the problem the way you're approaching it. (You can do that with N1QL.)
Typically you solve this by adding a key range filter to your query. Look at the documentation for querying views. There are examples on how to select by date. You'll have to decide how you want to structure the index (view) you create.
Let's assume the following Grails domains:
Owner {
String name
static hasMany [cars: Cars]
}
Car {
Date inspectionDate
}
I want to be able to search for Owners through Criteria, with the following rule: Most recent Car by inspectionDate in Owner's cars list being lower than *given date*.
As an example, I want to apply the following code to a select query in GORM:
queryResultsList = allOwnersList.filter { owner ->
owner.cars.min{ car -> car.inspectionDate }.inspectionDate < myDate
}
I need to achieve it using Criteria because I am already filtering Owners on other fields.
The whole given code is used as an example, some parts of the original code has been ommited, and source code is not about cars and owners.
As on first thought I assumed I needed a subquery in SQL to retrieve my data as I expected, I tried the following:
Owner.createCriteria().list {
// [...] some filters on other fields
cars {
lt('inspectionDate', params.inspectionDate)
'in'('inspectionDate', new grails.gorm.DetachedCriteria(Owner).list {
projections {
cars {
min('inspectionDate')
}
}
})
}
}
I also tried to add groupProperty in different places in the projection, ending with MissingPropertyException.
I am using Grails 2.2.4
After a few days testing solutions, I've come to the following one:
Owner.createCriteria().list {
// [...] some filters on other fields
cars {
lt('inspectionDate', params.inspectionDate)
'in'('inspectionDate', Owner.createCriteria().list {
projections {
cars {
min('inspectionDate')
}
groupProperty 'id'
}
// this allows to only get first property, which is inspectionDate
}.collect { it[0] })
}
}
However, I still feel like doing things wrong. In particular, the collect part looks like a code smell.
I am posting this because it does what I need, but any help with finding a good solution will be appreciated.
Let's say I have the following documents
Document 1
{
companyId: "1",
salesDate: "1425254400000" //this is UTC time as a long
}
Document 2
{
companyId: "1",
salesDate: "1425340800000" //this is UTC time as a long
}
Document 3
{
companyId: "2",
salesDate: "1425254400000" //this is UTC time as a long
}
I currently have my view set up as
function(doc, meta) { emit([doc.salesDate, doc.companyId], doc); }
Which is pulling back all 3 documents when using
?startkey=[1425254400000,"1"]&endkey=[1425340800000,"1"]
I'm not sure how to make it only pull back the sales for that date range by company id.
The sql version would be SELECT * FROM sales WHERE companyId = :companyId AND salesDate BETWEEN :rangeStart AND :rangeEnd
EDIT: I'm using the rest API.
When designing views for range queries with multiple query fields, the fixed query field(companyId) should be a prefix of the compound index and the range query field should be at the end. With the current view, Couchbase will emit every document where salesDate is within the range without considering companyId.
Reversing the order of keys will work:
function(doc, meta) {
emit([doc.companyId, doc.salesDate], doc);
}
Query:
?startkey=["1", 1425254400000]&endkey=["1", 1425340800000]
N.B. if salesDate is a string and not a numeric value, Couchbase will use lexicographic ordering to perform the range query.
Given a JSON document on couchbase, for example, a milestone collections, which is similar to this:
{
"milestoneDate" : /Date(1335191824495+0100)/,
"companyId" : 43,
"ownerUserId": 475,
"participants" : [
{
"userId": 2,
"docId" : "132546"
},
{
"userId": 67,
"docId" : "153"
}
]
}
If I were to select all the milestones of the company 43 and want to order them by latest first.. my view on couchbase would be something similar to this:
function (doc, meta) {
if(doc.companyId && doc.milestoneDate)
{
//key made up of date particles + company id
var eventKey = dateToArray(new Date(parseInt(doc.milestoneDate.substr(6))));
eventKey.push(doc.companyId);
emit(eventKey, null);
}
}
I do get both dates and the company Id on rest urls.. however, being quite new to couchbase, I am unable to work out how to restrict the view to return only milestones of company 43
The return key is similar to this:
"key":[2013,6,19,16,11,25,14]
where the last element (14) is the company id.. which is quite obviously wrong.
The query parameters that I have tried are:
&descending=true&startkey=[{},43]
&descending=true&startkey=[{},43]&endKey=[{},43]
tried adding companyId to value but couldn't restrict return results by value.
And according to couchbase documentation I need the date parts in the beginning to sort them. How do I restrict them by company id now, please?
thanks.
Put the company id at the start of the array, and because you'll be limiting by company id, couchbase sorts by company id and then by date array so you will be only ever getting the one company's milestone documents
I'd modify the view to emit
emit([doc.copmanyId, eventKey], null);
and then you can query the view with
&descending=true&startkey=[43,{}]
This was what worked for me previously..
I went back and tried it with end key and this seems to work - restricts and orders as required:
&descending=true&startkey=[43,{}]&endkey=[42,{}]
or
&descending=true&startkey=[43,{}]&endkey=[43,{}]&inclusive_end=true
either specify the next incremented/decremented value (based on descending flag) with end key, or use the same endkey as startkey and set inclusiveEnd to true
Both of these options should work fine. (I only tested the one with endkey=42 but they should both work)