N1ql query to filter by two fields in couchbase database - spring-boot

I created a web service using springboot and reactive couchbase,in the repository interface i want to add two fields to the WHERE clause. What I did is below
#ViewIndexed(designDoc = "enrolment")
#N1qlPrimaryIndexed
public interface EnrolmentRepository extends ReactiveCouchbaseRepository<Enrolment,String> {
#Query("#{#n1ql.selectEntity} where #{#n1ql.filter} and course like $1 and role like $2 within #{#n1ql.bucket}")
Mono<Enrolment> findEnrolmentByCourseRole(String course, String role);
}
But when use the above code, I get response as ,
{
"timestamp": "2019-06-14T03:57:29.670+0000",
"path": "/enrolments",
"status": 500,
"error": "Internal Server Error",
"message": "Unable to execute n1ql query due to error:{"msg":"syntax error - at role","code":3000}"
}
My endpoint is,
http://localhost:8783/enrolments?course=testcourse&role=testrole

This worked fine after changed as below.
#Query("#{#n1ql.selectEntity} where #{#n1ql.filter} and enrolledCourse = $1 and enrolledRole = $2")
Mono<Enrolment> findEnrolmentByCourseRole(String enrolledCourse, String enrolledRole);
Seems the error was with the variable name "role"; changed everywhere to "enrolledRole".It works fine.

role is reserved keyword as described here https://docs.couchbase.com/server/current/n1ql/n1ql-language-reference/reservedwords.html
You can use back-ticks escape identifiers
Example :
SELECT *
FROM default
WHERE `role` = "xyz";

Related

How to handle where clause in GraphQL Schema

