Using #Query to access a data object? - spring

I have a Spring Elastic Repository function that has way too many parameters (11 parameters).
I want to be able to access members of class objects to reduce the parameter count. Is there a way to access class members via #Query? For example, I want to access members of the following class:
public class Header {
int version;
string timestamp;
}
and want to do a query as follows:
#Query("""
{
"bool":{
"must":[
{ "match": { "header_version": {"query": "<header version substitution>"}}},
{ "match": { "header_timestamp": {"query": "<header timestamp substitution>"}}},
{ "match": { "body": {"query": "<body value>"}}}
]
}
}
""")
List<Result> findEntry(Header header, String body);
Where:
public class Result {
int version;
String timestamp;
String body;
}

Related

Self Join scenario in spring data elastic seach

How to search self referencing documents in elastic search?
public class ProductDocument {
#Id
private String id;
private String title;
private List<String> tags;
//private List<ProductDocument> relatedProducts -- Doesn't work
private List<String> relatedProducts;
}
So while searching i want to perform operations like
{
"query": {
"multi_match" : {
"query": "cloths",
"fields": [ "title", "tags", "relatedProducts.title", "relatedProducts.tags" ]
}
}
}
Sample DataSet:
id title tags relatedProducts
1. Book null null
2. WM. cloths. 1
3. cloths. 2
Output:
id title tags relatedProducts
2. WM. cloths. 1
3. cloths. 2
How can this be achieved? I searched around and found nothing so far. any help is highly appreciated
Need to use a nested type
public class ProductDocument {
#Id
private String id;
private String title;
private List<String> tags;
// Use the nested datatype for the related products field
#Field(type = FieldType.Nested)
private List<ProductDocument> relatedProducts;
}
Then, when you perform a search, you can use the nested query to search for matches in the related products field. Here's an example of how you could do that:
{
"query": {
"nested": {
"path": "relatedProducts",
"query": {
"multi_match" : {
"query": "cloths",
"fields": ["title", "tags"]
}
}
}
}
}

Spring GraphQL mutation NullPointerException

I'm learning GraphQL in spring boot and I have created a mutation that saves used details in database. When I try to call the mutation from GraphiQL UI using following mutation query, it throws NullPointerException because the object I passed in mutation is not mapped with UserDto and I don't know why.
I have following code:
Controller
#Controller
public class UserController {
private UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
#QueryMapping(name = "userById")
public UserDto findUserById(#Argument Long id) {
return userService.findById(id);
}
#QueryMapping
public String removeUserById(#Argument Long id) {
return userService.removeById(id);
}
#MutationMapping(name = "save")
public UserDto save(#Argument UserDto user) {
return userService.save(user);
}
#QueryMapping(name = "users")
public List<UserDto> findAll() {
return userService.findAll();
}
}
GraphQL Schema
schema {
query: Query
mutation: Mutation
}
type Query{
users:[User]
userById(id:ID):User
}
type Mutation{
save(userInput:UserInput):User
}
#input types
input UserInput{
firstName:String!
lastName:String!
emailAddress:String!
ipAddress:String!
address:AddressInput!
}
input AddressInput{
addressLine1:String!
addressLine2:String!
addressLine3:String!
addressLine4:String!
addressLine5:String!
addressPostCode:Int!
}
#object types
type User{
firstName:String!
lastName:String!
emailAddress:String!
ipAddress:String!
address:Address!
}
type Address{
addressLine1:String!
addressLine2:String!
addressLine3:String!
addressLine4:String!
addressLine5:String!
addressPostCode:Int!
}
Mutation Query
mutation Save($userDto: UserInput!) {
save(userInput: $userDto) {
firstName
lastName
emailAddress
ipAddress
address {
addressLine1
addressLine2
addressLine3
addressLine4
addressLine5
addressPostCode
}
}
}
Variables
{
"userDto": {
"ipAddress": "192.168.0.124",
"firstName": "John",
"lastName": "Mark",
"emailAddress": "john#gmail.com",
"address": {
"addressLine1": "251 WC",
"addressLine2": "UC MAIN",
"addressLine3": "PB121",
"addressLine4": "New York",
"addressLine5": "USA",
"addressPostCode": 457821
}
}
}
Results
{
"errors": [
{
"message": "INTERNAL_ERROR for b6287602-fc10-6ecf-2091-b57ceaeb9f0a",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"save"
],
"extensions": {
"classification": "INTERNAL_ERROR"
}
}
],
"data": {
"save": null
}
}
When I run the GraphQL query, it throws NullPointerException in console.
Console Error
java.lang.NullPointerException: Cannot invoke "com.graphql.sample.dto.UserDto.getEmailAddress()" because "userDto" is null
I'm answering my question because I have already solved it. I had made a small mistake in controller near #Argument UserDto user as below:
#MutationMapping(name = "save")
public UserDto save(#Argument(name = "userInput") UserDto user) {
return userService.save(user);
}
The name in #Argument should match the name of parameter in Mutation type as below:
type Mutation{
save(userInput:UserInput):User
}
type User{
firstName:String!
lastName:String!
emailAddress:String!
ipAddress:String!
address:Address!
}
Reason 1:
Your save mutation result is returned to this entity. But it can be possible that a property that you are receiving might be null that's why this error is thrown, as you are using ! with each property. You can remove ! if a value can/is null
Reason 2:
I couldn't see your resolver so it might be possible that the result is not an object or the property names that you are receiving don't match with the property names inside type User. It should be same or use resolver to provide their values

Using Mustache API to parse Elasticsearch JSON Template requests

I have been using the SearchTemplateRequest class to execute my requests which uses Mustache templating to parse my template string with the passed parameters.
Elasticsearch Template - Converting Parameters to JSON
However, I have to change my implementation where I will be switching to the Java Low-Level Client. I want to use the Mustache implementation that SearchTemplateRequest uses internally to parse the template.
I'm okay to use the Mustache dependency or use the Elasticsearch implementation of it. Could someone help me out here?
My Template String:
{
"query": {
"bool": {
"filter": "{{#toJson}}clauses{{/toJson}}"
}
}
}
My Params Object:
{
"clauses": [
{
"term": {
"field1": "field1Value"
}
}
]
}
My test code:
StringWriter writer = new StringWriter();
MustacheFactory mustacheFactory = new DefaultMustacheFactory();
mustacheFactory.compile(new StringReader(requestTemplate), "templateName").execute(writer, params);
writer.flush();
The above code returns me the request template string with empty strings replacing the template.
Returned Response:
{
"query": {
"bool": {
"filter": ""
}
}
}
Expected Response:
{
"query": {
"bool": {
"filter": [
{
"term": {
"field1": "field1Value"
}
}
]
}
}
}
I finally figured out the solution.
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptEngine;
import org.elasticsearch.script.TemplateScript;
import org.elasticsearch.script.mustache.MustacheScriptEngine;
import java.util.Map;
import static java.util.Collections.singletonMap;
public class CustomMustacheScriptEngine {
private final String JSON_MIME_TYPE_WITH_CHARSET = "application/json; charset=UTF-8";
private final String JSON_MIME_TYPE = "application/json";
private final String PLAIN_TEXT_MIME_TYPE = "text/plain";
private final String X_WWW_FORM_URLENCODED_MIME_TYPE = "application/x-www-form-urlencoded";
private final String DEFAULT_MIME_TYPE = JSON_MIME_TYPE;
private final Map<String, String> params = singletonMap(Script.CONTENT_TYPE_OPTION, JSON_MIME_TYPE_WITH_CHARSET);
public String compile(String jsonScript, final Map<String, Object> scriptParams) {
jsonScript = jsonScript.replaceAll("\"\\{\\{#toJson}}", "{{#toJson}}").replaceAll("\\{\\{/toJson}}\"", "{{/toJson}}");
final ScriptEngine engine = new MustacheScriptEngine();
TemplateScript.Factory compiled = engine.compile("ScriptTemplate", jsonScript, TemplateScript.CONTEXT, params);
TemplateScript executable = compiled.newInstance(scriptParams);
String renderedJsonScript = executable.execute();
return renderedJsonScript;
}
}

How to parse belowJSON in springboot

How to parse the response JSON and get summary and action each time and form a separate object with that.
{
"issues": [
{
"fields":
{
"summary": "This is summary",
"action": "Start"
}
}, {
"fields":
{
"summary": "Second summary",
"action": "Stop"
}
}
]
}
You can create a Issues class and Fields class. Below is a snippet for reference,
public class Issues {
private List<Fields> fields;
// Getters and Setters
}
public class Fields {
private String summary;
private String action;
// Getters and Setters
}
You can map the response to the Issues object and go ahead iterating the fields List to fetch the summary and action out of it.

How to query nested objects from MongoDB using Spring Boot (REST) and TextQuery?

I am writing RESTful API to search MongoDB collection named 'global' by criteria using TextQuery. My problem is that I cannot access nested object fields when doing query.
For example this works:
GET localhost:8080/search?criteria=name:'name'
And this does not:
GET localhost:8080/search?criteria=other.othername:'Other Name'
I have MongoDB json structure (imported from JSON into 'global' collection as whole nested objects)
[{
"name": "Name",
"desc": "Desc",
"other" {
"othername": "Other Name",
}
},
{
"name": "Name",
"desc": "Desc",
"other" {
"othername": "Other Name",
}
}
]
And classes (with getters & setters & etc):
#Document(collection="global")
public class Global{
#TextIndexed
String name;
#TextIndexed
String desc;
Other other;
...
}
public class Other{
String othername;
...
}
My controller has method
#GetMapping("/search")
public Iterable<Global> getByCriteria(#RequestParam("criteria") String criteria) {
...
}
And I am trying to write text search with
public Iterable<Global> findByCriteria(String criteria) {
TextCriteria criteria = TextCriteria.forDefaultLanguage().matching(criteria);
TextQuery query = TextQuery.queryText(criteria);
return mongoTemplate.find(query, Global.class);
}
You need to add #TextIndexed to your other field.
public class Global{
#TextIndexed
String name;
#TextIndexed
String desc;
#TextIndexed
Other other;
...
}
Note: All nested object fields are searchable
or you may add #TextIndexed for each nested object field:
public class Other {
#TextIndexed
String othername;
...
}

Resources