I was evaluating Hasura GraphQL engine + Postgres on Heroku and have run into a situation. I have 2 simple tables as shown below;
Employees
Payrolls
Employees table and Payrolls have a foreign key employees.ID -> payrolls.employee_id
Employees table has a foreign key with itself employees.manager_id -> employees.ID
I have gone ahead and "auto-tracked" all the relationships in Hasura.
What I want to calculate is the "sum of salaries" for all employees reporting. But, when I'm using the Hasura explorer to form the GQL query, I'm not able to find the "sum" aggregation under the "managed_employees_aggregate" subquery.
The expected output is
[
{
"full_name": "anuj gupta",
"total_reportee_salary": 4000
},
{
"full_name": "sowmya",
"total_reportee_salary": 2000
},
... "total_reportee_salary" for everyone else is 0
]
Any suggestions or references is really appreciated.
Answering my own question
As suggested by #Ambassel in the comments I ended up creating a view
create view reportee_total_vw AS
select
employees.manager_id,
SUM(payrolls.salary)
from
employees,
payrolls
where
payrolls.employee_id = employees."ID"
group by
employees.manager_id
Next I created a relationship named "reportee" that bound the "ID" from employees table with the "manager_id" from the view.
After that I could issue the underlying GQL query to get the result I wanted (although not in the exact format, but I can live with that :))
{
employees {
full_name
reportee {
total_reportee_salary:sum
}
}
}
Related
I have table structure like this :
Admin can create another admin, and they can create Accounts. So In account table, I have add relationship like this :
#ManytoOne(()=> adminEntity, (Admin)=> admin.id)
created_by: String
Now I want to fetch all the account detail with admin's detail like this :
[{
name: "account 1",
created_by: "fa46-f456as-45f6sd-fads"
owner : <Admin Detail of created_by id above>
},
{
name: "account 2",
created_by: "f45sd6-4654fd-5f4asd6-f654asd"
owner : <Admin Detail of created_by id above>
}
]
I have setup only single relationship in my entity : ManyToOne. Not setup relationship in admin table (Id) to OnetoMeny.
How can I get such a data object using typeorm querybuilder. I am using nest js framework for it.
If anyone have some idea how to get data from multiple table. then please help me.
What I tried :
const query = this.Account.createQueryBuilder('account')
.leftJoinAndSelect('account.created_by', 'admin')
.execute()
But It is throwing error like this : create_by is ambiguous.
Please help me how can I get this kind of data?
I'm having trouble building a hasura graphql query that inserts a row into a table that has multiple foreign key relationships.
I have three tables, "Users", "Groups", and "UserGroups". The "UserGroups" table has foreign key relationships on columns groupId, and userId. I used Hasura's UI to track the relationships that it had suggested based on the two foreign keys.
I can create new UserGroups by connecting to the database directly but when I try to create a new UserGroup through a graphql query I get a foreign key violation response.
{
"errors": [
{
"extensions": {
"path": "$.selectionSet.insert_user_group.args.objects",
"code": "constraint-violation"
},
"message": "Foreign key violation. insert or update on table \"user_group\" violates foreign key constraint \"user_group_userId_fkey\""
}
]
}
Here is the mutation I'm trying to use:
mutation insert_user_group {
insert_user_group(objects: [{
userId: 1,
groupId: 1,
}]) {
affected_rows
}
}
I was only able to find documentation regarding querying tables with one-to-many relationships but nothing showing how to construct an insert mutation.
Turns out it was failing because I wasn't using strings in the query for the id values. They are BigInt columns but the queries required strings for the insert to work correctly.
Thank you #praveenweb for your comment.
From the admin UI, there is a Tables and Columns explorer that dutifully shows all available columns for a table, such as the incident table:
My ultimate goal is to be able to query all fields for a given table that I can insert data to (mostly centered around incident and problem tables), match that against what data I have, and then insert the record with a PUT to the table. The immediate problem I am having is that when I query sys_dictionary as various forums suggest, I only get returned a subset of the columns the UI displays.
Postman query:
https://{{SNOW_INSTANCE}}.service-now.com/api/now/table/sys_dictionary?sysparm_fields=internal_type,sys_name,name,read_only,max_length,active,mandatory,comments,sys_created_by,element&name={{TableName}}&sysparm_display_value=all
I understand the reduced result set has something to do with them being real columns in the table vs. links to other tables but I can't find any documentation describing how to get the result set that the UI has using the REST api.
The follow on problem is that I can't find an example with an example payload where all standard fields have been filled out for the incident table so that I can populate as many fields as I have data for.
The reason you don't get all the columns back is because the table you are querying inherits from another table. You need to go through all the inheritance relationships first, finding all parent tables, then query the sys_dictionary for all of those tables.
In the case of the incident table, you need to query the sys_db_object table (table of all tables) to find the parent, which is the task table. Then query the sys_db_object table again to find its parent, which is empty, so we have all the relevant tables: incident and task. Obviously, you would want to write this code as a loop, building up a list of tables by querying the table at the end of the list.
Once you have this list, you can query sys_dictionary with the query: sysparm_query=name=incident^ORname=task, which should return your full list of columns.
I think you could do this by creating your own scripted rest api and iterating/inspecting the fields:
(function process(/*RESTAPIRequest*/ request, /*RESTAPIResponse*/ response) {
var queryParams = request.queryParams;
var table = queryParams.table;
var t = new GlideRecord(table);
t.initialize();
var fields = t.getElements(); //or getFields if global scope
var fieldList = [];
for (var i = 0; i < fields.length; i++) {
var glideElement = fields[i]; //or field.get(i) if global scope
var descriptor = glideElement.getED();
var fldName = glideElement.getName().toString();
var fldLabel = descriptor.getLabel().toString();
var fldType = descriptor.getInternalType().toString();
var canWrite = glideElement.canWrite();
if (canWrite){
fieldList.push({
name: fldName,
type: fldType,
label: fldLabel,
writable: canWrite
});
}
}
return fieldList;
})(request, response);
This should save you the hassle of determining the inheritance of fields. Here's the sample output:
{
"result": [
{
"name": "parent",
"type": "reference",
"label": "Parent",
"writable": true
},
{
"name": "made_sla",
"type": "boolean",
"label": "Made SLA",
"writable": true
},
...
I'm trying to model a cataloging system in DynamodDB. It has "Catalogs" which contains "Collections". Each "Collection" can be tagged by many "Tags".
In an RDBMS I would create a table "Catalogs" with a 1:n relationship with "Collections". "Collections" would have an n:n with "Tags" as a Collection can have multiple Tags and a Tag can belong to multiple Collections.
The queries I want to run are:
1) Get all catalogs
2) Get catalog by ID
3) Get collections by catalog ID
I read on AWS I can use the adjacency list map design (because I have the n:n with "Tags"). So here is my table structure:
PK SK name
cat-1 cat-1 Sales Catalog
cat-1 col-1 Sales First Collection
cat-1 col-2 Sales Second Collection
cat-2 cat-2 Finance Catalog
tag-1 tag-1 Recently Added Tag
col-1 tag-1 (collection, tag relationship)
The problem here is I have to use a scan which I understand to be inefficient in order to get all "Catalogs" because a query's PK has to be an '=' and not a 'Begins With'.
The only thing I can think of is creating another attribute like "GSI_PK" and add "Catalog_1" when the PK is cat-1 and the SK is cat-1, "Catalog_2" when the PK is cat-2 and SK is cat-2. I've never really see this done so I'm not sure if it's the way to go and it takes some maintenance if I ever want to change IDs.
Any ideas how I would accomplish this?
In that case, you can have the PK be the type of the object and the SK be a uuid. A record would look like this { PK: "Catalog", SK: "uuid", ...other catalog fields }. You can then do a get all catalogs by doing a query on the PK = Catalog.
To store the associations you can have a GSI on two fields sourcePK and relatedPK where you could store records that associate things. To associate an object you would create a record like e.g. { PK: "Association", SK: "uuid", sourcePK: "category-1", relatedPK: "collection-1", ... other data on the association }. To find objects associated with the "Catalog" with id 1, you would do a query on the GSI where sourcePK = catalog-1.
With this setup you need to be careful about hot keys and should make sure you never have more than 10GBs of data under the same partition key in a table or index.
Let's walk through it. I'll use GraphQL SDL to layout the design of the data model & queries but you can just apply the same concepts to DynamoDB directly.
Thinking data model first we will have something like:
type Catalog {
id: ID!
name: String
# Use a DynamoDB query on the **Collection** table
# where the **catalogId = $ctx.source.id**. Use a GSI or make catalogId the PK.
collections: [Collection]
}
type Collection {
id: ID!
name: String
# Use a DynamoDB query on the **CollectionTag** table where
# the **collectionId = $ctx.source.id**. Use a GSI or make the collectionId the PK.
tags: [CollectionTag]
}
# The "association map" idea as a GraphQL type. The underlying table has a collectionId and tagId.
# Create objects of this type to associate a collection and tag in the many to many relationship.
type CollectionTag {
# Do a GetItem on the **Collection** table where **id = $ctx.source.collectionId**
collection: Collection
# Do a GetItem on the **Tag** table where **id = $ctx.source.tagId**
tag: Tag
}
type Tag {
id: ID!
name: String
# Use a DynamoDB query on teh **CollectionTag** table where
# the **tagId = $ctx.source.id**. If collectionId is the PK then make a GSI where this tagId is the PK.
collections: [CollectionTag]
}
# Root level queries
type Query {
# GetItem to **Catalog** table where **id = $ctx.args.id**
getCatalog(id: ID!): Catalog
# Scan to **Catalog** table. As long as you don't care about ordering on a filed in particular then
# this will likely be okay at the top level. If you only want all catalogs where "arePublished = 1",
# for example then we would likely change this.
allCatalogs: [Catalog]
# Note: You don't really need a getCollectionsByCatalogId(catalogId: ID!) at the top level because you can
# use `query { getCatalog(id: "***") { collections { ... } } }` which is effectively the same thing.
# You could add another field here if having it at the top level was a requirement
getCollectionsByCatalogId(catalogId: ID!): [Collection]
}
Note: Everywhere I use [Collection] or [Catalog] etc above you should use a CollectionConnection, CatalogConnection, etc wrapper type to enable pagination.
I am trying to get one query work since morning and not able to get it working I have two tables photographers and reviews please have a look at structure and then I will ask the question at the bottom :
Reviews table :
id int(10) unsigned -> primary key
review text
user_id int(10) unsigned foreign key to users table
user_name varchar(64)
photographer_id int(10) unsigned foreign key to photographers table
Photographers table :
id int(10) unsigned -> primary key
name text
brand text
description text
photo text
logo text
featured varchar(255)
Photographers model :
class Photographer extends Model
{
public function reviews()
{
return $this->hasMany('\App\Review');
}
}
Reviews Model :
class Review extends Model
{
public function photographers()
{
return $this->belongsTo('\App\Photographer');
}
}
My logic to query the records
$response = Photographer::with(['reviews' => function($q)
{
$q->selectRaw('max(id) as id, review, user_id, user_name, photographer_id');
}])
->where('featured', '=', 'Yes')
->get();
The question is : I want to fetch all the photographers who have at least one review in the review table, also I want to fetch only one review which is the most latest, I may have more than one review for a photographer but I want only one.
I would add another relationship method to your Photogrpaher class:
public function latestReview()
{
return $this->hasOne('App\Review')->latest();
}
Then you can call:
Photographer::has('latestReview')->with('latestReview')->get();
Notes:
The latest() method on the query builder is a shortcut for orderBy('created_at', 'desc'). You can override the column it uses by passing an argument - ->latest('updated_at')
The with method loads in the latest review.
The has method only queries photographers that have at least one item of the specified relationship
Have a look at Has Queries in Eloquent. If you want to customise the has query further, the whereHas method would be very useful
If you're interested
You can add query methods to the result of a relationship method. The relationship objects have a query builder object that they pass any methods that do not exist on themselves to, so you can use the relationships as a query builder for that relationship.
The advantage of adding query scopes / parameters within a relationship method on an Eloquent ORM model is that they are :
cacheable (see dynamic properties)
eager/lazy-loadable
has-queryable
What you need is best accomplished by a scoped query on your reviews relation.
Add this to your Review model:
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Eloquent\Model;
class Review extends Model {
public function scopeLatest(Builder $query) {
// note: you can use the timestamp date for the last edited review,
// or use "id" instead. Both should work, but have different uses.
return $query->orderBy("updated_at", "desc")->first();
}
}
Then just query as such:
$photographers = Photographer::has("reviews");
foreach ($photographers as $photographer) {
var_dump($photographer->reviews()->latest());
}