I am new to GraphQL and creating a API Server using Flask and GraphQL,
Facing some issues while handling the "where" clause in GraphQL Request.
The basic Request and Response is working fine . please find a short snippet of the Schema I have designed
type data {
edges:[data_edges]
}
type QueryCustom {
data: data
}
type Query {
query: QueryCustom
}
Below mentioned basic request (Without the where clause) is working fine with this schema
query {
query {
data {
edges { .... }
But Getting an error when I am executing the Request with the where clause
query dataClosingSoon($month: Long) {
query{
data(where: { LastModifiedDate: { CALENDAR_MONTH: { value: { eq: $month } } } } ) {
edges { ....... }
Following is the response I get:
{
"errors": [
{
"locations": [
{
"column": 40,
"line": 1
}
],
"message": "Unknown type 'Long'."
},
{
"locations": [
{
"column": 9,
"line": 5
}
],
"message": "Unknown argument 'where' on field 'QueryCustom.data'."
}
]
}
I need to understand how to handle the where condition.
GraphQL is not SQL. You cannot use SQL clauses such as WHERE, LIKE, etc. in a GraphQL query.
You need to look at the schema to check how can you filter your query. These filters are pre-defined in the schema. You cannot create custom filters (at least in a basic sense) for a GraphQL query.
Edit:
If you want to use the query you are trying to send, your schema should look like something this:
type data {
edges:[data_edges]
}
type Query {
data(where: Filter!): data
}
input type Filter {
lastModifiedDate: // the type of this field
// Rest of the input fields
}
Note that your first query and the second query are totally different. Your second query is clearly wrong due to two reasons:
The Query type does not have a field called data. It only has one field called query. (I wouldn't add a field named query under the Query type though).
Your data field does not have any inputs. But your document (the GraphQL request) clearly does.

How to get details of high importance mails via Graph API

I am trying to get list of mails that are with high importance via Graph API.
For that, I am using below query:
https://graph.microsoft.com/v1.0/me/messages?$select=importance,subject
But that's not working out as it is giving all :(
I found that I have to add filter in the query and tried like below:
https://graph.microsoft.com/v1.0/me/messages?$select=importance,subject &
$filter=importance eq high
But I'm getting error like below:
{
"error": {
"code": "RequestBroker--ParseUri",
"message": "Could not find a property named 'high' on type 'Microsoft.OutlookServices.Message'.",
"innerError": {
"date": "2022-06-10T04:29:52",
"request-id": "",
"client-request-id": ""
}
}
}
I think I'm using query in a wrong way. Can anyone suggest me what's wrong?
Use single quotation mark '' for the specific name of the importance
https://graph.microsoft.com/v1.0/me/messages?$select=importance,subject&$filter=importance eq 'high'

GraphQL java: return a partial response and inform a user about it

I have a SpringBoot application that uses GraphQL to return data to a request.
What I have
One of my queries returns a list of responses based on a list of ids supplied. So my .graphqls file is a follows:
type Query {
texts(ids: [String]): [Response]
}
type Response {
id: String
text: String
}
and the following are request & response:
Request
texts(ids:["id 1","id 2"]){
id
text
}
Response
{
"data": [
{
"id": "id 1",
"text": "Text 1"
},
{
"id": "id 2",
"text": "Text 2"
}
]
}
At the moment, if id(s) is/are not in aws, then exception is thrown and the response is an error block saying that certain id(s) was/were not found. Unfortunately, the response for other ids that were found is not displayed - instead the data block returns a null. If I check wether data is present in the code via ssay if/else statment, then partial response can be returned but I will not know that it is a partial response.
What I want to happen
My application fetches the data from aws and occasionally some of it may not be present, meaning that for one of the supplied ids, there will be no data. Not a problem, I can do checks and simply never process this id. But I would like to inform a user if the response I returned is partial (and some info is missing due to absence of data).
See example of the output I want at the end.
What I tried
While learning about GraphQL, I have encountered an instrumentation - a great tool for logging. Since it goes through all stages of execution, I thought that I can try and change the response midway - the Instrumentation class has a lot of methods, so I tried to find the one that works. I tried to make beginExecution(InstrumentationExecutionParameters parameters) and instrumentExecutionResult(ExecutionResult executionResult, InstrumentationExecutionParameters parameters) to work but neither worked for me.
I think the below may work, but as comments suggests there are parts that I failed to figure out
#Override
public GraphQLSchema instrumentSchema(GraphQLSchema schema, InstrumentationExecutionParameters parameters) {
String id = ""; // how to extract an id from the passed query (without needing to disect parameters.getQuery();
log.info("The id is " + id);
if(s3Service.doesExist(id)) {
return super.instrumentSchema(schema, parameters);
}
schema.transform(); // How would I add extra field
return schema;
}
I also found this post that seem to offer more simpler solution. Unfortunately, the link provided by host does not exist and link provided by the person who answered a question is very brief. I wonder if anyone know how to use this annotation and maybe have an example I can look at?
Finally, I know there is DataFetcherResult which can construct partial response. The problem here is that some of my other apps use reactive programming, so while it will be great for Spring mvc apps, it will not be so great for spring flux apps (because as I understand it, DataFetcherResult waits for all the outputs and as such is a blocker). Happy to be corrected on this one.
Desired output
I would like my response to look like so, when some data that was requested is not found.
Either
{
"data": [
{
"id": "id 1",
"text": "Text 1"
},
{
"id": "id 2",
"text": "Text 2"
},
{
"id": "Non existant id",
"msg": "This id was not found"
}
]
}
or
{
"error": [
"errors": [
{
"message": "There was a problem getting data for this id(s): Bad id 1"
}
]
],
"data": [
{
"id": "id 1",
"text": "Text 1"
},
{
"id": "id 2",
"text": "Text 2"
}
]
}
So I figured out one way of achieving this, using instrumentation and extension block (as oppose to error block which is what I wanted to use initially). The big thanks goes to fellow Joe, who answered this question. Combine it with DataFetchingEnviroment (great video here) variable and I got the working solution.
My instrumentation class is as follows
public class CustomInstrum extends SimpleInstrumentation {
#Override
public CompletableFuture<ExecutionResult> instrumentExecutionResult(
ExecutionResult executionResult,
InstrumentationExecutionParameters parameters) {
if(parameters.getGraphQLContext().hasKey("Faulty ids")) {
Map<Object, Object> currentExt = executionResult.getExtensions();
Map<Object, Object> newExtensionMap = new LinkedHashMap<>();
newExtensionMap.putAll(currentExt == null ? Collections.emptyMap() : currentExt);
newExtensionMap.put("Warning:", "No data was found for the following ids: " + parameters.getGraphQLContext().get("Faulty ids").toString());
return CompletableFuture.completedFuture(
new ExecutionResultImpl(
executionResult.getData(),
executionResult.getErrors(),
newExtensionMap));
}
return CompletableFuture.completedFuture(
new ExecutionResultImpl(
executionResult.getData(),
executionResult.getErrors(),
executionResult.getExtensions()));
}
}
and my DataFetchingEnviroment is in my resolver:
public CompletableFuture<List<Article>> articles(List<String> ids, DataFetchingEnvironment env) {
List<CompletableFuture<Article>> res = new ArrayList<>();
// Below's list would contain the bad ids
List<String> faultyIds = new ArrayList<>();
for(String id : ids) {
log.info("Getting article for id {}",id);
if(s3Service.doesExist(id)) {
res.add(filterService.gettingArticle(id));
} else {
faultyIds.add(id);// if data doesn't exist then id will not be processed
}
}
// if we have any bad ids, then we add the list to the context for instrumentations to pick it up, right before returning a response
if(!faultyIds.isEmpty()) {
env.getGraphQlContext().put("Faulty ids", faultyIds);
}
return CompletableFuture.allOf(res.toArray(new CompletableFuture[0])).thenApply(item -> res.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList()));
}
You can obviously separate error related ids to different contexts but for my simple case, one will suffice. I however still interested in how can the same results be achieved via error block, so i will leave this question hanging for a bit before accepting this as a final answer.
My response looks as follows now:
{
"extensions": {
"Warning:": "No data was found for the following ids: [234]"
},
"data": { ... }
My only concern with this approach is security and "doing the right thing" - is this correct thing to do, adding something to the context and then using instrumentation to influence the response? Are there any potential security issues? If someone know anything about it and could share, it will help me greatly!
Update
After further testing it appears if exception is thrown it will still not work, so it only works if you know beforehand that something goes wrong and add appropriate exception handling. Cannot be used with try/catch block. So I am a half step back again.

Springdocs: Specifying an explicit type for Paged responses

I'm working on a "global search" for my application.
Currently, I'm using hibernate-search to search for instances of multiple different objects and return them to the user.
The relevant code looks as follows:
Search.session(entityManager)
.search(ModelA.classs, ModelB.class)
.where(...)
.sort(...)
.fetch(skip, count);
Skip and count are calculated based on a Pageable and the result is used to create an instance of Page, which will be returned to the controller.
This works as I'd expect, however, the types generated by swagger-docs obviously doesn't know, what the type within the Page is, and therefore uses Object.
I'd like to expose the correct types, as I use them to generate the types for the frontend application.
I was able to set the type to an array, when overwriting the schema like this:
#ArraySchema(schema = #Schema(anyOf = {ModelA.class, ModelB.class}))
public Page<?> search(Pageable pageable) {
However, this just disregards the Page and also isn't correct.
The next thing I tried is extending the PageImpl, overwriting the getContent method, and specifying the same schema on this method, but this wasn't included in the output at all.
Next was implementing Page<T> myself (and later removing the implements reference to Page<T>) and specifying the same schema on getContent, iterator, and the field itself, but also to no effect.
How do I tell spring-docs, what the content of the resulting Page might be?
I stumbled upon this when trying to solve a similar problem
Inspired from this thread Springdoc with a generic return type i came up with the following solution, and it seems to apply to your case also. Code examples are in Kotlin.
I introduced a stub class that will just act as the Schema for the response:
private class PageModel(
#Schema(oneOf = [ModelA::class, ModelB::class]))
content: List<Object>
): PageImpl<Object>(content)
Then i annotated my Controller like this:
#Operation(
responses = [
ApiResponse(
responseCode = "200",
content = [Content(schema = Schema(implementation = PageModel::class))]
)
]
)
fun getPage(pageable: Pageable): Page<Object>
This generated this api response:
"PageModel": {
"properties": {
"content": {
"items": {
"oneOf": [
{
"$ref": "#/components/schemas/ModelA"
},
{
"$ref": "#/components/schemas/ModelB"
}
],
"type": "object"
},
"type": "array"
},
... -> more page stuff from spring's PageImpl<>
And in the "responses" section for the api call:
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PageModel"
}
}
},
"description": "OK"
}
All generated openapi doc is similar to the autogenerated json when returning a Page, it just rewrites the "content" array property to have a specific type.

How can I define type in gql query in Apollo Client?

My Apollo Server have following definition.
input MinMax{
min:Float
max:Float
}
input ScreenerInput{
fy:Int!
quarter:Int!
ltp:MinMax
eps:MinMax
pe:MinMax
netWorth:MinMax
paidUp:MinMax
reserve:MinMax
netProfit:MinMax
}
How can I define MinMax type in Apollo client to send variables for the MinMax type. Till now I was doing scalar types only so my query were simple.
My query type is screeenedCompanies(criteria:ScreenerInput!):[ScreenedCompanies]
if I query directly like below it works.
{
screeenedCompanies(criteria:{
fy:2075
quarter:2
eps:{
min:30
max:40
}
}){
sector
symbol
}
}
What I want is to query like this so that variables can be changed
query getScreenedCompanies($criteria:<What type should be here ?>){
{
screeenedCompanies(criteria:$criteria){
sector
symbol
}
}
}
After giving ScreenerInput as type my query looks like below
query getScreenedCompanies($criteria:ScreenerInput!){
screeenedCompanies(criteria:$criteria){
sector
symbol
}
}
my variables in apolloplayground look like below
{
"criteria": {
"fy": 2075
"quarter": 4
"ltp": {
"min": 345
"max": 400
}
}
}
I am receiving the following error
"error": {
"errors": [
{
"message": "Variable \"$criteria\" of required type \"ScreenerInput!\" was not provided."
It works the same way, as any scalar type in GQL. Just define them with the same input name as on the server(like MinMax or ScreenerInput in your case). You don't need to describe its shape on the client, server will validate it for you :)
And don't forget to add exclamation mark !, if parameter is required.

Resources