RethinkDB: Cannot call `changes` on an eager stream - rethinkdb

I have a table of users who each have an array of friends.
A document in it looks something like this:
{
id: "0ab43d81-b883-424a-be56-32f9ff98f7d2",
username: "testUser1234",
friends: [
"04423c56-1890-4028-b38a-cb9aff7112de" ,
"05e4e613-2131-408c-b0ae-a952f3007405" ,
"0395ee53-8ab0-48cc-aa4e-41aad93b8737"
]
}
I want to watch for changes on a user's friends'. A query like this will get me a list of friends:
r.db("Test").table("Users").get("0ab43d81-b883-424a-be56-32f9ff98f7d2")("friends").map(function(id) {
return r.db("Test").table("Users").get(id);
})
But, when I try to throw a .changes() on the end, RethinkDB tells me that it won't work:
RqlRuntimeError: Cannot call `changes` on an eager stream in:
r.db("Test").table("Users").get("0ab43d81-b883-424a-be56-32f9ff98f7d2")("friends").map(function(var_19) { return r.db("Test").table("Users").get(var_19); }).changes()
Is there anyway to get this to work? I am afraid that my only alternative is to subscribe to the friends list (in my app) and update the subscription to the actual friends when it changes:
r.db("Test").table("Users").getAll(friendId1, friendId2 , friendId3, friendId4).changes()
Not the end of the world, but I was really excited about being able to do it entirely in the DB.
Also, can anyone explain what an "eager stream" is? I think it has something to do with lazy vs. immediate evaluation, but I had no idea how to tell what the criteria determines whether a stream is eager or not.

I can get the query working with the following formation, inspired by this post:
r.db("Test").table('Users').getAll(r.args(
r.db('Test').table('Users').get("0ab43d81-b883-424a-be56-32f9ff98f7d2")('friends')
)).changes()

You can attach the .changes before some of the transofrmations.
r.db("Test")
.table("Users")
.get("0ab43d81-b883-424a-be56-32f9ff98f7d2")
.changes()
.getField('new_val')('friends')
.map(function(id) {
return r.db("Test").table("Users").get( id );
})
Basically, every time there is a change, the map function is executed. At the moment, that is the only way to do this type of operations with .changes, but that will change in upcoming versions of RethinkDB.

Related

GraphQL: Querying for a list of object which have relationship to another object

I have set up my schema on GraphCMS and finding graphQL to be very convenient.
I have a workout object and a workoutCategory object. Those two are linked by a many to many relationship.
I'd like to write a query that allows me to get the list of workout which are part of a certain category.
I'm writing the query as follow:
workout(where:{ workoutCategories: { id: "xxxxxxx" } }) {
id
}
graphCMS gives me a syntax error on the workoutCategories which does not make sense to me yet
Field workout categories is not defined by type WorkoutWhereUniqueInput
What do I need to do to be able to achieve my goal?
Thanks in advance
Turns out I need to query on 'workouts' (see the 's' at the end) and not on 'workout'...

readFragment to return all object of a type

i'm using Apollo Client do request a very structured dataset from my server. Something like
-Show
id
title
...
-Seasons
number
-Episodes
id
number
airdate
Thanks to normalization my episodes are stored individually but i cannot query them. For exemple i would like to query all the episodes to then sort them by date to display coming next.
the only way i see is to either 'reduce' my show list to an array of episode and then do the filtering. Or to do a new query to the server.
But it will be so much faster if I could get a list of all Episodes in cache.
Unfortunately with readFragment you can only query One object by its id.
Question:
Is there a way to query the cache for all object of a defined type?
The answer is late, but could have helped someone else, currently apollo does not support it. This is the issue here from github, and also a work around.
https://github.com/apollographql/apollo-client/issues/4724#issuecomment-487373566
Here is the copied workaround by #superandrew213
const serializedState = client.cache.extract()
const typeNameItems = Object.values(serializedState)
.filter(item => item.__typename === 'TypeName')
.map(item => client.readFragment({
fragmentName: 'FragmentName',
fragment: Fragment,
id: item.id,
}))
Please take note that this method is slow, especially if you have a large normalized data.

