Creating secondary indexes in RethinkDB (javascript) not working as in doc - rethinkdb

I am creating an index based on 2 fields in RethinkDB, in javascript (actually with rethinkdbdash driver). The code is like this :
r.table('someTable').indexList().contains("indexName").do(containsIndex => {
return r.branch(
containsIndex,
{created: 0},
r.table('someTable').indexCreate("indexName", [r.row("field1"), r.row("field2")])
);
}).run();
So it conditionnally creates the index if it doesn't exist already. The branching does work for single-field indexes. But it returns a ReqlCompileError: Cannot use r.row in nested queries. Use functions instead in this case.
The docs (https://www.rethinkdb.com/api/javascript/index_create/) clearly give this example :
r.table('comments').indexCreate('postAndDate', [r.row("postId"), r.row("date")]).run(conn, callback)
So what am I missing? Is using the rethinkdbdash driver changing anything? If I do use a function (as suggested by the error message) I can concatenate my 2 fields, but then how do I query with that index?
Thanks.

You can use r.row in un-nested queries like the example in the docs, but for nested queries you need to use an actual function. When you put the indexCreate inside a do it became part of a nested query.
If instead of r.table('someTable').indexCreate("indexName", [r.row("field1"), r.row("field2")]) you write r.table('someTable').indexCreate('indexName', function(row) { return [row('field1'), row('field2')]; }) in your query it should work.

I don't know how to do this type of branching correctly when creating compound indexes, but RethinkDB will just warn you if you try to create an index that already exists, so there is no worry if you just catch it and continue:
function createPostAndDateIndex() {
return r.table('comments').indexCreate('postAndDate',
[r.row("postId"), r.row("date")]).run();
}
function createDateIndex() {
return r.table('comments').indexCreate('d', 'date').run()
}
function initDb() {
return createPostAndDateIndex().error(console.warn)
.then(createDateIndex).error(console.warn);
}

Related

Laravel sortBy not having any affect

I have a custom attribute on my User model that's calculates the length of some other tables and returns an integer value:
public function GetCurrentQueueLengthAttribute()
{
// return int
}
I then have an API endpoint that returns a "Team" with all its users (simple Spark pivot)
public function show($teamId)
{
$query = Team::query();
$query->with('users')->where('id', $teamId);
$team = $query->first();
return $team->users->sortBy('currentQueueLength');
return $team;
}
The issue is that the returned data doesn't change order. There are no errors, just the same order of the users every time.
Is there something I'm missing?
The sortBy function is not to be mistaken by the orderBy function, the first one sorts a collection, the second one alters the sql of the query builder.
To be able to use the sortBy function one first needs to retrieve the collection. These functions can still be chained by using:
return $team->users()->sortBy('currentQueueLength');
optionally one could also use orderByRaw if you are willing to write a custom sql query for the sorting.

Apollo GraphQl react. How to clear query cache for all variable combinations?

I am using apollo graphql in my react application.
Say I have the following query:
query ListQuery($filter: String!) {
items(filter: $filter) {
id
name
}
}
This query lets me query a list of items using a filter. Say I used filter string A, and then used filter string B. The cache would now contain two entries: ListQuery(A) and ListQuery(B).
Now let's say I use a mutation to add a new item. How can I remove all the cached queries from the cache? So in this case, I want to remove both ListQuery(A) and ListQuery(B) from the cache.
How can I accomplish this?
In your case, you can use the apollo's method
client.resetStore();
It will clear the previous cache and then load the active queries.
Try this:
query ListQuery($filter: String!) {
items(filter: $filter) {
id
name
}
},
fetchPolicy: 'no-cache'
More here:
https://www.apollographql.com/docs/react/caching/cache-interaction/#bypassing-the-cache
Try evicting the particular query object from the cache...
cache.evict({ id: "ROOT_QUERY", fieldName: "listQuery" });
cache.gc();
I got this working after reading this: https://danreynolds.ca/tech/2020/05/04/Apollo-3-Client-Cache/
Seems like what you need is refetchQueries option/prop to be set to array of query names strings. The documentation states that:
If options.refetchQueries is an array of strings then Apollo Client
will look for any queries with the same names as the provided strings
and will refetch those queries with their current variables.
So as an example for using graphql HOC you could try to do:
function SomeComponent(props) {
const performMutation = () => {
props.doStuff({ refetchQueries: ["ListQuery"] })
.then(/* ... */)
.catch(/* ... */)
}
return (/* ... */)
}
export default graphql(DO_STUFF, { name: "doStuff" })(SomeComponent)
Or with the Mutation component:
function SomeComponent(props) {
return (
<Mutation
mutation={DO_STUFF}
refetchQueries={mutationResult => ["ListQuery"]}>
{(doStuff, { loading, error }) => {
/* ... */
}}
</Mutation>
);
}
If this is somehow doesn't do what you need, there is also a workaround using update option.
I was able to solve this problem by first changing a value in my schema from required to not required, which resulted in values being queried again! After that, I reverted the change and have had no problems!
Read about fetch policies
options.fetchPolicy
--Edit:--
But it is only as a trick if you want reset only one query. To reset all store see answer by #Nitin : Set fetchPolicy to network-only. Refresh query. Return it to option of your choice.

Using NODE_DELETE without refetching data

Using a NODE_DELETE requires the parent, and to actually return the parent of the connection:
Relay Error when deleting: RelayMutationQuery: Invalid field name on fat query
Unfortunately, using this refetches ALL my nested items, which is simply unacceptable for my use case.
fragment on deleteItemNested #relay(pattern: true) {
id
ok
item {
nested {
edges {
node { id }
}
}
}
clientMutationId
}
Is there a way to delete an item from a connection/list without refetching all data? Trying not to fetch for the edges in nested results in nested being just an empty object.
All the nested items are refetched because #relay(pattern: true) was used in the query. This makes the query to match against the tracked query, which already includes the nested fields. See an excellent answer by steveluscher to the question Purpose of #relay(pattern:true).
The code example of NODE_DELETE in mutation documentation is worth taking a look.

