RethinkDB: How can I perform several aggregation operations by running only one query? - rethinkdb

For a table with the following results (not necessarily records with ids):
[{time: 4, votes: 10}, {time: 6, votes: 3} ... ]
How can I get the following result (or similar):
{average_time: 5, total_votes: 13}
by running only ONE query and not two?

You can use reduce to perform multiple aggregations. This should work
r.table('data').map(function(doc) {
return {
total_votes: doc("votes"),
total_time: doc("times"),
count: 1
}
}).reduce(function(left, right) {
return {
total_votes: left("total_votes").add(right("total_votes")),
total_time: left("total_time").add(right("total_time")),
count: left("count").add(right("count"))
}
}).map(function(result) {
return {
total_votes: result("total_votes"),
average_time: result("total_time").div(result("count"))
}
})

try this :)
r.object(
'average_time',
r.table('data')('time').avg(),
'total_votes',
r.table('data')('votes').sum()
)

Related

Graphql: How can I solve the N + N problem?

After having implemented dataloader in the respective resolvers to solve the N+1 problem, I also need to be able to solve the N+N problem.
I need a decently efficient data loading mechanism to get a relation like this:
{
persons (active: true) {
id,
given_name,
projects (active: true) {
id,
title,
}
}
}
I've created a naive implementation for this, returning
{
persons: [
{
id: 1,
given_name: 'Mike'
projects: [
{
id: 1,
title: 'API'
},
{
id: 2,
title: 'Frontend'
}
]
}
{
id: 2,
given_name: 'Eddie'
projects: [
{
id: 2,
title: 'Frontend'
},
{
id: 3,
title: 'Testing'
}
]
}
]
}
In SQL the underlying structure would be represented by a many many to many relationship.
Is there a similiar tool like dataloader for solving this or can this maybe even be solved with dataloader itself?
The expectation with GraphQL is that the trip to the database is generally the fastest thing you can do, so you just add a resolver to Person.projects that makes a call to the database. You can still use dataLoaders for that.
const resolvers = {
Query: {
persons(parent, args, context) {
// 1st call to database
return someUsersService.list()
},
},
Person: {
projects(parent, args, context) {
// this should be a dataLoader behind the scenes.
// Makes second call to database
return projectsService.loadByUserId(parent.id)
}
}
}
Just remember that now your dataLoader is expecting to return an Array of objects in each slot instead of a single object.

How can Gatsby/GraphQL sort an integer field numerically rather than alphabetically?

