ElasticsearchRepository - Modify the #Query value - java

Currently I'm working with a ElasticsearchRepository and Springboot Data. I can write a simple method with a simple Query annotation:
#Query("{\"bool\":{\"must\":[{\"match\":{\"_resource\":\"?0\"}}]}}")
List<Activity> findByResource(String resource);
However, the Activity index might have different, multiple fields (varying from line to line in logstash). So, I might need to add multiple match clauses. I was testing adding a second parameter that would contain the rest of the params (if any) like this:
#Query("{\"bool\":{\"must\":[{\"match\":{\"_resource\":\"?0\"}}?1]}}")
List<Activity> findByResource(String resource, String additionalClauses);
the second parameter could look like this:
String additional = ",{\"match\":{\"_method\":\"GET\"}}
However, I get this exception when calling the endpoint with the service:
Elasticsearch exception [type=x_content_parse_exception, reason=[1:60] [bool] failed to parse field [must]]; nested exception is ElasticsearchStatusException[Elasticsearch exception [type=x_content_parse_exception, reason=[1:60] [bool] failed to parse field [must]]]; nested: ElasticsearchException[Elasticsearch exception [type=json_parse_exception, reason=Unexpected character ('\\' (code 92)): was expecting double-quote to start field name\n at [Source: (ByteArrayInputStream); line: 1, column: 62]]];
I understand that what I was trying to do isn't not the ideal way to use the #Query annotation (I don't think it's even allowed to do what I'm trying), so:
Is there a different way to accomplish this?
Is #Query que only option I have? How could I do it then?
Cheers.

