Configure postgraphile to support succinct nested mutations - postgraphile

I have come to postgraphile yesterday from a couple of years with Hasura. I was stunned at what PG does out of the box. My main sense of loss is around the GQL api that PG offers, relative to Hasura. If I have Customer with a foreign key to an Address, and I add the nested mutations plugin, I can say this:
mutation MyMutation {
createCustomer(
input: {customer: {addressToAddressId: {create: {line1: "5", line2: "The Street"}}, firstName: "Breda", lastName: "Smith"}}
) {
address {
id
}
customer {
id
}
}
}
But with Hasura I would be able to say something like :
mutation MyMutation {
createCustomer(input:{firstName: "Breda", lastName: "Smith", address:{line1: "5", line2: "The Street"}}) {
address {
id
}
customer {
id
}
}
}
How can I configure PG so it can do this kind of mutation?

I asked on the postgraphile discord channel, and Benjie replied:
It’d be possible to write a plugin to do this, but I’m not a big fan of CRUD mutations in general. I’d do it this way:
The first way to consider adding a mutation to your GraphQL schema is with a Custom Mutations. These create individual highly specialised mutations from the VOLATILE functions in your PostgreSQL database. There's a checklist of requirements they must meet defined in their documentation .
If this doesn't suit your needs, you may also using makeExtendSchemaPlugin or have a look through the community pluginsto see if one suits your needs. Relevant reading on GraphQL mutation design

Related

Correct implementation of MySQL tables in MongoDB?

I'm new to mongoDB, and I wonder how I can translate tables relations in MongoDB.
For example in my MySQL base, I have two tables : Manager and Employee.
Manager {
name: String,
ID: Int Primary Key
}
Employee {
name: String,
ID: Int Primary Key,
Manager: int Foreign key (Manager.ID)
}
I would like to know what is the correct way to implement this with MongoDB.
MongoDB is a non-relational document database.
'Document' is a record in a collection and each document can have any number of fields, including arrays, maps, etc. The strength and weakness of this are that two documents in the same collection may hold completely different fields. Normal forms of relational databases do not apply. Also, each document has a unique id.
How to link documents/collections together is completely up to the app logic.
Few options:
Keep everything together in once collection.
//
{
name: "Jon Appleseed",
manager: "Jonh Bosch"
},
{
name:"Another employee",
manager: "Jonh Bosch"
}
Use two collections as suggested by #iprakashv
Use a mixed approach:
// Employees
{
_id: "_234234",
name: "Jon Appleseed",
manager:{
name:"John Bosh",
id: _12321
}
},
{
_id: "_233234",
name: "Another employee",
manager:{
name:"John Bosh",
id: "_12321"
}
}
// Managers
{
_id: _123213,
name:"John Bosh",
subordinates:
[
{name:"Jon Appleseed", id: "_234234" },
{name:"Another employee", id: "_12321" },
]
}
As you see, you gain much more flexibility at the expense of normalization.
As mongodb is a NoSQL database for schema-less and relation-less collections (though you can put validations over a collection), join-less schema and nested objects make more sense here.
Now as per our requirement, each Manager object is linked to its employee so, should ideally have an array containing all its employee objects, but, it is not practical when we need all the employees irrespective of the Manager.
So, we may want to have just the array of employee id's instead of an array of employee objects and employee collection (table) to be maintained separately to fetch employee objects.
The samples documents for the above schema would look like:
//employee collection document
{
"ID": "E12334",
"name": "John Doe"
}
//manager collection document
{
"ID": "M453",
"name": "Jane Doe",
"employeeIDs": ["E12334", "E12343"]
}

JSON Deserialization on Talend