GraphQL Authorization / Permission

So basically how do you handle permissions?
Let's say we have a list of Post(s) of some kind, with an argument first to limit the amount of posts. And only the owner and approved users can read the posts, everyone else can't. What is the best way to implement this?
query {
{
viewer {
posts(first: 10) {
id
text
}
}
}
}
What I'm currently thinking of, is to have a single source of truth to whether a user can read the post or not, and hook it up with the dataloader module.
But, how do I query for exactly 10 posts? If I query my DB for exactly 10 rows, when I then later on filter them with some business logic, then I can get for example 8 posts returned.
A solution is to not put a limit on the query, but that's not very efficient. So what is a good way to go about this?
Inspiration from here
(1) https://dev-blog.apollodata.com/auth-in-graphql-part-2-c6441bcc4302
(2) https://dev-blog.apollodata.com/graphql-at-facebook-by-dan-schafer-38d65ef075af
(1) solved it by
export const DB = {
Lists: {
all: (user_id) => {
return sql.raw("SELECT id FROM lists WHERE owner_id is NULL or owner_id = %s, user_id);
}
}
}
as the query, and then to filter out which rows can be read:
resolve: (root, _, ctx) => {
// factor out data fetching
return DB.Lists.all(ctx.user_id)
.then( lists => {
// enforce auth on each node
return lists.map(auth.List.enforce_read_perm(ctx.user_id));
});
}
So, we can clearly see that it's querying for all the rows, even if, say, the first argument was 1, which is what I'm trying to avoid.
Maybe I'm approaching the problem wrong in some way, as the business logic lives on another layer than the DB one, so there's no way but to query all the rows. Any help appreciated.
For future reference and other people searching for solutions.
Used Dataloader to solve the authentication problem.
Literally implemented what they did in https://dev-blog.apollodata.com/graphql-at-facebook-by-dan-schafer-38d65ef075af and used this boilerplate repo as guidance. Not much more to say than that.

Rethinkdb filter on an earlier query

I have a table "posts" with "timestamp".
Now I want from all user that have more than 1 post, to get all posts EXCEPT the most recent post.
With this query I can successfully check the users who have more than 1 post:
r.table("post")
.group('userId')
.count()
.ungroup()
.filter(r.row("reduction").gt(1))
I can get the last post of a specific user by doing
r.table("post")
.filter({userId: 'xxx'})
.max('timestamp')
Now I need to tie those somehow together, and then compare the timestamp from each row with the max('timestamp') to see if they are not equal. The following is what I had but it's obviously wrong
.filter(r.row('timestamp').ne(r.row('timestamp').max('timestamp')('timestamp')))
Any advice how I bring all this together?
Something like this ought to work:
r.table('post')
.group({
index: 'userId'
})
.ungroup()
.filter(function(doc) {
return doc('reduction').count().gt(1)
})
.group('group')('reduction')
.nth(0)
.orderBy(
r.desc('timestamp')
).skip(1)
With reservations for syntax errors; I built this query using python and then converted it to javascript. Especially unsure about the .nth(0) part, never used it in javascript. In python it's just [0].

Document concurrent update

I have a document like:
{
owner: 'alex',
live: 'some guid'
}
Two or more users can update live field simultaneously.
How can I make sure that only the first user wins and others updates fails?
You can get the semantics you want if you store some variable like "times_updated" in the document. Operations on a single document are atomic, so you can check that the field is the value you expect, and then throw an error if it isn't.
It might look something like:
var timesUpdated = 3
r.table('foo').get(rowId).update(function(row) {
return r.branch(row('timesUpdated').eq(timesUpdated),
{
timesUpdated: row('timesUpdated').add(1),
live: 'some special value'
},
r.error('Someone else updated the live field!')
);
}, {returnChanges: true})
So if another query comes in before you for timesUpdated = 3, your query will blow up. When do you get timesUpdated? That depends on how your app is designed, and what you're trying to do.
Another thing to note is that adding {returnChanges: true} is really useful because it allows you to get the new value of timesUpdated atomically. You can also see what exactly changed in the updated document.

Resources