Syntax for msearch in new Elastic Java Api - java

I am migrating my existing code from the old Elasticsearch Java API to the Java API (so, replacing RestHighLevelClient with ElasticsearchClient).
The new API (I'm writing in Scala) basically works like:
val resp = client.msearch(searchRequest, Product.class)
val products: List[Product] = resp.hits().hits()
But the whole point of msearch is to submit many queries to the ES server in a single HTTP request, right? And those queries don't necessarily have the same return schema.
What is the correct way to write this so that you have some base class Product, and then some subclass Product1 that is the return type of query #1, and some other subclass Product2 that is the return type of query #2? Does the new API not support this? The docs do not give clear guidance, and neither do the javadoc.

You can use Object.class while fetching data from different indexes as below.
val resp = client.msearch(searchRequest, Object.class)

Related

How to convert SearchHits<T> return type to Page<T> return type in spring data elasticsearch 4.0

I have a spring boot application connecting to an ElasticSearch cluster and I'm performing the following operation on it:
#Autowired
private ElasticsearchOperations operations;
public Page<Metadata> search(Request request){
Query query = this.queryBuilder(request);
SearchHits<Metadata> hits = operations.search(query, Metadata.class);
\\ some code to convert SearchHits<Metadata> to Page<Metadata> type
}
Metadata is my custom entity and query builder is another function I've defined that returns an elasticsearch query type at runtime.
The issue is, I need to page this incoming data before returning it. I've looked through the entire official documentation for spring data elasticsearch but haven't found any examples for paging results. In fact, the only example of paging I could find anywhere on the internet was using a spring repository search method, which was something like this,
Page<Metadata> page = repository.search(query, PageRequest.of(0,1));
but that method is now deprecated in the 4.x version of the package.
The query I'm using is constructed dynamically by another function (the queryBuilder function) and it depends on incoming request parameters so defining methods on the repository interface is out of the questions as it would require me to define methods for every single combination of parameters and checking which one to use with if-else conditional blocks each time.
Is there any way to either return a Page <T> type from the ElasticSearchOperations interface methods (the official documentation claims a SearchPage <T> type as one of the types available for return values, but none of the suggested methods returns that type) or alternatively is there any way to convert SearchHits<T> to type Page<T>.
Any help is much appreciated.
I have done this in the following way:
SearchPage<YourClass> page = SearchHitSupport.searchPageFor(searchHits, query.getPageable());
return (Page)SearchHitSupport.unwrapSearchHits(page);
fortunately I found an answer to this question, thanks to this questions asked earlier
As mentioned over there, the query parameter passed to the search method has a setPageable method.
So, the code will now look somewhat like
public SearchPage<Metadata> search(Request request){
Query query = this.queryBuilder(request);
query.setPageable(PageRequest.of(PageNumber,PageSize));
SearchHits<Metadata> hits = operations.search(query, Metadata.class);
return SearchHitSupport.searchPageFor(hits, query.getPageable());
}
As suggested by a commenter, SearchPage and Page are both interfaces, so changing the return type of the method is not necessary

How to consume a Spring HAL/HATEOAS API in Java using purely Jackson, not Spring

We are trying to create a Java client for an API created with Spring Data.
Some endpoints return hal+json responses containing _embedded and _links attributes.
Our main problem at the moment is trying to wrap our heads around the following structure:
{
"_embedded": {
"plans": [
{
...
}
]
},
...
}
When you hit the plans endpoint you get a paginated response the content of which is within the _embedded object. So the logic is that you call plans and you get back a response containing an _embedded object that contains a plans attribute that holds an array of plan objects.
The content of the _embedded object can vary as well, and trying a solution using generics, like the example following, ended up returning us a List of LinkedHashMap Objects instead of the expected type.
class PaginatedResponse<T> {
#JsonProperty("_embedded")
Embedded<T> embedded;
....
}
class Embedded<T> {
#JsonAlias({"plans", "projects"})
List<T> content; // This instead of type T ends up deserialising as a List of LinkedHashMap objects
....
}
I am not sure if the above issue is relevant to this Jackson bug report dating from 2015.
The only solution we have so far is to either create a paginated response for each type of content, with explicitly defined types, or to include a List<type_here> for each type of object we expect to receive and make sure that we only read from the populated list and not the null ones.
So our main question to this quite spread out issue is, how one is supposed to navigate such an API without the use of Spring?
We do not consider using Spring in any form as an acceptable solution. At the same time, and I may be quite wrong here, but it looks like in the java world Spring is the only framework actively supporting/promoting HAL/HATEOAS?
I'm sorry if there are wrongly expressed concepts, assumptions and terminology in this question but we are trying to wrap our heads around the philosophy of such an implementation and how to deal with it from a Java point of view.
You can try consuming HATEOS API using super type tokens. A kind of generic way to handle all kind of hateos response.
For example
Below generic class to handle response
public class Resource<T> {
protected Resource() {
this.content = null;
}
public Resource(T content, Link... links) {
this(content, Arrays.asList(links));
}
}
Below code to read the response for various objects
ObjectMapper objectMapper = new ObjectMapper();
Resource<ObjectA> objectA = objectMapper.readValue(response, new TypeReference<Resource<ObjectA>>() {});
Resource<ObjectB> objectB = objectMapper.readValue(response, new TypeReference<Resource<ObjectB>>() {});
You can refer below
http://www.java-allandsundry.com/2012/12/json-deserialization-with-jackson-and.html
http://www.java-allandsundry.com/2014/01/consuming-spring-hateoas-rest-service.html

REST GET list as JSON

I have a list of ids which I need to pass as an argument to be displayed in JSON format. Specifically, i put the values in a List and would like my GET method to return them as:
[ 1, 5, 12,...]
I tried using GenericList, but I get a MessageBodyWriter error:
MessageBodyWriter not found for media type=application/json, type=class java.util.ArrayList, genericType=java.util.List<java.lang.Long>.
I'm using Jersey 2.16, and I've had no problems outputting lists of custom classes. The code is here:
#GET
#Path("{name}")
#Produces(MediaType.APPLICATION_JSON)
public Response getUsersByName(#PathParam("name") String name){
List<Long> userIds = userService.getUserssByName(name);
GenericEntity<List<Long>> list = new GenericEntity<List<Long>>(userIds){};
return Response.ok(list).build();
}
UserService queries a hashmap for all the users whose name matches a certain name and returns a list of their keys:
public List<Long> getUserssByName(String name){
List<Long> ids = new ArrayList<>();
for (Entry<Long, User> entry : users.entrySet()){
User user = entry.getValue();
if (user.getName().equals(name)){
ids.add(entry.getKey());
}
}
return ids;
}
How can I get that list to be displayed as I've stated above?
I think this explanation-answer will serve better than just "fixing" your code
By default, jersey provides a set of entity providers that can be used for given media types. For application/json, the following types are provided by default:
byte[]
String
InputStream
Reader
File
DataSource
StreamingOutput
To add support for serialization of other types to json is a bit of a rocky slope with the tools you are currently working with. I'll explain...
Jersey is just a framework for developing RESTful web services in java. There was a time when REST didn't exist (or wasn't seen as useful) and before that concept came about there were already many concrete implementations to facilitate Http data transfer. JAX-RS was one of them. This was during a time when javascript was likely in its infancy and XML largely ruled the world wide web. JAX-RS wasn't designed to natively prevent type erasure, and type erasure is precisely what we are trying to achieve. For me to go through the extremely long-winded solution of implementing the behaviors in MessageBodyWriter would be reinventing the wheel, and no one likes to reinvent the wheel! But, if you are interested in writing your own entities, or you are interested in learning what entities are and how to customize them, head over to https://jersey.java.net/documentation/latest/message-body-workers.html. Reading this document will outline what exactly is going on with your issues.
Solution
Choose a service. Learn it, and do not reinvent the wheel. There are many services out there that can achieve this. My favorite tool is Gson. It is a Java library developed by Google to convert Java objects into their JSON representation and vice versa.
Want to know how simple this is to fix your code?
#GET
#Path("{name}")
#Produces(MediaType.APPLICATION_JSON)
public Response getUsersByName(#PathParam("name") String name){
List<Long> userIds = userService.getUserssByName(namee);
Type listType = new TypeToken<List<Long>>() {}.getType();
Gson gson = new Gson();
String userIdList = gson.toJson(userIds, listType);
return Response.ok(userIdList).build();
}
Now, jersey supports application/json for String entity responses. Pretty much all of your responses will be represented as a String, type erasure is prevented, and you become a more happy developer.
Hope this helps!
You have to convert the list to array.
#GET
#Path("{name}")
#Produces(MediaType.APPLICATION_JSON)
public Response getUsersByName(#PathParam("name") String name){
List<Long> userIds = userService.getUserssByName(namee);
Long[] userIdArray = userIds.toArray();
return Response.ok(userIdArray).build();
}

