Elasticsearch get elements fulfilling two conditions - elasticsearch

I have in db elements with following structure:
{
"id": 324214,
"modDate": "2014-10-01",
"otherInfo": {
..
..
}
}
Let's suppose that I have list of pairs [id, modDate]:
Map<String, String> idAndModDate
which contains f.e (324214, "2014-10-01"), (3254757, "2015-10-04")..
Now, I would like to use Java Api Elasticsearch QueryBuilder to build Query which in result give me list of all "ids" which are present in system but for who modDate is different as given.
Suppose that I have in database elements with following id/date pairs:
id, date
1, 2015-01-01
2, 2014-03-02
3, 2000-01-22
4, 2020-09-01
Now, I want to create query for
Map with following data:
Map<String, String> idDataPairs =[
(1, 2015-01-01)
(2, 2014-03-03)
(3, 2000-01-22)
(7, 2020-09-01)]
now I want create function like
List<String> ids = search(Map<String, String>) {
QueryBuilder.(sth).(sth) <--- thats what I'm asking about
}
which will return ids: 1, 3 because those ids exist in DB and dates from query are equal to dates in db respectively.

This is what you are looking for, more or less.
//build the test data in the map
Map<String, String> idDataPairs = new HashMap<String, String>();
idDataPairs.put("1", "2015-01-01");
idDataPairs.put("2", "2014-03-03");
idDataPairs.put("3", "2000-01-22");
idDataPairs.put("4", "2020-09-01");
//construct the query
BoolQueryBuilder should = QueryBuilders.boolQuery();
for(String id : idDataPairs.keySet()){
BoolQueryBuilder bool = QueryBuilders.boolQuery();
bool.must(QueryBuilders.termQuery("id", id));
bool.must(QueryBuilders.termQuery("modDate", idDataPairs.get(id)));
should.should(bool);
}
should.minimumNumberShouldMatch(1);
What i am doing is this:
For each of the Pairs, i am constructing a BoleanQuery called bool. This boolean query has two must conditions, that both the id and the date MUST match the document.
After constructing one bool Boolean Query, I add it to a parent BooleanQuery as well. This time, i say that the inner bool query should match, but its not required to. The final line says that at least one of these queries should match, if we want the document to match.
This structure is easier to understand, because must functions like AND and should functions like OR, but another way to do this is to use a TermsQuery, where we construct several TermsQuerys, and then add them to another parent BooleanQuery using should.
So, for the data
id, date
1, 2015-01-01
2, 2014-03-02
3, 2000-01-22
4, 2020-09-01
the above code will return the documents with ids 1,2,3

Related

How to replace custom objects in list based on a condition using Java 8?

Having two lists of custom objects Entity(id, name) - mainEntityList, otherEntityList and I want to iterate through them to replace the items in mainEntityList with that of otherEntityList where the ids match.
Ex
mainEntityList = [{1, "abc"},{2, "xyz"}]
otherEntityList = [{2, "value"}]
Then after replacing I should have
mainEntityList = [{1, "abc"},{2, "value"}]
It is working with the conventional loop method but what would be the best solution using java stream? Thanks!
Create a map from your otherEntityList mapping each Id to the Entity object and then use List.replaceAll cheking if the map keyset contains the ids in your mainEntityList:
List<Entity> mainEntityList = new ArrayList<>();
mainEntityList.add(new Entity(1, "abc"));
mainEntityList.add(new Entity(2, "xyz"));
List<Entity> otherEntityList = new ArrayList<>();
otherEntityList.add(new Entity(2, "value"));
Map<Integer,Entity> map = otherEntityList.stream().collect(Collectors.toMap(Entity::getId,Function.identity()));
mainEntityList.replaceAll(entity -> map.keySet().contains(entity.getId()) ? map.get(entity.getId()): entity);
System.out.println(mainEntityList);

Usage of Criteria Builder with not in on joined field

