GraphQL Conditional Queries - node.js

I'm a newbie in GraphQL and I was wondering if there is a easy way to query with "dynamic conditions".
For exemple, on GraphiQL I can query for :
query {
users{
name
age
}
}
And It will bring me a list of all users
{
"data": {
"users": [
{
"name": "Luis Coimbra",
"age": 15
},
{
"name": "Sebastião Campagnucci",
"age": 50
},
{
"name": "Giovana Ribeiro",
"age": 30
}
]
}
}
But is there an easy way for me to bring only, for example, users who are above 18 or any other age ?
An expected solution would be:
query {
users{
name
age > 18
}
}
Haven't found anything like that on documentation...

This is possible-it would have to be different. Your query wouldn't be a valid GQL query. Something like this would:
{
users(where: {age: { $gt: 18 }}){ #inspired by mongoDB query api
name
age
}
}
or maybe simpler:
{
users(where: {age: ">18"}}){
name
age
}
}
of course either way the resolver on the backend needs to expect this where argument on the users field and construct the DB query accordingly when it is passed. You would not find this in GraphQL docs because GraphQL itself doesn't care about that. It only showcases how to use features of GraphQL.
If you tried example projects like for example star was api, those don't have any filtering built in.

You should send your age filter as a parameter.You might try the following one:
In your graphql file
type users {
name: String,
age: Int,
...
}
usersQuery(ageLimit: Int): [users]
also you can send '>' , '<' , '=' as a parameter. Also it seems like that
usersQuery(ageLimit: Int, ageOperator: String): [users]
and you should configure your resolver where statement with these operators. hope it helps you.

Related

partial search by number in mongodb node js