I'm building a Gatsby site (v2.24.47) where I pull data from a CSV file with information about pages in a book. This is loaded using gatsby-transformer-csv, and I use custom schema to ensure that the column page_num is transformed into a Int-type field. But it seems as if Gatsby is ordering the field alphabetically rather than numerically. For example, if I query all pages after, say, page 12, it includes page 8 and 9, and indeed just sorting the pages also returns them in alphabetic order. I need the pages to be sorted numerically. Is there a way to do so?
Any help would be much appreciated. I've included a minimal example below.
gatsby-config.js
module.exports = {
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/data/`,
}
},
`gatsby-transformer-csv`
],
}
gatsby-node.js
exports.createSchemaCustomization = ({ actions, schema }) => {
const { createTypes } = actions
createTypes(`
type PagesCsv implements Node #dontInfer {
page_num: Int
}
`)
}
data/pages.csv
page_num
8
9
10
11
12
13
14
GraphQL query (in GraphiQL)
query MyQuery {
allPagesCsv(filter: {page_num: {gt: 12}}) {
nodes {
page_num
}
}
}
returns
{
"data": {
"allPagesCsv": {
"nodes": [
{
"page_num": 8
},
{
"page_num": 9
},
{
"page_num": 13
},
{
"page_num": 14
}
]
}
},
"extensions": {}
}
Similarly, querying allPagesCsv(sort: {fields: page_num}) returns the pages in alphabetic order 10, 11, 12, 13, 14, 8, 9.

How to make a condition based on relation ship count in Laravel Eloquent?

I would like to add a condition based on the tasks relationship count,
Here is the code:
return TeamleaderDeal
::withCount('tasks')
->get();
The result is:
[
{
"id": 4,
(...)
"dealPhase": "Refused",
"tasksCount": 5,
(...)
},
{
"id": 5,
(...)
"tasksCount": 0,
"companyLanguage": "nl",
(...)
},
{
"id": 16,
(...)
"dealPhase": "New",
"tasksCount": 17,
(...)
},
{
(...)
How to only return results where tasksCount equal to 5?
You may use has method like this:
// Retrieve all team-leader deals that have 5 tasks...
return TeamleaderDeal
::has('tasks', '=', 5)
->get();
Check Laravel docs for more info.

Laravel 5.5 - Merge 2 collections into one based on id?

So I have 2 models Books and Classes:
$books = Books::limit(3)->get(['id','classable_id','easy_book']);
// Books returned:
{ id: 200,
classable_id: 2,
easy_book: false
},
{ id: 201,
classable_id: 3,
easy_book: true
},
{ id: 202,
classable_id: 4,
easy_book: false
}
$classIds = $books->pluck('classable_id');
$classes = Classes::whereIn('id', $classIds);
// Classes returned:
{ id: 2,
subject: Math,
students: 30
},
{ id: 3,
subject: History,
students: 30
},
{ id: 4,
subject: Physics,
students: 30
}
Then trying to get the following output (without combining the queries, but keeping them separate like above, and just using php logic to output):
Classes returned:
{ id: 2,
subject: Math,
students: 30.
easy_book: false }, // trying to merge this!
{ id: 3,
subject: History,
students: 30.
easy_book: true}, // trying to merge this!
{ id: 4,
subject: Physics,
students: 30.
easy_book: false } // trying to merge this!
Basically, I am trying to merge the easy_book field from books returned to the respective class returned based on class.id == books.classable_id. Any idea how to merge it?
Add a relationship to your Books model like so:
public function class() {
return $this->belongsTo(Classes::class, 'id', 'classable_id);
}
Then you can do:
Book::with('class')->select('id', 'classable_id', 'easy_book')->limit(3)->get();
Each collection item will then have a collection of classes where applicable.
If after that you want to manipulate them, you can use the map function as documented here: https://laravel.com/docs/5.7/collections#method-map

Conditional update of several fields

I'm new with rethinkdb, I'm trying to write an update query preferred upsert which will set values to several fields if they don't exist or their values is less than the new value I want to set. Below is the way I did it with mongodb
collection.updateOne(new BasicDBObject(BookKeeperEvent.tenantPropertyName, bookKeeper.getTenantId()).append(BookKeeperEvent.timeLayerPropertyName, bookKeeper.getTimeLayer()),
new BasicDBObject("$max", new BasicDBObject(BookKeeperEvent.latestFullDataPropertyName, bookKeeper.getLatestFullData())
.append(BookKeeperEvent.latestRawDataPropertyName, bookKeeper.getLatestRawData())
.append(BookKeeperEvent.latestHealthPropertyName, bookKeeper.getLatestHealth())
.append(BookKeeperEvent.lastUpdatePropertyName, bookKeeper.getLastUpdate())
.append(BookKeeperEvent.firstFullDataPropertyName, bookKeeper.getFirstFullData()))
.append("$setOnInsert", new BasicDBObject(BookKeeperEvent.tenantPropertyName, bookKeeper.getTenantId()).append(BookKeeperEvent.timeLayerPropertyName, bookKeeper.getTimeLayer())),
new UpdateOptions().upsert(true))
This code set tenantId and timeLayer only if they are null, and for the other fields only if they are null or the value is less than the value I set in the query.
Is there a way to do the same thing in rethinkdb? and how?
I think the following query is equivalent to what I did in mongo but it fails.
r.table('book_keeper').filter({tenantId: '123', timeLayer: 4}).coerceTo('array').do(function (matches) {
return r.branch(
matches.isEmpty(),
r.table('book_keeper').insert({tenantId: '123', timeLayer: 4, latestRawData: 100, id: 4}),
r.table('book_keeper').get(4).update(function (doc) {
return r.branch(
doc.not(doc.hasFields('firstFullData')).or(doc('firstFullData').lt(100)),
{firstFullData : 100},
null)
}
))
})
The excption is:
e: Expected 1 argument but found 2 in:
r.table("book_keeper").filter({"tenantId": "123", "timeLayer": 4}).coerceTo("array").do(function(var_193) { return r.branch(var_193.isEmpty(), r.table("book_keeper").insert({"tenantId": "123", "timeLayer": 4, "latestRawData": 100, "id": 4}), r.table("book_keeper").get(4).update(function(var_194) { return r.branch(var_194.not(var_194.hasFields("firstFullData")).or(var_194("firstFullData").lt(100)), {"firstFullData": 100}, null); })); })
How can I fix it?
Thanks,
Daniela

Resources