GraphQL: UnionType with Scalar and InputObjectType - graphql

I have a database storing some videogames. Each of them has three release dates: one for Europe, another for America and a last one for Japan.
I would like to allow users of my GraphQL API to search for games according to these dates. For exemple, in order to get name and release date of games released in Europe and America but not released in Japan, we can write this query:
{
games(has_been_released: { eur: true, usa: true, jap: false }) {
data {
name
release_date
}
}
}
Therefore, if we want to fetch games released in all regions, the query will be this one:
{
games(has_been_released: { eur: true, usa: true, jap: true}) {
data {
name
release_date
}
}
}
When all booleans have the same value, I would like to simplify the query so that we could write the below instead:
{
games(has_been_released: true) {
data {
name
release_date
}
}
}
In order to do that, I've tried this type definition for field has_been_released (with graphql-php):
<?php
$regionalizedBooleanInput = new InputObjectType([
'name' => 'RegionalizedBooleanInput',
'description' => 'An object with properties "eur", "usa" and "jap" as booleans',
'fields' => [
'eur' => ['type' => Type::boolean(), 'defaultValue' => null],
'usa' => ['type' => Type::boolean(), 'defaultValue' => null],
'jap' => ['type' => Type::boolean(), 'defaultValue' => null],
]
]);
$booleanOrRegionalizedBooleanInputType = new UnionType([
'name' => 'BooleanOrRegionalizedBooleanInput',
'description' => 'A boolean that can be different according to regions',
'types' => [
Type::boolean(),
$regionalizedBooleanInput,
],
'resolveType' => function($value) use ($regionalizedBooleanInput) {
if ($value->type === 'boolean') {
return Type::boolean();
}
return $regionalizedBooleanInput;
},
]);
But when I do that, GraphiQL throws this error:
Error: Introspection must provide object type for possibleTypes.
at invariant (chrome-extension://fkkiamalmpiidkljmicmjfbieiclmeij/dist/chromeiql.js:14605:11)
at getObjectType (chrome-extension://fkkiamalmpiidkljmicmjfbieiclmeij/dist/chromeiql.js:72489:80)
at Array.map ()
at buildUnionDef (chrome-extension://fkkiamalmpiidkljmicmjfbieiclmeij/dist/chromeiql.js:72566:47)
at buildType (chrome-extension://fkkiamalmpiidkljmicmjfbieiclmeij/dist/chromeiql.js:725
So I assume something is wrong with my types definition, but I don't get why. Any ideas?
tl;dr: I would like to have a GraphQL field which can accept a scalar value or an InputObject with this scalar type as fields. Is it even possible?
Thanks in advance!

GraphQL currently doesn't support polymorphic input type or Union in input .
https://github.com/graphql/graphql-spec/issues/488
One solution could be all_regions to part of input schema but not mandatory
input Regions{
eur: Boolean
usa: Boolean
jpa: Boolean
all_regions: Boolean
}
{
games(has_been_released: { all_regions: true}) {
data {
name
release_date
}
}
}

Related

Laravel attach on associative key

I have a ManyToMany relationship setup but need to specify a value for the key in my array to attach.
$data = request()->validate([
'homework_title' => '', // string
'homework_description' => '', // string
'group_id' => '', // array
]);
$homework = request()->user()->homeworks()->create([
'homework_title' => $data['homework_title'],
'homework_description' => $data['homework_description'],
]);
$homework->groups()->attach($data['group_id'][key]);
group_id is an array and looks like the following:
[
{
"group_id": 1,
"group_name": "8x/En2"
},
{
"group_id": 2,
"group_name": "9x/En3"
}
]
How do I specify to attach only the group_id in array?
Got it to work, with:
Arr::pluck()
First time round I forgot to import the class (stupid me)!
use Illuminate\Support\Arr;
finally looking like the following:
$homework->groups()->attach(Arr::pluck($data['group_id'], 'group_id'));

Why Graphql cannot get the args by using variable but worked when using static var

My mutation scheme:
mutation edit($id: Int!) {
user_edit(id:$id) {
user_name
}
}
query variable is follow
{
"id": 1
}
I use this with laravel-graphql. Here is my definition of user_edit
class UserEdit extends Mutation
{
protected $attributes = [
'name' => 'UserEdit',
'description' => 'A mutation'
];
public function type()
{
return GraphQL::type('UserInfo');
}
public function args()
{
return [
'id' => [
'type' => Type::int(),
],
];
}
public function resolve($root, $args, $context, ResolveInfo $info)
{
var_dump($args);exit;
$data = User::find($args['id']);
return $data;
}
}
I use my query string to request the graphql server, then server return my error
{
"data": null,
"errors": [
{
"message": "Variable \"$edit1\" of required type \"Int!\" was not provided.",
"locations": [
{
"line": 1,
"column": 11
}
]
}
]
}
l tried a lot things and read the document on github [https://github.com/Folkloreatelier/laravel-graphql#creating-a-mutation][1]
and read the document of graphql's website ,and change my args definition style in many ways, but all failed, and Strange is l can get args but use static variable like follow
mutation edit{
user_edit(id:1) {
user_name
}
}
and then it worked! l tried to goole but get nothing about this. l think l really need some help
Comparing the docs vs your code, seems in your args method, you needs to specify the arg name, like so:
public function args()
{
return [
'id' => [
'type' => Type::int(),
'name' => 'id'
],
];
}
Ref: https://github.com/Folkloreatelier/laravel-graphql#creating-a-query
Hope this help!
Regards
Reason is because of lower version, if you install laravel-graphql version with '~1.0.0',
go to modify config/graphql.php
// The name of the input that contain variables when you query the endpoint.
// Some library use "variables", you can change it here. "params" will stay
// the default for now but will be changed to "variables" in the next major
// release.
'variables_input_name' => 'variables', // modify here from 'params' to 'variables'

In CakePHP3 how do I create a custom model rule which validates that a time is after another time in the same table?

Columns (both datatype time):
Start
End
End cannot be before start.
$validator
->requirePresence('end', 'create')
->notEmpty('end')
->add('end', [
'time' => [
'rule' => 'time',
'message' => 'end can only accept times.'
],
'dependency' => [
'rule' => [$this, 'endBeforeStart'],
'message' => 'end can not be before start.'
],
]);
If it is a PUT request which only contains end, the model will need to query the existing record to compare against start. If it is a PUT which contains both then it need to validate against the intended new parameter.
How does cakePHP3 do this?
private function endBeforeStart($fieldValueToBeValidated, $dataRelatedToTheValidationProcess)
{
//What goes here?
}
I can't seem to find any examples of doing this online.
I'm not quite sure and haven't tested it, but maybe this gives you some hints:
$validator
->add('end', [
'endBeforeStart' => [
'rule' => function ($value, $context) {
// If it's a POST (new entry):
if ( $context['newRecord'] == '1' ) {
// Do your comparison here
// Input values are e.g. in $context['data']['starttime']
// If end is before start:
return false;
}
// If it's a PUT (update):
else {
// If starttime is not in $context['data']['starttime']
// check for the old value in $getOldEntry
$getOldEntry = $this->getOldEntry( $context['data']['id'] );
// And do your comparison here...
// If end is before start:
return false;
}
return true;
},
'message' => 'end can not be before start.' ],
])
public function getOldEntry($id = null) {
return $this->get($id);
}
I'm also not sure if the last function has to be private or public...

Mongoid: Query based on size of embedded document array

This is similar to this question here but I can't figure out how to convert it to Mongoid syntax:
MongoDB query based on count of embedded document
Let's say I have Customer: {_id: ..., orders: [...]}
I want to be able to find all Customers that have existing orders, i.e. orders.size > 0. I've tried queries like Customer.where(:orders.size.gt => 0) to no avail. Can it be done with an exists? operator?
I nicer way would be to use the native syntax of MongoDB rather than resort to rails like methods or JavaScript evaluation as pointed to in the accepted answer of the question you link to. Especially as evaluating a JavaScript condition will be much slower.
The logical extension of $exists for a an array with some length greater than zero is to use "dot notation" and test for the presence of the "zero index" or first element of the array:
Customer.collection.find({ "orders.0" => { "$exists" => true } })
That can seemingly be done with any index value where n-1 is equal to the value of the index for the "length" of the array you are testing for at minimum.
Worth noting that for a "zero length" array exclusion the $size operator is also a valid alternative, when used with $not to negate the match:
Customer.collection.find({ "orders" => { "$not" => { "$size" => 0 } } })
But this does not apply well to larger "size" tests, as you would need to specify all sizes to be excluded:
Customer.collection.find({
"$and" => [
{ "orders" => { "$not" => { "$size" => 4 } } },
{ "orders" => { "$not" => { "$size" => 3 } } },
{ "orders" => { "$not" => { "$size" => 2 } } },
{ "orders" => { "$not" => { "$size" => 1 } } },
{ "orders" => { "$not" => { "$size" => 0 } } }
]
})
So the other syntax is clearer:
Customer.collection.find({ "orders.4" => { "$exists" => true } })
Which means 5 or more members in a concise way.
Please also note that none of these conditions alone can just an index, so if you have another filtering point that can it is best to include that condition first.
Just adding my solution which might be helpful for someone:
scope :with_orders, -> { where(orders: {"$exists" => true}, :orders.not => {"$size" => 0}}) }

Elasticsearch sum total values for specific hours within a month

I have an elasticsearch server with fields: timestamp, user and bytes_down (among others)
I would like to total the bytes_down value for a user for a month BUT only where the hours are between 8am and 8pm
I'm able to get the daily totals with the date histogram with following query (I'm using the perl API here) but can't figure out a way of reducing this down to the hour range for each day
my $query = {
index => 'cm',
body => {
query => {
filtered => {
query => {
term => {user => $user}
},
filter => {
and => [
{
range => {
timestamp => {
gte => '2014-01-01',
lte => '2014-01-31'
}
}
},
{
bool => {
must => {
term => { zone => $zone }
}
}
}
]
}
}
},
facets => {
bytes_down => {
date_histogram => {
field => 'timestamp',
interval => 'day',
value_field => 'downstream'
}
}
},
size => 0
}
};
Thanks
Dale
I think you need to use script filter instead of range filter and then you need to put it in facet_filter section of your facet:
"facet_filter" => {
"script" => {
"script" => "doc['timestamp'].date.getHourOfDay() >= 8 &&
doc['timestamp'].date.getHourOfDay() < 20"
}
}
Add a bool must range filter for every hour, I'm not sure if you're looking to do this forever or for the specific day, but this slide show from Zachary Tong is a good way to understand what you could be doing, especially with filters in general.
https://speakerdeck.com/polyfractal/elasticsearch-query-optimization?slide=28

Resources