search using two filters in spring-data-elasticsearch java api

I'm trying to use filters in spring-data-elasticsearch java api. I understand that I can use andFilter to search based on two fields like this
builder.withFilter(FilterBuilders.andFilter(FilterBuilders.rangeFilter("DATE").gte(startDate).lt(endDate), FilterBuilders.boolFilter().mustNot(FilterBuilders.termFilter("STATUS", "ACTIVE"))));
but in the code i have some if conditions and thus I cannot use andFilter directly to search ... take a look at this example
if("MSGSTAT".equals("SENT")) {
builder.withFilter(FilterBuilders.rangeFilter("DATE").gte(startDate).lt(endDate));
}
if("STATUS".equals("ACTIVE")) {
builder.withFilter(FilterBuilders.boolFilter().mustNot(FilterBuilders.termFilter("STATUS", "ACTIVE")));
}
When I use filters in the above mentioned way it doesn't applies the and keyword and the result received is incorrect. I also tried to do it this way but it didn't help either
builder.withFilter(FilterBuilders.andFilter(FilterBuilders.rangeFilter("DATE").gte(startDate).lt(endDate)));
builder.withFilter(FilterBuilders.andFilter(FilterBuilders.boolFilter().mustNot(FilterBuilders.termFilter("STATUS", "ACTIVE"))));
How should I do it making sure that the AND aggregation is always applied when using if conditions
Got an answer ...
I created a reference variable of type AndFilterBuilder and added all my filters to it using the add() and then built the Query Builder
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
AndFilterBuilder filters = null;
filters = new AndFilterBuilder(<your filter>);
filters.add(<your filter>);
builder.withFilter(filters);
builder.build()

