How Changefeeds feature works internally in RethinkDB? - rethinkdb

I am a new user of RethinkDB. Could anyone tell me if every time that I receive changes on a table, through the Changefeeds feature, the query needs to reevaluated every time?
For example, a query that return all the users from a specified country. Two tables: users and countries. I allways want to know all the users from that country. I ask because if my table has a lot of records (millions) could be a bit expensive...
Thanks in advance,
Humberto

The answer is... it depends on what you want to do and how your query is structured.
#1 Simple Queries
If you have a simple filter query that is listening for changes on all 'users' from a particular country, your query won't get reevaluated every single time. RethinkDB handles that automatically and it's not an expensive operation. In terms of performance, this is the preferred strategy.
Example:
r.table('users')
.filter({ country: "China })
.changes()
You can also listen to users from multiple countries in the same query:
var countries = [ 'China', 'India', 'Colombia', 'Mexico', 'United States' ];
r.table('users')
.filter(function (row) {
return r.expr(countries).contains(row('country'));
})
.changes()
#2 Simple Queries with single join
If, when you get an update on the user, but you also want to join the country data from the 'countries' table, you can do that after the changes with a merge and a get.
r.table('users')
.filter({ country_name: "China })
.changes()
.merge(function (row) {
return {
'country': r.table('countries').get(row('country_name'))
}
})
This will join the data every time there's an update, but it's more expensive than the previous one because, every time there is an update, it will have to do a point read in the 'countries' table.
#3 Alternative to #2
If you don't want to do this, but want to still join the countries to the 'users' table, you could do the joins in your application layer.

Related

Apollo query does not return cached data available using readFragment

I have 2 queries: getGroups(): [Group] and getGroup($id: ID!): Group. One component first loads all groups using getGroups() and then later on a different component needs to access a specific Group data by ID.
I'd expect that Apollo's normalization would already have Group data in cache and would use it when getGroup($id: ID!) query is executed, but that's not the case.
When I set cache-only fetchPolicy nothing is returned. I can access the data using readFragment, but that's not as flexible as just using a query.
Is there an easy way to make Apollo return the cached data from a different query as I would expect?
It's pretty common to have a query field that returns a list of nodes and another that takes an id argument and returns a single node. However, deciding what specific node or nodes are returned by a field is ultimately part of your server's domain logic.
As a silly example, imagine if you had a field like getFavoriteGroup(id: ID!) -- you may have the group with that id in your cache but that doesn't necessarily mean it should be returned by the field (it may not be favorited). There's any number of factors (other arguments execution context, etc.) that might affect what nodes(s) are returned by a field. As a client, it's not Apollo's place to make assumptions about your domain logic.
However, you can effectively duplicate that logic by implementing query redirects.
const cache = new InMemoryCache({
cacheRedirects: {
Query: {
group: (_, args) => toIdValue(cache.config.dataIdFromObject({ __typename: 'Group', id: args.id })),
},
},
});

Using Laravel Eloquent to count how many times something exists in an efficient manner

I have a table called rentals, within each row are columns state,city,zipcode which all house ids to another table with that info. There are about 3400 rentals. I am pulling each column to display the states,city and zipcode distinctly. I need to show how many rentals are in each one. I am doing this now via ajax, the person starts typing in what they want to see and it auto completes it with the count, but its slow because of the way im doing it.
$rentals_count = Rentals::where('published',1)->get();
foreach($states as $state) {
echo $state.”-“.$rentals_count->where(‘state’,$state->id)->count();
}
Above is roughly what im doing with pieces removed because they are not related to this question. Is there a better way to do this? It lags a bit so the auto complete seems broken to a new user.
Have you considered Eager loading your eloquent query? Eager loading is used to reduce query operations. When querying, you may specify which relationships should be eager loaded using the with method:
$rental_counts = Rentals::where('published',1)->with('your_relation')->get();
You can read more about that in Laravel Documentation
$rentals = Rentals::wherePublished(true)->withCount('state')->get();
When you loop through $rentals, the result will be in $rental->state_count
Setup a relation 'state' on rentals then call it like this
$rentals_count = Rentals::where('published',1)->with('state')->get()->groupBy('state');
$rentals_count->map(function($v, $k){
echo $v[0]->state->name .' - '. $v->count();
});
Meanwhile in Rentals Model
public function state(){
return $this->hasOne(State::class, 'state'); //state being your foreign key on rentals table. The primary key has to be id on your states table
}

Can you do a join using an embedded array in a document with rethinkdb?

Say I have a user table with a property called favoriteUsers which is an embedded array. i.e.
users
{
name:'bob'
favoriteUsers:['jim', 'tim'] //can you have an index on an embedded array?
}
user_presence
{
name:'jim', //index on name
online_since:14440000
}
Can I do an inner or eqJoin against say a 2nd table using the embedded property, or would I have to pull favoriteUsers out of the users table and into a join table like in traditional sql?
r.table('users')
.getAll('bob', {index:'name'})
// inner join user_presence on user_presence.name in users.highlights
.eqJoin("name", r.table('user_presence'), {index:'name'})
Eventually, I'd like to call changes() on the query so that I can get a realtime update of the users favorite users presence changes
eqJoin can works on embedded document, but it works by compare a value which we transform/pick from the embedded document to mark secondary index on right table.
In any other complicated join, I would rather use concatMap together with getAll.
Let's say we can fetch user and user_presence of their favoriteUsers
r.table('users')
.getAll('bob', {index: 'name'})
.concatMap(function(user) {
return r.table('user_presence').filter(function(presence) {
return user("favoriteUsers").contains(presence("name"))
})
)
So ideally, now you get the data and do the join yourself by querying extra data that you need. My query may have some syntax/error but I hope it gives you the idea

Rethinkdb - filtering by value in another table

In our RethinkDB database, we have a table for orders, and a separate table that stores all the order items. Each entry in the OrderItems table has the orderId of the corresponding order.
I want to write a query that gets all SHIPPED order items (just the items from the OrderItems table ... I don't want the whole order). But whether the order is "shipped" is stored in the Order table.
So, is it possible to write a query that filters the OrderItems table based on the "shipped" value for the corresponding order in the Orders table?
If you're wondering, we're using the JS version of Rethinkdb.
UPDATE:
OK, I figured it out on my own! Here is my solution. I'm not positive that it is the best way (and certainly isn't super efficient), so if anyone else has ideas I'd still love to hear them.
I did it by running a .merge() to create a new field based on the Order table, then did a filter based on that value.
A semi-generalized query with filter from another table for my problem looks like this:
r.table('orderItems')
.merge(function(orderItem){
return {
orderShipped: r.table('orders').get(orderItem('orderId')).pluck('shipped') // I am plucking just the "shipped" value, since I don't want the entire order
}
})
.filter(function(orderItem){
return orderItem('orderShipped')('shipped').gt(0) // Filtering based on that new "shipped" value
})
it will be much easier.
r.table('orderItems').filter(function(orderItem){
return r.table('orders').get(orderItem('orderId'))('shipped').default(0).gt(0)
})
And it should be better to avoid result NULL, add '.default(0)'
It's probably better to create proper index before any finding. Without index, you cannot find document in a table with more than 100,000 element.
Also, filter is limit for only primary index.
A propery way is to using getAll and map
First, create index:
r.table("orderItems").indexCreate("orderId")
r.table("orders").indexCreate("shipStatus", r.row("shipped").default(0).gt(0))
With that index, we can find all of shipper order
r.table("orders").getAll(true, {index: "shipStatus"})
Now, we will use concatMap to transform the order into its equivalent orderItem
r.table("orders")
.getAll(true, {index: "shipStatus"})
.concatMap(function(order) {
return r.table("orderItems").getAll(order("id"), {index: "orderId"}).coerceTo("array")
})

Extracting a tree structure

How do I retrieve a hierarchical tree structure with a linq query and EF?
My database has tables for Tab which is 1-to-Many to TabGroupBox which is 1-to-Many to FieldDetail
I would like to retrieve only portions of the structure based on some criteria.
Say, I have the following in the database:
Tab (Name)
"Tab1"
"Tab2"
"Tab3"
TabGroupBox (Tab.Name, Name)
"Tab1", "Group1"
"Tab2", "Group2"
"Tab1", "Group3"
FieldDetail: (TabGroupBox.Name, Name, FieldStatus)
"Group1", "Field1", "Good1"
"Group2", "Field2", "Good2"
"Group3", "Field3", "Bad"
"Group1", "Field4", "Good3"
"Group1", "Field5", "Bad"
If the criteria is for the FieldStatus to contain "Good", then the result I'm looking for is:
Tab1
-Group1
--Field1
--Field4
Tab2
-Group2
--Field2
However, at the moment can only seem to get either
Field1
Field2
Field4
or a duplication of tabs corresponding to the fields
Tab1
Tab1
Tab2
A sample of my non-working query:
Tabs.SelectMany(x => x.TabGroupBoxes.SelectMany(y => y.FieldDetails).Where(z =>
(z.FieldDetailType.Contains("Good")
))
.Select(x => x.TabGroupBox.Tab)
I've also tried making use of .Include() but no success
Additionally, if possible, I'd like to project the db models onto my view models within the query, but this is not as important as above.
The best you can do is the following.
context.FieldDetails
.Include(fieldDetail => fieldDetail.GroupBox.Tab) // Use eager loading.
.Where(fieldDetail => fieldDetail.FieldDetailType.Contains("Good")))
.AsEnumerable() // We will do the remaining work client side.
.Select(fieldDetail => fieldDetail.GroupBox.Tab)
.Distinct() // Get rid of duplicate tabs.
.ToList()
What is not easily possible is to retrieve a collection of related entites partially, for example only Group1 but not Group3 for Tab1. If you could do this it would cause some headaches. What if you commit changes for Tab1? Should Group3 get deleted because it is not present in the collection of groups? Should it stay because you did not load it in the first place? If you think about it for some time you will realize that the ability to partially load collections of related entities would add a lot of complexity and make the behavior very non-intuitive in some cases.
If this is not enough you might be able to create views or functions in the database and map these to your model but I have not used the Entity Framework for more than two years and did not follow the development so I can not provide you more details how to approach this.

Resources