Hi i have a model which contains OrderNumber:540. I have to add partial search in params like if i send 5 in params it should shows all the results where OrderNumber started with 5. i found the solution with $where but i am getting an error of error MongoError: $where not allowed in this atlas tier
i have an api like /getPurchaseOrdersBySupplierId/:id/:po if po is 5 then it must show all the results that is starting with 5...
OrderNumber: req.params.po})```
please suggest me the method for partial search by int. $where is not working in my case. need some other suggestion
You can use $toString to parse values to string and then search using $regex like this:
db.collection.aggregate([
{
"$addFields": {
"key": {
"$toString": "$key"
}
}
},
{
"$match": {
"key": {
"$regex": "^"+req.params.po
}
}
}
])
Example here

AWS AppSync RDS - Passthrough output resolver for json SQL result

Is it possible to create some sort of passthrough response resolver for an RDS datasource. The result of the query is 1 row and 1 column of type json which I would like to be the result of the graphql query.
Eg. schema could be
type Query {
getJsonFromDb(p: String): Res
}
type Res {
prop: String
}
schema {
query: Query
}
The request resolver could be
{
"version": "2018-05-29",
"statements": [
"select json_build_object('prop',:P)"
],
"variableMap": {
":P": $util.toJson($ctx.arguments.p)
}
}
In the log result logging looks like this
"result": "{\"sqlStatementResults\":[{\"columnMetadata\":[{\"arrayBaseColumnType\":0,\"isAutoIncrement\":false,\"isCaseSensitive\":true,\"isCurrency\":false,\"isSigned\":false,\"label\":\"json_build_object\",\"name\":\"json_build_object\",\"nullable\":0,\"precision\":2147483647,\"scale\":0,\"schemaName\":\"\",\"tableName\":\"\",\"type\":1111,\"typeName\":\"json\",\"signed\":false,\"autoIncrement\":false,\"caseSensitive\":true,\"currency\":false}],\"numberOfRecordsUpdated\":0,\"records\":[[{\"stringValue\":\"{\\\"prop\\\" : \\\"test\\\"}\"}]]}]}"
So it's there allright, but I cannot seem to find the correct velocity expressions to get it out.
All tips certainly appreciated!
Peter
So I found one way of getting this done by doing like in the response mapping
$util.parseJson($ctx.result).sqlStatementResults[0].records[0][0].stringValue

Node.js/MongoDB - querying dates

I'm having a bit of an issue understanding how to query dates; I think the issue might be with how my data is structured. Here is a sample document on my database.
{
"phone_num": 12553,
"facilities": [
"flat-screen",
"parking"
],
"surroundings": [
"ping-pong",
"pool"
],
"rooms": [
{
"room_name": "Standard Suite",
"capacity": 2,
"bed_num": 1,
"price": 50,
"floor": 1,
"reservations": [
{
"checkIn": {
"$date": "2019-01-10T23:23:50.000Z"
},
"checkOut": {
"$date": "2019-01-20T23:23:50.000Z"
}
}
]
}
]
}
I'm trying to query the dates to see check if a specific room is available at a certain date-range but no matter what I do I can't seem to get a proper result, either my query 404's or returns empty array.
I really tried everything, right now for simplicity I'm just trying to get the query to work with checkIn so I can figure out what I'm doing wrong. I tried 100 variants of the code below but I couldn't get it to work at all.
.find({"rooms": { "reservations": { "checkIn" : {"$gte": { "$date": "2019-01-09T00:00:00.000Z"}}}}})
Am I misunderstanding how the .find method works or is something wrong with how I'm storing my dates? (I keep seeing people mentioning ISODates but not too sure what that is or how to implement).
Thanks in advance.
I think the query you posted is not correct. For example, if you want to query for the rooms with the checkin times in a certain range then the query should be like this -
.find({"rooms.reservations.checkout":{$gte:new Date("2019-01-06T13:11:50+06:00"), $lt:new Date("2019-01-06T14:12:50+06:00")}})
Now you can do the same with the checkout time to get the proper filtering to find the rooms available within a date range.
A word of advice though, the way you've designed your collection is not sustainable in the long run. For example, the date query you're trying to run will give you the correct documents, but not the rooms inside each document that satisfy your date range. You'll have to do it yourself on the server side (assuming you're not using aggregation). This will block your server from handling other pending requests which is not desirable. I suggest you break the collection down and have rooms and reservations in separate collections for easier querying.
Recently I was working on date query. First of all we need to understand how we store date into the mongodb database. Say I have stored data using UTC time format like 2020-07-21T09:45:06.567Z.
and my json structure is
[
{
"dateOut": "2020-07-21T09:45:06.567Z",
"_id": "5f1416378210c50bddd093b9",
"customer": {
"isGold": true,
"_id": "5f0c1e0d1688c60b95360565",
"name": "pavel_1",
"phone": 123456789
},
"movie": {
"_id": "5f0e15412065a90fac22309a",
"title": "hello world",
"dailyRentalRate": 20
}
}
]
and I want to perform a query so that I can get all data only for this( 2020-07-21) date. So how can we perform that?. Now we need to understand the basic.
let result = await Rental.find({
dateOut: {
$lt:''+new Date('2020-07-22').toISOString(),
$gt:''+new Date('2020-07-21').toISOString()
}
})
We need to find 21 date's data so our query will be greater than 21 and less than 22 cause 2020-07-21T00:45:06.567Z , 2020-07-21T01:45:06.567Z .. ... .. this times are greater than 21 but less than 22.
var mydate1 = new Date();
var mydate1 = new Date().getTime();
ObjectId.getTimestamp()
Returns the timestamp portion of the ObjectId() as a Date.
Example
The following example calls the getTimestamp() method on an ObjectId():
ObjectId("507c7f79bcf86cd7994f6c0e").getTimestamp()
This will return the following output:
ISODate("2012-10-15T21:26:17Z")
If your using timestamps data to query.
EG : "createdAt" : "2021-07-12T16:06:34.949Z"
const start = req.params.id; //2021-07-12
const data = await Model.find({
"createdAt": {
'$gte': `${start}T00:00:00.000Z`,
'$lt': `${start}T23:59:59.999Z`
}
});
console.log(data);
it will show the data of particular date .i.,e in this case. "2021-07-12"

How to filter Subscribers based on array of tags in Loopabck

I've two models - subscribers and tags
Sample data:
{
subscribers: [
{
name: "User 1",
tags: ["a","b"]
},
{
name: "User 2",
tags: ["c","d"]
}
]
}
I want to filter subscribers based on their tags.
If I give a and b tags, User 1 should list
If I give a and c tags,
both User 1 and User 2 should list
Here is what I tried:
Method 1:
tags is a column in subscribers model with array data type
/subscribers/?filter={"where":{"tags":{"inq":["a","b"]}}} // doesn't work
Method 2:
Created a separate table tags and set subscribers has many tags.
/subscribers/?filter={"where":{"tags":{"inq":["a","b"]}}} // doesn't work
How can I achieve this in Loopback without writing custom methods?
I've Postgresql as the connector
UPDATE
As mentioned in the loopback docs you should use inq not In
The inq operator checks whether the value of the specified property matches any of the values provided in an array. The general syntax is:
{where: { property: { inq: [val1, val2, ...]}}}
From this:
/subscribers/?filter={"where":{"tags":{"In":["a","b"]}}}
To this:
/subscribers/?filter={"where":{"tags":{"inq":["a","b"]}}}
Finally found a hack, using Regex! it's not a performant solution, but it works!!
{ "where": { "tags": { "regexp": "a|b" } } }

How to return both error and data in a graphql resolver?

I was thinking about ways of implementing graphql response that would contain both an error and data.
Is it possible to do so without creating a type that would contain error?
e.g.
Mutation addMembersToTeam(membersIds: [ID!]! teamId: ID!): [Member] adds members to some team. Suppose this mutation is called with the following membersIds: [1, 2, 3].
Members with ids 1 and 2 are already in the team, so an error must be thrown that these members cannot be added, but member with an id 3 should be added as he is not in the team.
I was thinking about using formatResponse but seems that I can't get an error there.
Is it possible to solve this problem without adding error field to the return type?
Is it possible to solve this problem without adding error field to the return type?
Unfortunately, no.
A resolver can either return data, or return null and throw an error. It cannot do both. To clarify, it is possible to get a partial response and some errors. A simple example:
const typeDefs = `
type Query {
foo: Foo
}
type Foo {
a: String
b: String
}
`
const resolvers = {
Query: {
foo: () => {},
}
Foo: {
a: () => 'A',
b: () => new Error('Oops!'),
}
}
In this example, querying both fields on foo will result in the following response:
{
"data": {
"foo": {
"a": "A",
"b": null
}
},
"errors": [
{
"message": "Oops",
"locations": [
{
"line": 6,
"column": 5
}
],
"path": [
"foo",
"b"
]
}
]
}
In this way, it's possible to send back both data and errors. But you cannot do so for the same field, like in your question. There's a couple of ways around this. As you point out, you could return the errors as part of the response, which is usually how this is done. You could then use formatResponse, walk the resulting data, extract any errors and combine them with them with any other GraphQL errors. Not optimal, but it may get you the behavior you're looking for.
Another alternative is to modify the mutation so it takes a single memberId. You can then request a separate mutation for each id you're adding:
add1: addMemberToTeam(memberId: $memberId1 teamId: $teamId): {
id
}
add2: addMemberToTeam(memberId: $memberId2 teamId: $teamId): {
id
}
add3: addMemberToTeam(memberId: $memberId3 teamId: $teamId): {
id
}
This can be trickier to handle client-side, and is of course less efficient, but again might get you the expected behavior.
If you think about combining the GraphQL error - there is a way to do it in Apollo.
You need to set errorPolicy to all. That will help you notify users about the error and at the same time have as much data as possible.
none: This is the default policy to match how Apollo Client 1.0
worked. Any GraphQL Errors are treated the same as network errors and
any data is ignored from the response.
ignore: Ignore allows you to
read any data that is returned alongside GraphQL Errors, but doesn’t
save the errors or report them to your UI.
all: Using the all policy
is the best way to notify your users of potential issues while still
showing as much data as possible from your server. It saves both data
and errors into the Apollo Cache so your UI can use them.
But according to best practices, you shouldn't manipulate it in this way.
This is a great article about handling errors in GraphQL.
So, preferable way is to add "errors" field as part of your response and handle it in JS code.
We can achieve this by using a union. I would recommend visiting the great article Handling GraphQL errors like a champ
Example:
Mutation part: We can return the union type for the response & capture the result according to types.
type MemberType {
id: ID!
name: String!
}
enum ErrorType {
BAD_REQUEST_ERROR
FORBIDDEN_ERROR
INTERNAL_SERVER_ERROR
NOT_FOUND_ERROR
UNAUTHORIZED_ERROR
}
type GraphqlError {
type: ErrorType!
code: String!
message: String!
helpLink: URL
}
union UserRegisterResult = MemberType | GraphqlError;
addMembersToTeam(membersIds: [ID!]! teamId: ID!): UserRegisterResult!
Response:
addMembersToTeam(membersIds: [ID!]! teamId: ID!): {
...on MemberType{
id,
name,
}
...on GraphqlError{
id,
message,
statusCode,
}
}

Resources