Can graphql return aggregate counts?

Graphql is great and I've started using it in my app. I have a page that displays summary information and I need graphql to return aggregate counts? Can this be done?
You would define a new GraphQL type that is an object that contains a list and a number. The number would be defined by a resolver function.
On your GraphQL server you can define the resolver function and as part of that, you would have to write the code that performs whatever calculations and queries are necessary to get the aggregate counts.
This is similar to how you would write an object serializer for a REST API or a custom REST API endpoint that runs whatever database queries are needed to calculate the aggregate counts.
GraphQL's strength is that it gives the frontend more power in determining what data specifically is returned. Some of what you write in GraphQL will be the same as what you would write for a REST API.
There's no automatic aggregate function in GraphQL itself.
You can add a field called summary, and in the resolve function calculate the totals.
You should define a Type of aggregated data in Graphql and a function you want to implement it. For example, if you want to write the following query:
SELECT age, sum(score) from student group by age;
You should define the data type that you want to return:
type StudentScoreByAge{
age: Int
sumOfScore: Float
}
and a Graphql function:
getStudentScoreByAge : [StudentScoreByAge]
async function(){
const res = await client.query("SELECT age, sum(score) as sumOfScore
from Student group by age");
return res.rows;
}
... need graphql to return aggregate counts? Can this be done?
Yes, it can be done.
Does GraphQL does it automatically for you? No, because it does not know / care about where you get your data source.
How? GraphQL does not dictate how you get / mutate the data that the user has queried. It's up to your implementation to get the requested aggregated data. You could get aggregated data directly from your MongoDB and serve it back, or you get all the data you need from your data source and do the aggregation yourself.
If you are using Hasura, in the explorer, you can definitely see an "agregate" table name, thus, your query would look something similar to the following:
query queryTable {
table_name {
field1
field2
}
table_name_aggregate {
aggregate { count }
}
}
In your results, you will see the total row count for the query
"table_name_aggregate": {
"aggregate": {
"count": 9973
}
This depends on whether you build the aggregator into your schema and are able to resolve the field.
Can you share what kind of GraphQL Server you're running? As different languages have different implementations, as well as different services (like Hasura, 8base, and Prisma).
Also, when you say "counts", I'm imagining a count of objects in a relation. Such as:
query {
user(id: "1") {
name
summaries {
count
}
}
}
// returns
{
"data": {
"user": {
"name": "Steve",
"summaries": {
"count": 10
}
}
}
}
8base provides the count aggregate by default on relational queries.

Exception with Doctrine2 and HYDRATE_SIMPLEOBJECT

As a good practice i'm tring to hydrate an object as small as possible since data is going to be read only (just show the entity in my Twig template). So i've tried HYDRATE_SIMPLEOBJECT hydratation mode but i'm getting this exception:
Cannot use SimpleObjectHydrator with a ResultSetMapping that contains
more than one object result.
How should i interpret this message? By the way, here is the code that throws the exception:
protected function getFindAllQueryBuilder()
{
return $this->createQueryBuilder('p')
->select(array('p', 'parent', 'features', 'users'))
->leftJoin('p.parent', 'parent')
->leftJoin('p.features', 'features')
->leftJoin('p.users', 'users');
}
public function findOneBySlugAsObject($slug)
{
$qb = $this->getFindAllQueryBuilder();
return $qb
->where($qb->expr()->eq('p.slug', ':slug'))
->setParameter('slug', $slug)
->getQuery()->getOneOrNullResult(Query::HYDRATE_SIMPLEOBJECT);
}
SimpleObjectHydrator is for result sets where you don't use any fetch joins in your query, in other words you can't use it if you use more than one alias in your select. SimpleObjectHydrator is faster because doesn't handle these fetch joins.

Resources