How to resolve graphene.Union Type? - graphql

I want to create a UnionType(graphene.Union) of two existing types (FirstType and SecondType) and be able to resolve the query of this union type.
Schema
class FirstType(DjangoObjectType):
class Meta:
model = FirstModel
class SecondType(DjangoObjectType):
class Meta:
model = SecondModel
class UnionType(graphene.Union):
class Meta:
types = (FirstType, SecondType)
So with this schema I want to query all objects from FirstType and SecondType with pk in some list [pks]
query {
all_items(pks: [1,2,5,7]){
... on FirstType{
pk,
color,
}
... on SecondType{
pk,
size,
}
}
}
PKs from FirstType are normally not in the SecondType.
I tried like one below
def resolve_items(root, info, ids):
queryset1 = FirstModel.objects.filter(id__in=pks)
queryset2 = SecondModel.objects.filter(id__in=pks)
return queryset1 | queryset2
but it gives an error: 'Cannot combine queries on two different base models.'
I expect the following response from query:
{ 'data':
{'all_items':[
{'pk': 1,
'color': blue
},
{'pk': 2,
'size': 50.0
},
...
]}
}
So how the resolver should look like?

The graphene Documentation on union types is very sparse. Here is a working example of how to do it correctly:
from graphene import ObjectType, Field, List, String, Int, Union
mock_data = {
"episode": 3,
"characters": [
{
"type": "Droid",
"name": "R2-D2",
"primaryFunction": "Astromech"
},
{
"type": "Human",
"name": "Luke Skywalker",
"homePlanet": "Tatooine"
},
{
"type": "Starship",
"name": "Millennium Falcon",
"length": 35
}
]
}
class Human(ObjectType):
name = String()
homePlanet = String()
class Droid(ObjectType):
name = String()
primaryFunction = String()
class Starship(ObjectType):
name = String()
length = Int()
class Character(Union):
class Meta:
types = (Human, Droid, Starship)
#classmethod
def resolve_type(cls, instance, info):
if instance["type"] == "Human":
return Human
if instance["type"] == "Droid":
return Droid
if instance["type"] == "Starship":
return Starship
class RootQuery(ObjectType):
result = Field(SearchResult)
def resolve_result(_, info):
return mock_data
Then, for a query like
query Humans {
result {
episode
characters {
... on Droid {
name
}
... on Starship {
name
}
... on Human {
name
}
}
}
}
it returns the correct result.

Okay, so I was too concentrated on merging query sets and I didn't notice that I can simply return a list.
So here is solution which gives me the response I was looking for:
def resolve_items(root, info, ids):
items = []
queryset1 = FirstModel.objects.filter(id__in=pks)
items.extend(queryset1)
queryset2 = SecondModel.objects.filter(id__in=pks)
items.extend(queryset2)
return items

Related

How to use orderBy for graphQL