I have a Hotel entity & HotelReservation entity.
I try to fetch available Hotels to make a reservation.
Idea:
Fetch All Hotels and remove which are not available. (Hotel is not available if it's reserved in a certain time - in function, there are parameters from -> to). I followed the examples on stackoverflow.com and produce this code:
fun availableHotelsBetweenDates(from: ZonedDateTime, to: ZonedDateTime): Specification<Hotel> = Specification<Hotel> { root, query, cb ->
val reservationsBetweenDates: Subquery<HotelReservation> = query.subquery(HotelReservation::class.java)
val subqueryRoot = reservationsBetweenDates.from(HotelReservation::class.java)
val join: Join<Hotel, HotelReservation> = root.join("reservations", JoinType.LEFT)
reservationsBetweenDates.select(subqueryRoot.get("id"))
reservationsBetweenDates.where(activeReservationsBetweenDates(from, to).toPredicate(subqueryRoot, query, cb))
reservationsBetweenDates.distinct(true)
cb.not(join.get<Long>("id").`in`(reservationsBetweenDates))
}
This function returns 0 entities (should return 2 - because I have 3 hotels and only one reservation for one hotel).
Function activeReservationsBetweenDates(from, to) return a Specification<HotelReservation> of all HotelReservation between from, to.
How to properly use not in, with subquery on nested field?

JdbcTemplate. IncorrectResultSetColumnCountException: Incorrect column count

I use Spring JdbcTemplate to query list of several values:
List<String[]> tankNames = jdbcTemplate.queryForList(
"select name, country, level from tanklist", String[].class);
get the following error:
org.springframework.jdbc.IncorrectResultSetColumnCountException:
Incorrect column count: expected 1, actual 3
Why it expects 1 as I use String[]?
How I can get a list of values of several columns (maybe in an array of Strings) without creating an object of these 3 values?
My final goal to transform this response to a list of strings.
You can use following method instead to simplify your implementation
<T> List<T> query(String sql, RowMapper<T> rowMapper)
If you want to get list of String array (String[]) i.e. columns of each row are elements of a String array, use following
List<String[]> allTankNames = jdbcTemplate.query(
"select name, country, level from tanklist",
(rs, rowNum) -> new String[] {rs.getString(1), rs.getString(2), rs.getString(3)});
However, your code suggests you want to get one string per row having concatenated all columns, for that, use following
List<String> allTankNames = jdbcTemplate.query(
"select name, country, level from tanklist",
(rs, rowNum) -> String.format("%s %s %s", rs.getString(1),rs.getString(2), rs.getString(3)));
I did the following:
List<Map<String, Object>> allTankNames = jdbcTemplate.queryForList(
"select name, country, level from tanklist");
My goal was to transform this into a list of strings:
List<String> tankNamesWithInfo = allTankNames.stream().map(m -> (String) m.get("name") + m.get("country") + m.get("level")).collect(Collectors.toList());

How to sort a list of strings by using the order of the items in another list?

I want to sort a list of strings (with possibly duplicate entries) by using as ordering reference the order of the entries in another list. So, the following list is the list I want to sort
List<String> list = ['apple','pear','apple','x','x','orange','x','pear'];
And the list that specifies the order is
List<String> order = ['orange','apple','x','pear'];
And the output should be
List<String> result = ['orange','apple','apple','x','x','x','pear','pear'];
Is there a clean way of doing this?
I don't understand if I can use list's sort and compare with the following problem. I tried using map, iterable, intersection, etc.
There might be a more efficient way but at least you get the desired result:
main() {
List<String> list = ['apple','pear','apple','x','x','orange','x','pear'];
List<String> order = ['orange','apple','x','pear'];
list.sort((a, b) => order.indexOf(a).compareTo(order.indexOf(b)));
print(list);
}
Try it on DartPad
The closure passed to list.sort(...) is a custom comparer which instead of comparing the passed item, compares their position in order and returns the result.
Using a map for better lookup performance:
main() {
List<String> list = ['apple','pear','apple','x','x','orange','x','pear'];
List<String> orderList = ['orange','apple','x','pear'];
Map<String,int> order = new Map.fromIterable(
orderList, key: (key) => key, value: (key) => orderList.indexOf(key));
list.sort((a, b) => order[a].compareTo(order[b]));
print(list);
}
Try it on DartPad

LINQ return records where string[] values match Comma Delimited String Field

I am trying to select some records using LINQ for Entities (EF4 Code First).
I have a table called Monitoring with a field called AnimalType which has values such as
"Lion,Tiger,Goat"
"Snake,Lion,Horse"
"Rattlesnake"
"Mountain Lion"
I want to pass in some values in a string array (animalValues) and have the rows returned from the Monitorings table where one or more values in the field AnimalType match the one or more values from the animalValues. The following code ALMOST works as I wanted but I've discovered a major flaw with the approach I've taken.
public IQueryable<Monitoring> GetMonitoringList(string[] animalValues)
{
var result = from m in db.Monitorings
where animalValues.Any(c => m.AnimalType.Contains(c))
select m;
return result;
}
To explain the problem, if I pass in animalValues = { "Lion", "Tiger" } I find that three rows are selected due to the fact that the 4th record "Mountain Lion" contains the word "Lion" which it regards as a match.
This isn't what I wanted to happen. I need "Lion" to only match "Lion" and not "Mountain Lion".
Another example is if I pass in "Snake" I get rows which include "Rattlesnake". I'm hoping somebody has a better bit of LINQ code that will allow for matches that match the exact comma delimited value and not just a part of it as in "Snake" matching "Rattlesnake".
This is a kind of hack that will do the work:
public IQueryable<Monitoring> GetMonitoringList(string[] animalValues)
{
var values = animalValues.Select(x => "," + x + ",");
var result = from m in db.Monitorings
where values.Any(c => ("," + m.AnimalType + ",").Contains(c))
select m;
return result;
}
This way, you will have
",Lion,Tiger,Goat,"
",Snake,Lion,Horse,"
",Rattlesnake,"
",Mountain Lion,"
And check for ",Lion," and "Mountain Lion" won't match.
It's dirty, I know.
Because the data in your field is comma delimited you really need to break those entries up individually. Since SQL doesn't really support a way to split strings, the option that I've come up with is to execute two queries.
The first query uses the code you started with to at least get you in the ballpark and minimize the amount of data you're retrieving. It converts it to a List<> to actually execute the query and bring the results into memory which will allow access to more extension methods like Split().
The second query uses the subset of data in memory and joins it with your database table to then pull out the exact matches:
public IQueryable<Monitoring> GetMonitoringList(string[] animalValues)
{
// execute a query that is greedy in its matches, but at least
// it's still only a subset of data. The ToList()
// brings the data into memory, so to speak
var subsetData = (from m in db.Monitorings
where animalValues.Any(c => m.AnimalType.Contains(c))
select m).ToList();
// given that subset of data in the List<>, join it against the DB again
// and get the exact matches this time
var result = from data in subsetData
join m in db.Monitorings on data.ID equals m.ID
where data.AnimalType.Split(',').Intersect(animalValues).Any ()
select m;
return result;
}

Resources