Trying to figuring out how to deserialize this kind of json in talend components :
{
"ryan#toofr.com": {
"confidence":119,"email":"ryan#toofr.com","default":20
},
"rbuckley#toofr.com": {
"confidence":20,"email":"rbuckley#toofr.com","default":15
},
"ryan.buckley#toofr.com": {
"confidence":18,"email":"ryan.buckley#toofr.com","default":16
},
"ryanbuckley#toofr.com": {
"confidence":17,"email":"ryanbuckley#toofr.com","default":17
},
"ryan_buckley#toofr.com": {
"confidence":16,"email":"ryan_buckley#toofr.com","default":18
},
"ryan-buckley#toofr.com": {
"confidence":15,"email":"ryan-buckley#toofr.com","default":19
},
"ryanb#toofr.com": {
"confidence":14,"email":"ryanb#toofr.com","default":14
},
"buckley#toofr.com": {
"confidence":13,"email":"buckley#toofr.com","default":13
}
}
This JSON comes from the Toofr API where documentation can be found here .
Here the actual sitation :
For each line retreived in the database, I call the API and I got this (the first name, the last name and the company change everytime.
Does anyone know how to modify the tExtractJSONField (or use smthing else) to show the results in tLogRow (for each line in the database) ?
Thank you in advance !
EDIT 1:
Here's my tExtractJSONfields :
When using tExtractJSONFields with XPath, you need
1) a valid XPath loop point
2) valid XPath mapping to your structure relative to the loop path
Also, when using XPath with Talend, every value needs a key. The key cannot change if you want to loop over it. Meaning this is invalid:
{
"ryan#toofr.com": {
"confidence":119,"email":"ryan#toofr.com","default":20
},
"rbuckley#toofr.com": {
"confidence":20,"email":"rbuckley#toofr.com","default":15
},
but this structure would be valid:
{
"contact": {
"confidence":119,"email":"ryan#toofr.com","default":20
},
"contact": {
"confidence":20,"email":"rbuckley#toofr.com","default":15
},
So with the correct data the loop point might be /contact.
Then the mapping for Confidence would be confidence (the name from the JSON), the mapping for Email would be email and vice versa for default.
EDIT
JSONPath has a few disadvantages, one of them being you cannot go higher up in the hierarchy. You can try finding out the correct query with jsonpath.com
The loop expression could be $.*. I am not sure if that will satisfy your need, though - it has been a while since I've been using JSONPath in Talend because of the downsides.
I have been ingesting some complex json structures and did this via minimal json libraries, and tjava components within talend.

getDegree()/isOutgoing() funcitons don't work in graphAware/neo4j-to-elasticsearch mapping.json

Neo4j Version: 3.2.2
Operating System: Ubuntu 16.04
I use getDegree() function in mapping.json file, but the return would always be null; I'm using the dataset neo4j tutorial Movie/Actor dataset.
Output from elasticsearch request
mapping.json
{
"defaults": {
"key_property": "uuid",
"nodes_index": "default-index-node",
"relationships_index": "default-index-relationship",
"include_remaining_properties": true
},
"node_mappings": [
{
"condition": "hasLabel('Person')",
"type": "getLabels()",
"properties": {
"getDegree": "getDegree()",
"getDegree(type)": "getDegree('ACTED_IN')",
"getDegree(direction)": "getGegree('OUTGOING')",
"getDegree('type', 'direction')": "getDegree('ACTED_IN', 'OUTGOING')",
"getDegree-degree": "degree"
}
}
],
"relationship_mappings": [
{
"condition": "allRelationships()",
"type": "type",
}
]
}
Also if I use isOutgoing(), isIncoming(), otherNode function in relationship_mappings properties part, elasticsearch would never load the relationship data from neo4j. I think I probably have some misunderstanding of this sentence only when one of the participating nodes "looking" at the relationship is provided on this page https://github.com/graphaware/neo4j-framework/tree/master/common#inclusion-policies
mapping.json
{
"defaults": {
"key_property": "uuid",
"nodes_index": "default-index-node",
"relationships_index": "default-index-relationship",
"include_remaining_properties": true
},
"node_mappings": [
{
"condition": "allNodes()",
"type": "getLabels()"
}
],
"relationship_mappings": [
{
"condition": "allRelationships()",
"type": "type",
"properties": {
"isOutgoing": "isOutgoing()",
"isIncomming": "isIncomming()",
"otherNode": "otherNode"
}
}
]
}
BTW, is there any page that list all of the functions that we can use in mapping.json? I know two of them
github.com/graphaware/neo4j-framework/tree/master/common#inclusion-policies
github.com/graphaware/neo4j-to-elasticsearch/blob/master/docs/json-mapper.md
but it seems there are more, since I can use getType(), which hasn't been listed in any of the above pages.
Please let me know if I can provide any further help to solve the problem
Thanks!
The getDegree() function is not available to use, in contrary to getType(). I will explain why :
When the mapper (the part responsible to create a node or relationship representation as ES document ) is doing its job, it receive a DetachedGraphObject being a detached node or relationship.
The meaning of detached is that it is happening outside of a transaction and thus query operations are not available against the database anymore. The getType() is available because it is part of the relationship metadata and it is cheap, however if we would want to do the same for getDegree() this can be seriously more costly during the DetachedObject creation (which happen in a tx) depending on the number of different types etc.
This is however something we are working on, by externalising the mapper in a standalone java application coupled with a broker like kafa, rabbit,.. between neo and this app. We would not, however offer the possibilty to requery the graph in the current version of the module as it can have serious performance impacts if the user is not very careful.
As last, the only suggestion I can give you is to keep a property on your node with the updates of degrees you need to replicate to ES.
UPDATE
Regarding this part of the documentation :
For Relationships only when one of the participating nodes "looking" at the relationship is provided:
This is used only when not using the json definition, so you can use one or the other. the json definition has been added later as addition and both cannot be used together.
For answering this part, it means that the nodes of the incoming or outgoing side, depending on the definition, should be included in the inclusion policy for nodes, like hasLabel('Employee') || hasProperty('form') || getProperty('age', 0) > 20 . If you have an allNodes policy then it is fine.

Design RESTful API using HAL - serialize model relationships

I'm relatively new to REST but I've been doing my homework on how RESTful should be. Now I'm trying to create a RESTful api implementing a JSON+HAL serializer for my models which have relationships with other models.
Example models in python:
class Category(Model):
name = CharField()
parent = ManyToOneField(Category)
categories = OneToManyField(Category)
products = ManyToManyField(Product)
class Product(Model):
name = CharField()
price = DecimalField()
related = ManyToManyField(Product)
categories = ManyToManyField(Category)
lets suppose we have a category "catalog" with a sub-category "food" with products "burger" and "hot-dog" which are both related.
First question. Categories and products should be resources so they need an URI, should I implement an uri field in my model and store it in the DB or somehow calculate it at runtime, what about multiple identifiers(URIs)?
Second question. Discoverability, In Hal format what should "GET /" and diferent nodes return to make the api easily self discoverable.
{
"_links":{
"self":{
"href":"/"
},
"categories":[
{
"href":"/catalog"
}
]
}
}
Third question. Add as properties, embed or link. Example "GET /catalog/food":
{
"_links":{
"self":{
"href":"/catalog/food"
}
},
"name":"food",
"parent":"/catalog",
"categories":[],
"products":[
"/products/burger",
"/products/hot-dog"
]
}
{
"_links":{
"self":{
"href":"/catalog/food"
},
"parent":{
"href":"/catalog"
},
"categories":[
],
"products":[
{
"href":"/products/burger"
},
{
"href":"/products/hot-dog"
}
]
},
"name":"food"
}
{
"_links":{
"self":{
"href":"/catalog/food"
}
},
"name":"food",
"_embedded":{
"parent":{
"_links":{
"self":{
"href":"/catalog"
}
},
"name":"catalog",
...
},
"categories":[
],
"products":[
{
"_links":{
"self":{
"href":"/products/burger"
}
},
"name":"burger",
...
},
{
"_links":{
"self":{
"href":"/products/hot-dog"
}
},
"name":"hot-dog",
...
}
]
}
}
Fourth question. How deep should I go when returning structures. Example "GET /catalog
{
"_links":{
"self":{
"href":"/catalog"
}
},
"name":"catalog",
"parent":null,
"categories":[
{
"name":"food",
"parent":{...},
"categories":[],
"products":[
{
"name":"burger",
"price":"",
"categories":[...],
"related":[...]
},
{
"name":"hot-dog",
"price":"",
"categories":[...],
"related":[...]
}
]
}
],
"products": []
}
About 1st question: I wouldn't store the URIs in the DB. You can easily calculate them inside your controller in runtime, and it's of controller's responsibility to care about URIs. This way you keep your model and your API decoupled, and should you decide to change the API structure in the future, you won't need to update your whole database with the new URIs.
About multiple identifiers, I'm not sure what the question is, but again, in my opinion, it has nothing to do with the DB, it's the routing and the controllers who should care about how to deal with any URIs.
About 2nd question: First of all, as a side note: I would keep the word categories as part of the URI. For example, I 'd have http://domain.com/api/categories/catalog/food. This way, you make your API more descriptive and more "hackable", meaning that user should be able to remove the /catalog/food part and expect to receive a collection with all the available categories.
Now, about what GET should return to allow discoverability: I think it's already being made clear from your URI structure. When user hits GET /categories he expects to get a list with the categories (the name and URI for each, to keep it lightweight), and when he follows one of the URIs like GET /categories/catalog he should receive the resource catalog which is a category. Likewise, when he wants to GET /products/burger, he should receive a product resource with all the attributes you have in your model. You may want to check this example about the structure of your responses.
About 3rd question: Again, the same example can help you form the structure. I think your 2nd way of response is closer to that, but I would also add a name field, not only the href.
About 4th question: When the GET request expects a collection of resources (like GET /categories) I would suggest providing only the necessary for each resource, that is, the name and the URI for each and only when user follows the desired URI, he can receive the rest information.
In your example, catalog is a resource, so on GET /categories/catalog I would include of course the name of the resource (catalog) and its self link, and for parent, sub-categories and products that are related to it, I would just provide the name and the URI for each, to keep it light. But: This was a general thought about designing APIs. In your actual problem, you should decide depending on your specific business problem. I mean, if your API is about a restaurant menu with categories and dishes, you may want to include the price, or a small description even when responding not for the actual product but for a collection of products, because probably for your users, that's an important information. So generally, provide all the necessary info (you only know what are these for your problem) when responding about a list of resources, and provide all the details of the resource when responding about a specific resource.
I would store something in the DB and calculate the URI at Runtime. That way if you move boxes it's not static.
Create a 'bookmark' page. The page we created was just a list of links with their rels. I believe HAL defines that specifically. The bookmark page was the only page other pages needed to know about
Not sure about this
How deep you go is up to you. There is a big debate now at my place of work for fine grain vs course grain. I'm going to do fine grain with small resource to keep the api simple, but then use the Expand-ability concept. It's a combination ofthe idea of composite resources defined on pg 35 of Subbu’s REST book and the expand concept used by Netflix. http://developer.netflix.com/docs/REST_API_Conventions

JSON structuring a groupchat

I'm going to implement a groupchat into my app by using Firebase. I were thinking of 2 different structures of saving data in JSON.
First structure:
Second structure:
I would like to achieve a fast query and i would like it to parse a small amount of data each time. What structure should i go for and are there maybe a better alternative then these 2?
The first solution is clearly not viable, as you would have a hard time finding all messages belonging to a given group.
The second solution is ok if every time you will query a given group's node, you are going to need also all of its messages, which is probably not what you want.
It is hard, of course, to advise on data structure without more info on your use-case, the queries you are going to make, etc., but a rather standard approach would be:
{
"users": {
"$userId"": {
// user data
}
},
"groups": {
"$groupId": {
// group data
}
},
"group_users": {
"$groupId": {
"$userId": true
// separation of list of users from the group is useful
// if you are going to query the group node not needing its full list of users
}
},
"group_messages": {
"$groupId": {
"$messageId": {
// message data
}
}
}
}