How to apply a Hasura `where` filter only if a variable is not null? - hasura

I have a query like this:
query getUsers ($setId: Int) {
user(where: { user_sets: { set_id: { _in: [$setId] } } }) {
id
name
status
user_sets {
set {
name
}
}
# more fields...
}
}
What I'm looking for, is a way to not apply the where filter and give all entries if $setId is null. I'd like to avoid dynamically writing the query - it'd be easy to do something like this, but we want queries in static .graphql files:
const query = `
query getUsers (${ setId ? '$setId: Int' : ''}) {
user(${ setId ? 'where: { user_sets: { set_id: { _in: [$setId] } } }' : '' }) {
`
Some things I've tried:
Using GraphQL directives like #skip and #include, but it seems these only apply to fields returned, not to any part of a where filter
Using Hasura boolExps like _is_null and _or, but it seems these can't test variables directly, they can only compare variables to columns contents

This behaviour changed somewhere between v1.3.4. Therefore there are two correct answers.
You can read more about this change in the hasura repository.
Before Version 1.3.4
This answer describes it.
After Version 1.3.4
Using null in comparisons is dangerous when defaulting to true because an accidentally unset variable could result in a dropped table. The maintainers removed this behaviour but made it accessible by setting the variable HASURA_GRAPHQL_V1_BOOLEAN_NULL_COLLAPSE.
When comparing with {_eq: null}, Hasura will throw an error because it is assumed that this is a mistake.
If you want to compare to a value and evaluate to true when the value is null, you need to handle the case on the client side and pass the whole boolean expression to Hasura.
query getUsers ($userSetsWhere: user_sets_bool_exp) {
user(where: { user_sets: { $userSetsWhere } }) {
id
name
status
user_sets {
set {
name
}
}
# more fields...
}
}
const userSetsWhere = setId ? { set_id: { _eq: $setId } } : {};
What it does, is that only in case the value is not null or undefined a non-empty expression gets passed to Hasura.