I am trying to sort reservesUSD of nested object dailyPoolSnapshots In descending order by timestamp and return it's first value (in other words, return the latest entry).
I know almost nothing of GraphQL and it's documentation seems confusing and scarce. Can someone help me to figure out how to sort my objects?
I am using subgraphs on the Ethereum mainnet for Curve.fi to get information about pools
My code:
pools(first: 1000) {
name
address
coins
coinDecimals
dailyPoolSnapshots(first: 1,
orderBy:{field: timestamp, order: DESC}) {
reservesUSD
timestamp
}
}
}
It throws and error:
"errors": [
{
"locations": [
{
"line": 0,
"column": 0
}
],
"message": "Invalid value provided for argument `orderBy`: Object({\"direction\": Enum(\"DESC\"), \"field\": Enum(\"timestamp\")})"
}
]
}```
Here is your solution
{
pools(first: 1000) {
name
address
coins
coinDecimals
dailyPoolSnapshots(first: 1,
orderBy: timestamp, orderDirection:desc) {
reservesUSD
timestamp
}
}
}
In the playground, you have the doc on the right, you can search dailyPoolSnapshots, if you click on it, you will have the documentation of this query
Sample for this query:
Type
[DailyPoolSnapshot!]!
Arguments
skip: Int = 0
first: Int = 100
orderBy: DailyPoolSnapshot_orderBy
orderDirection: OrderDirection
where: DailyPoolSnapshot_filter
block: Block_height
Arguments are all the params you can use
The orderBy and orderDirection are separate query params, and orderDirection needs to be lowercase for their enum syntax.
{
platforms(first: 5) {
id
pools {
id
dailyPoolSnapshots(first: 1, orderBy: timestamp, orderDirection: asc) {
timestamp
}
}
poolAddresses
latestPoolSnapshot
}
registries(first: 5) {
id
}
}

elasticsearch sort by price with currency

I have data
{
"id": 1000,
"price": "99,01USA",
},
{
"id": 1001,
"price": "100USA",
},
{
"id": 1002,
"price": "780USA",
},
{
"id": 1003,
"price": "20USA",
},
How I sort order by price (ASC , DESC)
You can alter it a little to parse price to integer and then sort it
You can create a dynamic sort function that sorts objects by their value that you pass:
function dynamicSort(property) {
var sortOrder = 1;
if(property[0] === "-") {
sortOrder = -1;
property = property.substr(1);
}
return function (a,b) {
/* next line works with strings and numbers,
* and you may want to customize it to your needs
*/
var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
return result * sortOrder;
}
}
So you can have an array of objects like this:
var People = [
{Name: "Name", Surname: "Surname"},
{Name:"AAA", Surname:"ZZZ"},
{Name: "Name", Surname: "AAA"}
];
...and it will work when you do:
People.sort(dynamicSort("Name"));
People.sort(dynamicSort("Surname"));
People.sort(dynamicSort("-Surname"));
Actually this already answers the question. Below part is written because many people contacted me, complaining that it doesn't work with multiple parameters.
Multiple Parameters
You can use the function below to generate sort functions with multiple sort parameters.
function dynamicSortMultiple() {
/*
* save the arguments object as it will be overwritten
* note that arguments object is an array-like object
* consisting of the names of the properties to sort by
*/
var props = arguments;
return function (obj1, obj2) {
var i = 0, result = 0, numberOfProperties = props.length;
/* try getting a different result from 0 (equal)
* as long as we have extra properties to compare
*/
while(result === 0 && i < numberOfProperties) {
result = dynamicSort(props[i])(obj1, obj2);
i++;
}
return result;
}
}
Which would enable you to do something like this:
People.sort(dynamicSortMultiple("Name", "-Surname"));
Subclassing Array
For the lucky among us who can use ES6, which allows extending the native objects:
class MyArray extends Array {
sortBy(...args) {
return this.sort(dynamicSortMultiple(...args));
}
}
That would enable this:
MyArray.from(People).sortBy("Name", "-Surname");

Run Elasticsearch processor on all the fields of a document

I am trying to trim and lowercase all the values of the document that is getting indexed into Elasticsearch
The processors available has the field key is mandatory. This means one can use a processor on only one field
Is there a way to run a processor on all the fields of a document?
There sure is. Use a script processor but beware of reserved keys like _type, _id etc:
PUT _ingest/pipeline/my_string_trimmer
{
"description": "Trims and lowercases all string values",
"processors": [
{
"script": {
"source": """
def forbidden_keys = [
'_type',
'_id',
'_version_type',
'_index',
'_version'
];
def corrected_source = [:];
for (pair in ctx.entrySet()) {
def key = pair.getKey();
if (forbidden_keys.contains(key)) {
continue;
}
def value = pair.getValue();
if (value instanceof String) {
corrected_source[key] = value.trim().toLowerCase();
} else {
corrected_source[key] = value;
}
}
// overwrite the original
ctx.putAll(corrected_source);
"""
}
}
]
}
Test with a sample doc:
POST my-index/_doc?pipeline=my_string_trimmer
{
"abc": " DEF ",
"def": 123,
"xyz": false
}

access content of parent's field in graphene python

I am using graphene in python.
Let's say I have the following schema:
extends type Query {
a(search:String):A
}
type A {
b:B
important_info:ID
}
type B {
fieldone: String
fieldtwo: String
}
Now I'd like to query:
query {
a(search:"search string") {
b {
fieldone
}
}
}
however fieldone is based on important_info.
My class B looks like this:
class B(graphene.ObjectType):
fieldone = graphene.String()
fieldtwo = graphene.String()
def resolve_fieldone(self,info):
# Here I want access to important_info, but I don't know how ...
return "something based on important_info"
How can I access important info from within the resolver of fieldone?
It seems there is no obvious or documented solution for this requirement.
I solved it by adding the root object to info.context within the outermost type:
class B(ObjectType):
c = String()
def resolve_c(parent, info):
return 'foo' if info.context['z'] == '' else 'bar'
class A(ObjectType):
b = Field(B)
def resolve_b(parent, info):
return parent.b
class Query(ObjectType):
a = Field(A)
z = String()
def resolve_a(parent, info):
return some_function_to_get_a()
def resolve_z(parent, info):
z = some_function_to_get_z()
info.context['z'] = z
return z

Filtering multiple times on one dictionary

I currently run this code:
searchterm = "test"
results = resultsArray.filter { $0.description.contains (searchterm!) }
My question is how do I search in company_name or place or any other field in my model and add it to the results.
Do I need to use filters together and then append the results to a new variable instance of my model?
EDIT:
If "test" is in company_name, place and description. I want all three results returned. However, if "test" is only in place, I need only place to be returned.
EDIT2:
This is an example of my model return. Is this a dictionary or an array? I'm sorry I dont 100% percent know the difference. I know ' "this": is ' what a dictionary looks like, however because there were [] brackets around them, I thought that made it an array...
struct GraphData {
var description: String
var company_name: String
var places: String
init(description: String, company_name: String, places: String){
self.description = description
self.company_name = company_name
self.places = places
}
func toAnyObject() -> Any {
print("return")
return [
"description": description,
"company_name": company_name,
"places": places,
]
}
The easiest way to do this would be to create a custom contains method in your model which can you can use to match the search term against any property in the model:
class YourModel {
var company_name: String
var description: String
var place: String
// ...
func contains(_ searchTerm: String) -> Bool {
return self.company_name.contains(searchTerm)
|| self.description.contains(searchTerm)
|| self.place.contains(searchTerm)
}
}
You can then simply filter using your custom method:
let searchTerm = "test"
let results = resultsArray.filter { $0.contains(searchTerm) }
Is this resultsArray a dictionary or an array?
You can do something like this
let searchTerm = "test"
let filter = resultsArray.filter{ $0.company_name!.contains(searchTerm) || $0.place!.contains(searchTerm) }
Edit
class TestClass: NSObject {
var place: String?
var company_name: String?
func contain(searchTerm: String) -> [String] {
var result = [String]()
if let placeVal = place, placeVal.contains(searchTerm) {
result.append(placeVal)
}
if let companyVal = company_name, companyVal.contains(searchTerm) {
result.append(companyVal)
}
return result
}
}
let searchTerm = "test"
let filter = resultsArray.map { $0.contain(searchTerm: searchTerm) }

Resources