Hibernate Search Facet on multiple fields - java

Let's assume a have an index with multiple fields created from #Indexed entities:
----------------------------------
| FieldA | FiedlB | FieldC | ... |
----------------------------------
| AX | BX | other | ... |
----------------------------------
| AZ | BZ | value | ... |
----------------------------------
Would it be possible to construct a facet aggregating values from two fields?
For example - A_&_B:
AX
AZ
BX
BZ
Any other options possible?

I think your best bet is to define a method aggregating the values of both fields and annotate this method to index it as a facet.
Something like:
#Field(bridge = #FieldBridge(impl = CollectionOfStringsFieldBridge.class), analyze = Analyze.NO)
#Facet
public List<String> getAggregatedField() {
return yourAggregatedValue;
}
With CollectionOfStringsFieldBridge being something like: https://github.com/hibernate/hibernate-search/blob/master/orm/src/test/java/org/hibernate/search/test/embedded/fieldoncollection/CollectionOfStringsFieldBridge.java
It's better than aggregating the facet values returned by Lucene manually.

Related

How to create a difference between two nested lists

I want to create a difference between two lists and their child items.
For the parent objects this is not a problem. But for the child lists I don't know how to get to the solution.
List/Object Structure:
Set<ParentObject>
|
|-Set<ChildObjects>
// example lists with diff
old list
|-MG1
| |-PL1
| |-PL2
|
|-MG2
|-PL1
|-PL2
|-PL3
new list
|-MG1
| |-PL1
| |-PL2
| |-PL3 <- new
|
|-MG2
| |-PL1
| |-PL2
| |-PL3
|
|-MG3 <- new
|-PL1
diff list
|-MG1
| |-PL3
|
|-MG3
|-PL1
Here is the diff between the two Parent lists.
// new minus old = diff
List<ParentObject> diff = newA.stream()
.filter(item -> !oldA.contains(item))
.collect(Collectors.toList());
works pretty well. but the code does not care about the child objects, which can also be different.

Stream Filter List based on Combination of values from another List