You can use bool expressions as binding variables for such situations
query getUsers ($condition: user_bool_exp!) {
user (where: $condition) {
id
name
status
user_sets {
set {
name
}
}
# more fields...
}
}
And you can build conditions depending on your variables
{ condition: { user_sets: { set_id: { _in: [$setId] } } } }
or
{ condition: { user_sets: {} }

This answer only applies to Hasura v1.X, see other answers for more recent versions.
It seems like matching all if the variable is null is the default behaviour if the _eq boolExp is used. I wasn't seeing this because this query was using _in.
Changing to this gives all items if $setId is passed as null:
query getUsers ($setId: Int) {
user(where: { user_sets: { set_id: { _eq: $setId } } }) {
id
name
status
user_sets {
set {
name
}
}
# more fields...
}
}
This is because Hasura follows SQL by having null not comparable to anything (only _is_null can match values that are set as null in nullable columns).
Therefore, { _eq: null } logically can't match anything, so it's simply optimised away. This:
(where: { user_sets: { set_id: { _eq: $setId } } })
...becomes this:
(where: { user_sets: { set_id: {} } }
...and because {} is trueExp, treated as true, it optimises away to be effectively WHERE 'true'.

Related

Firebase database().ref() nonexistent path

In firebase, my app will be creating nested data all the time. My question is do I first have to check if a key exists before i can reference it, or can i just...
const ref = firebase.database().ref("dataTree/" + childVar1 + "/" + childVar2);
ref.push({data: "this is data"});
and it will create the nested structure for me? i.e. :
{
"dataTree": {
"notChildVar1": {
"someChildName": {
"data": "test"
}
}
"alsoNotChildVar1": {
"someChildName": {
"data": "test"
}
}
}
}
Should Become...
{
"dataTree": {
"notChildVar1": {
"someChildName": {
"data": "test"
}
}
"alsoNotChildVar1": {
"someChildName": {
"data": "test"
}
}
"childVar1": {
"childVar2": {
"data": "test"
}
}
}
}
And if the above code will return an error, what can i do to test if a key exists, add it if it does not exist, and then push something in that?
Maybe you can try something like create the full content of childVar1 and insert directly in your database including the childVar2: {data: test} (I supposed that you're using RealtimeDatabase).
const ref = firebase.database().ref("dataTree/" + childVar1);
ref.push(
"childVar2": {
"data": "this is data"
}
);
You can write to any arbitrary location in the database, and any required nodes that do not exist will be created for you automatically. You don't have to create each child node individually.
Similarly, if all children of a node are deleted, the node itself will be deleted automatically (there is no such thing as an "empty" child node).

redux: how to deal with an API that returns null for some things?

If I am interacting with an API that returns null for some objects that may or may not have value, how can I reconcile that with the reducers?
example: app state
{
foo: {
foo1: null,
foo2: {
bar: null,
bar2: null
}
}
}
but the server, when things are null, returns this:
{
foo: null
}
but it can return the full state when things have value:
{
foo: {
foo1: "somefoo,
foo2: {
bar: "barvalue,
bar2: 27
}
}
}
the problem I ham having is that my reducers are trying to load the state from the return value from the server, and then my components are trying to read from a null object and it is failing.
EDIT: the reducer and the component would look like this... so the component is trying to read some nested json, which may come back as unnreadable because the parent object is null. In this case I know I could hack up a solution that checks if the object is null and inserts my predefined initial state...
BUT...my actual example is a bigger json object than this and I know it will change in the future, so I need a solution that is not so fragile and cumbersome as adding a ton of logic here to check to make sure that every object down the nested like is not null.
var updateSettings = (settings = jsonShape, action) => {
swtich (action.type) {
case UPDATE_SETTINGS:
return Object.assign({}), settings, {
foo2: {
...settings.foo2,
bar: action.newBar
}
}
}
}
const Component = ({ settings }) => {
return (
<div>{ settings.foo2.bar }</div>
)
}

When querying for a single item in graphql, meteor, and apollo i get null values returned

I have probably overlooked something in the docs, but I have seem to run into a problem with being able to get a single object from my graphql queries.
Here is the schema:
type Query {
product(name: String!): Product
}
type Product {
_id: String
name: String
}
Here is the resolver:
Query: {
product (_, args) {
return Products.find({where: args})
},
products () {
return Products.find().fetch()
}
}
Here is the Query:
query {
product(name: "burgers") {
name
}
}
I get a result of this:
{
"data": {
"product": {
"name": null
}
}
}
Am I just forgetting to add something to this, and if so could you point me the right direction.
If Products is a Meteor Collection, then .find returns a cursor, so the right thing to return would be Products.findOne({name: args.name})
http://docs.meteor.com/api/collections.html#Mongo-Collection-findOne

Use firebase timestamp within a string

Given I have the following data structure:
{
operation: {
abc123: {
action: "sendEmail",
status: "pending:167423383473"
}
}
}
Is it possible to insert an operation with a status of the following form?
"pending:Firebase.ServerValue.TIMESTAMP"
Edit: The reason they need to be in the same column is that I need to sort the operations by status and timestamp. With firebase this is only possible if they're combined into a single property.
I'm fairly certain this is not possible, since Firebase.ServerValue.TIMESTAMP is actually a placeholder object, {.sv: "timestamp"}, until it reaches the server. Concatenating it with a string would not have the desired effect. I would suggest the following solution:
{
operation: {
abc123: {
action: "sendEmail",
status: {
type: "pending",
timestamp: 167423383473
}
}
}
}
Or possibly:
{
operation: {
abc123: {
action: "sendEmail",
statustype: "pending",
statustimestamp: 167423383473
}
}
}

In Collection.find, how to format .limit, .sort, fieldlist, and variable column names

In non Meteor Server-Side calls to mongodb it is possible make the following chained-option call to the database
collection.find( { myField: { $gte: myOffset } ).limit( myLimit ).sort( { mySortField : 1 } );
where myField, myOffset, myLimit and mySortField may be resolved from elsewhere at run-time.
This pattern is very useful to create such a run-time generated generic query.
Meteor seems to insist on the non-chained options pattern of
collection.find( { { myField: { $gte: myOffset } }, { limit: myLimit, sort: { mySortField : 1 }} );
and I am having problems 'building up' a working Find Query as required above from js objects as described
in previous questions 17362401 and 10959729
Would anyone like to help?
Edited to show usage of variable:
I do it this way. You send two hashes, where the first is the where clause, and all else are peer level keys.
var locations;
var myfield = 'gps';
search = {
sureties: {
$in: sureties
}
}
search[myfield] = {
$near: this.gps,
$maxDistance: kilometers
};
locations = Agents.find(search, {
fields: {
name: 1,
phone: 1
},
limit: limit,
sort: { field1 : 1 }
}).fetch();
The chained pattern is not possible in Meteor, neither server side nor on the client. But the params pattern is as universal, you should be able to create any query you need with those params.

Resources