Easily create instance of Java DTO object from Scala code

I am converting the server side of my GWT project use Scala instead of Java. I have a number of RPC servlets that do DB lookups then map results to ArrayList where a class like SomeDTO might be
override def listTrips(): util.ArrayList[TripRoleDTO] = {
val trd = new TripRoleDTO
trd.setRoleType(RoleType.TripAdmin)
trd.setTripName(sessionDataProvider.get().getSessionUser.getEmail)
val res: util.ArrayList[TripRoleDTO] = new util.ArrayList[TripRoleDTO]()
res.add(trd)
res
}
instead of
#Override
public ArrayList<TripRoleDTO> listTrips() {
final SessionData sessionData = sessionDataProvider.get();
final List<TripRole> tripsForUser = tripAdminProvider.get().listTripRolesForUser(sessionData.getSessionUser().getId());
return newArrayList(transform(tripsForUser, DTOConverter.convertTripRole));
}
Note that the Java implementation actually makes the DB call (something I'm figuring out in Scala still) but it does its DTO transformation via Google Guava's Iterables.transform method.
Since the DTO objects need to be .java files that the client side of GWT can use what is an elegant way to transform my Scala domain objects to DTOS?
Use the GWT RequestFactory for automating the creation of DTOs. The DTO can be defined simply with an interface and a #ProxyFor annotation, see an example in the link provided.
If using RequestFactory by some reason is not an alternative, then consider using Dozer to map domain objects to DTOs, this is frequently used with GWT.

Categories