Need: To filter out data in list - 1 based on the values present in list - 2 with multiple criteria i.e. combination of Date & Order Number
Issue: Able to filter based on 1 criteria. But when I try adding another filter condition it treats it as 2 separate & not as combination. Unable to figure out how to make it as a combination.
Hope issue faced is clear.
Research: I referred to my earlier query on similar need - Link1 . Also checked - Link2
List 1: (All Orders)
[Date | OrderNumber | Time | Company | Rate ]
[2014-10-01 | 12345 | 10:00:01 | CompA | 1000]
[2015-03-01 | 23456 | 08:00:01 | CompA | 2200]
[2016-08-01 | 34567 | 09:00:01 | CompA | 3300]
[2017-09-01 | 12345 | 11:00:01 | CompA | 4400]
[2017-09-01 | 98765 | 12:00:01 | CompA | 7400]
List 2: (Completed Orders)
[Date | OrderNumber | Time]
[2014-10-01 | 12345 | 10:00:01]
[2015-03-01 | 23456 | 08:00:01]
[2016-08-01 | 34567 | 09:00:01]
[2017-09-01 | 98765 | 12:00:01]
Expected O/p after filter :
[Date | OrderNumber | Time | Company | Rate]
[2017-09-01 | 12345 | 11:00:01 | CompA | 4400]
Code:
// Data extracted from MySQL database
// List 1: All Orders
List<ModelAllOrders> listOrders = getDataFromDatabase.getTable1();
// List 2: Completed Orders
List<ModelCompletedOrders> listCompletedOrders = getDataFromDatabase.getTable2();
// Filter with 1 criteria works
Set<Integer> setOrderNumbers = listCompletedOrders.stream().map(ModelCompletedOrders::getOrderNumber).collect(Collectors.toSet());
listOrders = listOrders.stream().filter(p -> !setOrderNumbers.contains(p.getOrderNumber()).collect(Collectors.toList());
// Below not working as expected when trying to combinational filter
Set<LocalDate> setDates = listCompletedOrders.stream().map(ModelCompletedOrders::getDate).collect(Collectors.toSet());
listOrders = listOrders.stream().filter(p -> !setDates.contains(p.getDate()) && !setOrderNumbers.contains(p.getOrderNumber()))
.collect(Collectors.toList());
You've asked for logic that will do this:
The combination of Date & Order Number is unique. I need to check if that unique combination is present in List-2, if yes then filter out, if not then output should contain that row.
Stream::filter() will return a subset of the stream where the filter predicate returns true (i.e. it filters out those objects in the stream where the predicate is false).
listOrders = listOrders.stream().filter(p -> !setDates.contains(p.getDate()) && !setOrderNumbers.contains(p.getOrderNumber()))
.collect(Collectors.toList());
Your code expression here says "show me orders where the order's date does not appear in the list of prior orders AND where the order's order number does not appear in the list of prior orders". Your logical expression is wrong (you're getting confused between what in electronics would be called positive vs negative logic).
You want either:
listOrders = listOrders.stream().filter(p -> !(setDates.contains(p.getDate()) && setOrderNumbers.contains(p.getOrderNumber())))
.collect(Collectors.toList());
"show me orders where both the order's date and order's id are not
present in the list of prior orders"
or:
listOrders = listOrders.stream().filter(p -> !setDates.contains(p.getDate()) || !setOrderNumbers.contains(p.getOrderNumber()))
.collect(Collectors.toList());
"show me orders where either the order's date has not been seen before
OR the order's id has not been seen before"

Java - Gherkin & Cucumber: Pass an object or list of objects on a vertical table instead of horizontal

I have the following sample gherkin scenario on my feature file:
Scenario: Book an FX Trade
Given trades with the following details are created:
|buyCcy |sellCcy |amount |date |
|EUR |USD |12345.67 |23-11-2017 |
|GBP |EUR |67890.12 |24-11-2017 |
When the trades are executed
Then the trades are confirmed
In my glue file, I can map the data table to an object Trade as an out of the box cucumber solution:
#When("^trades with the following details are created:$")
public void trades_with_the_following_details_are_created(List<Trade> arg1) throws Throwable {
//do something with arg1
}
What I want to achieve:
Improve the readability of my gherkin scenario by doing the following:
Transpose the data table vertically, This will improve readability if my object has around 10 fields
Replace fields / column names with aliases
Sample:
Scenario: Book an FX Trade
Given trades with the following details are created:
|Buy Currency | EUR | GBP |
|Sell Currency | USD | EUR |
|Amount | 12345.67 | 67890.12 |
|Date | 23-11-2017 | 24-11-2017 |
When the trades are executed
Then the trades are confirmed
I want the table to be dynamic in a way that it can have more or less than 2 data sets / columns. What would be the best way to achieve this?
Additional info:
Language: Java 8
Cucumber version: 1.2.5
Trade POJO being something like:
public class Trade {
private String buyCcy;
private String sellCcy;
private String amount;
private String date;
/**
* These fields are growing and may have around 10 or more....
* private String tradeType;
* private String company;
*/
public Trade() {
}
/**
* accessors here....
*/
}
If the table is specified in your feature file as
|buyCcy | EUR | GBP |
|sellCcy | USD | EUR |
|amount | 12345.67 | 67890.12 |
|date | 23-11-2017 | 24-11-2017 |
you can use the following glue code (with your posted Trade class, assuming that there is a proper toString() method implemented)
#Given("^trades with the following details are created:$")
public void tradeWithTheFollowingDetailsAreCreated(DataTable dataTable) throws Exception {
// transpose - transposes the table from the feature file
// asList - creates a `List<Trade>`
List<Trade> list = dataTable.transpose().asList(Trade.class);
list.stream().forEach(System.out::println);
}
output
Trade{buyCcy=EUR, sellCcy=USD, amount=12345.67, date=23-11-2017}
Trade{buyCcy=GBP, sellCcy=EUR, amount=67890.12, date=24-11-2017}

How to pass Scenario out line data as a object in step method using cucumber-jvm

I am finding a solution to pass each scenario outline example row as object in cucuber-jvm.
So as for example if I consider a scenario
Scenario Outline: example
Given I have a url
When I choose <input_1>
Then page should hold field1 value as <validation field1> field2 value as <validation field2> fieldn value as <validation fieldn>
Examples:
| input_1 | validation field1 |validation field2|validation field n|
| input_1_case_1 | expected value 1 |expected value 1 |expected value n |
So in Step file
public void validationMethod(String validation field2,String validation field2,String validation field3){
............
............
}
So if I have more field then my method also consume more argument.
Now I want to pass all validation field as object in method. So is it possible using cucumber jvm? If possible could any one can please provide some suggestion with sample code.
You could try something like this
Then Use the following values
| <validation field1> | <validation field2> | <validation field3> |
Examples:
| input_1 | validation field1 |validation field2|validation field3 |
| input_1_case_1 | expected value 1 |expected value 2 |expected value 3 |
| input_2_case_2 | expected value 1 |expected value 2 |expected value 3 |
Step Definition
#Then("^Use the following values$")
public void useFollVal(List<String> valFields) {
//The values will be inside the list. Use index to access
}
You can even get an validation object instead of string list ie List<ValidationData>. To do this add a header in the step (not the examples table) with names matching the variables in the ValidationData class and cucumber will populate the data into the object.
Then Use the following values
| valField1 | valField2 | valField3 | <<<--- Header to add
| <validation field1> | <validation field2> | <validation field3> |
valField1 -> private String valField1; in ValidationData
Step Definition
#Then("^Use the following values$")
public void useFollVal(List<ValidationData> valObject) {
}
This is more of a comment: Wouldnt a variable length argument list work for you? You would need to know the sequence of your params though, without the argument names to help out.
public void multiParams(String... val){
}

What is the correct way to use db values in code

I've in a lot of places in my code hard coded comparing and I'm not happy with that. I'm looking for the correct way to approach this.
EXAMPLE
public class Status {
public static final int ACTIVE = 1,
INACTIVE = 2,
ENDED = 3,
PAUSED = 4,
NEW = 5,
INIT = 6,
STARTED = 7;
private int id;
private String name;
public int getId(){ return this.id; }
public String getName(){ return this.name; }
//get and set the object from the db by the id
public Status(int id){}
}
public class Job {
private int id;
private Status stage;
//get and set the object from the db by the id
Job(int id){}
public boolean isStageStatusEnded(){
return this.stage.getId() == Status.ENDED;
}
}
I've this DB table:
mysql> desc statuses;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(20) | NO | | NULL | |
+-------+-------------+------+-----+---------+----------------+
mysql> select * from statuses;
+----+----------+
| id | name |
+----+----------+
| 1 | ACTIVE |
| 2 | INACTIVE |
| 3 | ENDED |
| 4 | PAUSED |
| 5 | NEW |
| 6 | INIT |
| 7 | STARTED |
+----+----------+
As you can see the static final int in Status class is exact copy of the table statuses and that for the return this.stage.getId() == Status.ENDED; line. Now if in any time the values will change(id/name) i'll have to change the static int's as well. I dont see how can I change it but if you know a way - share it.
There is several ways. This can be one of them:
Drop the final keyword from your constants.
On start the application, query to the database for the current values.
Populate the values in the constants with Java reflection.
Field field = Status.class.getDeclaredField(name);
field.setInt(field, id);
You'll still have to keep these values hardcoded somewhere, either in the database or in the code cause otherwise you will simply not know what each value stands for. After that you can either persist them to the DB if that suits your needs or load into your app.
As Paul wrote, you'd load the values from DB when your application starts, but I suggest using a enum instead of a number of int constants which may save a lot of nerve.
Here is a link you may find useful and as for enums over constants, read J.Bloch, Effective Java.

Categories

Resources