Passing additional query components as parameter in this way is not possible. If a parameter is a String it will be quoted and escaped before being inserted into the value of the #Query annotation. This created String is then used to build a StringQuery.
You will need to create one of the Query variants (see https://docs.spring.io/spring-data/elasticsearch/docs/4.3.0/reference/html/#elasticsearch.operations.queries for details), probably a StringQuery and pass that to the ElasticsearchOperations.

Related

Sipios' Spring Boot JPA Search - Querying Enum

I am using Spring Boot (2.7.3) and Sipios' library for Spring Search for JPA Entities (https://github.com/sipios/spring-search).
I am trying make a search based on one of my class's enum fields, but receive the following error:
InvalidDataAccessApiUsageException: Parameter value [FIRST] did not match expected type [*.MY_ENUM (n/a)];
To make it clear through an example, let's say I have an entity-class "MyClass" which has a field "MY_ENUM enumField", and MY_ENUM has the values ('FIRST', 'SECOND', 'THIRD').
I am trying to query for all MyClass entries that have the value 'FIRST' in their enumField.
The enumField is annotated with #Enumerated(EnumType.STRING).
For those familiar with the specific library, my HTTP request is: localhost:8080/controller/search?search=enumField:FIRST.
I have also tried putting the FIRST query param in single & double quotes but the result was the same.
I have traced the problem to Hibernate's org.hibernate.query.spi.QueryParameterBindingValidator, method isValidBindValue().
Method isValidBindValue() checks whether value "FIRST" is of MY_ENUM type.
More specifically, the line that effectively causes the exception is expectedType.isInstance(value), where expectedType is "class MY_ENUM" and value is "FIRST", which evaluates to false and thus the error message - values checked via Intellij's debugger.
I have tried it with versions 0.2.3 & 0.2.4 of sipios/spring-search.
I saw that version 0.2.3 should support querying enums but I couldn't find any relative examples on how to do it, so it may be possible I have misunderstood how to use spring-search, but I would expect Spring Boot to recognise the "FIRST" string as an instance of MY_ENUM type.

Is there any way to use table name from #params in #query annotation in Hibernate? [duplicate]

This question already has answers here:
How to replace table name with parameter value while using Spring Data JPA nativeQuery
(5 answers)
Closed 3 years ago.
I am using Hibernate and Spring data JPA to build a web project.
In Eclipse-Link we can use native query like
String tableName = "sometablename";
String query = "SELECT * FROM " +tableName +"WHERE id > 10";
In Hibernate I am using #Query annotation
#Query(value = "SELECT COUNT(r.id) as resultsCount FROM #{#resultTable} r WHERE r.customerId= :customerId AND r.is_deleted=0 AND r.is_ignored=0 ", nativeQuery = true)
Integer getResultsCount(#Param("customerId") int customerId,
#Param("resultTable") String resultTable);
I tried #{#resultTable} but this is getting replaced as a string with quote and i am getting an exception that
You have a error in your SQL Syntax
I want to use table name dynamically from params. Is it possible? And if yes, Please tell me how?
It's not possible, #org.springframework.data.jpa.repository.Query takes only jpql, you cannot pass the name of the table since it's not recognized as any entity.
It states the javadoc of Query:
/**
* Defines the JPA query to be executed when the annotated method is called.
*/
String value() default "";
The best solution would be not to pass tablename as string, but resolve it using for example inheritance (link) or rebuild your datamodel somehow. As a quick and dirty solution I would suggest creating a custom repository and using EntityManager.createNativeQuery and pass the sql there. But remember to validate the query you're composing (validate user input, use enums for table names), because it can lead to sql injection.
Your own observations actually answer your question:
I tried #{#resultTable} but this is getting replaced as a string with quote and i am getting an exception
The placeholders which are used inside the #Query query string are intended to filled with literal values. Hence, the table name ended up appearing as literal string, inside single quotes. This means that behind the scenes #Query and Spring are probably using a JDBC prepared statement. It is not possible to bind the name of the table. Allowing this would be a major security hole.
The only possible workaround here would be to concatenate your query string together, and then trying to use that string with #Query. But note that this would not be a safe option.
What you are doing is wrong, you are mixing business logic into DAO layer, i suggest you create two DAO's, each one with its own table and query, then into the business/service layer call the desired one.

Spring-Data elastic search repo count with custom #Query annotation not working "Expected 1 but found 30 results"

For my application I have to perform a custom count on elastic, I want to use the #Query annotation for this in the ElasticsearchCrudRepository we use. When I use the following signature:
#Query("CUSTOM BOOL QUERY HERE")
long countItemsCustom();
This leads to a java.lang.IllegalArgumentException: Expected 1 but found 30 results Exception since it is executed as an query instead of an count. For spring-data-cassandra we have a special #CountQuery annotation to solve this issue. Is there a similar solution in spring-data-elasticsearch ?
I could use the elastic client or template to perform a custom query and get the results, but I prefer using the existing repository interface for this if possible.
No, that's not possible at the moment. The ElasticsearchStringQuery class checks the return type of the method that is annotated with the #Query annotation and then executes the ElasticsearchOperations.queryFor...() method that is appropriate for the method's return type. These are queries for data and not for the count.
I created an issue in the Spring Data Elasticsearch Jira to have this feature added.

Mybatis insert fail when add more parameter

I'm using Mybatis 3.2, Orace 12c for my project. I used code generator to generate insert() method. In the <insert> tag it has schema name; for ex:
insert into CPORTAL.CARD_USER_MASTER
Now, the schema is dynamic, so I put the parameter to change the schema:
insert into ${schema}.CARD_USER_MASTER
Parameter schema is defined in mapper.java as
insert(CardUserMaster record, #Param("schema") String schema)
However, without that schema, insert works but when has schema, error happen:
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'carduserSeqno' not found. Available parameters are [0, schema, param1, param2]
In other class I set schema parameter like that on insert method and it works. Don't know what happen to this class.
Any comments will be appreciated.
Thanks.
Change it to
insert(#Param("entity") CardUserMaster record, #Param("schema") String schema)
and use the params as entity.carduserSeqno in the SQL.
Looks like it cannot recognize that 0 parameter is POJO. Without the #Param("schema") it uses pojo fields directly as params btu with adding the schema param it cannot.

java.sql.SQLException: ORA-00932: inconsistent datatypes: expected NUMBER got BINARY

I have a method in Dao Class that returns List<Object[]> back and I am using named Query
public List<Object[]> getListByCustomer(Session session, int customerId, List<Integer> strIds) {
Query namedQuery = session.createSQLQuery(QueryConstants.EXPORT);
namedQuery.setParameter("customer", customerId);
namedQuery.setParameter("stringId", strIds);
List<Object[]> objects = namedQuery.list();
return objects;
}
I want to pass List<Integer> strIds in stringId into the named query as follows :
public class QueryConstants {
public static final String EXPORT =
"SELECT sv.NAME, sv.TYPE, sv.CLIENT_ADDRESS, sv.NAME_REDUNDANT, sv.DEPARTURE_DATE, s1.CODE,sv.STATE, sv.CODE "
+ "FROM VIEW sv, PROCESS p1, SET s1 "
+ "WHERE sv.R_ID = p1.R_ID and p1.ISSUER_ID = s1.USER_ID and sv.CUSTOMER_ID = :customer and sv.R_ID IN (:stringId)";
}
But I get ORA-00932: inconsistent datatypes: expected NUMBER got BINARY.
Also when I remove sv.R_ID IN (:stringId) from the query it works fine and
when I pass Integer(strIds) instead of List<Integer> strIds into the query it works fine.
I'm using Oracle 10g.
This is a very misleading error, and may root from different causes, for me I was setting a parameter that it was supposedly a number but at runtime it was setting null, hence it was binary. On a separate occasion got this error due to bean creation error in spring and was not setting the parameter correctly as well.
I think you just need to use
IN :stringId
instead of
IN (:stringId)
For JPA
namedQuery.setParameter("stringId", strIds);
is correct, but for Hibernate you should use
namedQuery.setParameterList("stringId", strIds);
I encountered this same exception and found the below reason for that -
In my entity, a field was mapped to a custom object (Parent child relationship - #ManyToOne). Later, the relationship annotation was removed by developer but the datatype was not changed.
After removing the #ManyToOne annotation, the #Column annotation should have been used with appropriate data type (Integer).
Case your param is list. If list is empty then raise error, you must check that list not empty to avoid error.
Case your param is single value. Let use TO_NUMBER(:your_param) to avoid error.
It working on me.
In my case, I was using HQL (in the repository of spring data) with an Entity mapped with #Enumerated (ORDINAL). I was trying to use the the enum object directly in the where clausule. The solution was to use TO_NUMBER(:your_param) as mentioned by the member above.
I got the same error but for a different reason. In my case it was due to the order the parameters were supplied being different from the order defined in the query. I assumed (wrongly) that because the parameters were named the order didn't matter. Sadly, it seems like it does.

Categories