Problems with Hibernate Criteria API (Disjunction and Conjunction) - java

I have a problem with using Conjunctions and Disjunctions. My program receives an arbitrary number of filter elements (each representing a Criterion) from the ui and is intended to chain together them as an AND or OR.
So for example I can have 3 elements like this (I represent a Criterion with a letter):
a OR b AND c
My code looks like this:
// ...
Criteria rootCriteria = createCriteria(entityClass);
Conjunction conjunction = Restrictions.conjunction();
Disjunction disjunction = Restrictions.disjunction();
boolean isFirst = true;
for (InternalFilterElement element : elements) {
if (isFirst) {
isFirst = false;
rootCriteria.add(createCriterion(element.getFilterRelation(), element.getValue()));
} else if (InternalFilterOperand.AND.equals(element.getInternalFilterOperand())) {
addCriterionToJunction(conjunction, element);
} else {
addCriterionToJunction(disjunction, element);
}
}
rootCriteria.add(disjunction);
rootCriteria.add(conjunction);
// ...
My problem is that I always get a AND b AND c and some unnecessary parentheses.
What I really wish to know is that I am using the wrong tool for this task or not? How can I mix AND and OR operators?
edit
InternalFilterOperand is basically an enum containing OR and AND
addCriterionToJunction just adds a Criterion to the Junction based on the relation (<, >, ...) and the value. It does not have any side effects.

By using the following code, you mix AND and OR operators in Hibernate:
Criteria rootCriteria = createCriteria(entityClass);
rootCriteria.add(Restrictions.or(
Restrictions.eq("a","a"),
Restrictions.and(
Restrictions.eq("b","b"),
Restrictions.eq("c","c")
)
));
This example results in the following output: a=a OR b=b AND c=c without the parenthesis you would get with conjunction and disjunction.

You're making it harder than necessary. Why not just use the following:
Junction junction =
InternalFilterOperand.AND.equals(element.getInternalFilterOperand()) ?
Restrictions.conjunction() :
Restrictions.disjunction();
for (InternalFilterElement element : elements) {
addCriterionToJunction(junction, element);
}
rootCriteria.add(junction);

I am close to believe that InternalFilterOperand is not set correctly in your elements as everything else looks right.
Please print/debug the element.getInternalFilterOperand() values in your loop as first statement to verify and correct.
EDIT: Try adding conjunction/disjunction directly in the rootCriteria
for (InternalFilterElement element : elements) {
if (isFirst) {
isFirst = false;
rootCriteria.add(
createCriterion(element.getFilterRelation(), element.getValue()));
}else if (InternalFilterOperand.AND.equals(element.getInternalFilterOperand())){
//add debugg/sys out: adding conjunction
System.out.println("adding conjunction");
rootCriteria.add(Restrictions.conjunction().add(element));
} else {
//add debugg/sys out: adding disjunction
System.out.println("adding disjunction");
rootCriteria.add(Restrictions.disjunction().add(element));
}
}

Related

Append Criteria query to previous query in Hibernate

I have a String array named andOrButtonFilter which stores and or filters selected by user.
Also two ArrayList named column and value storing column names and their values respectively.
I want the current query to append to the previous query and show the results
But my query is not being appended to the previous query, it is showing individual results.
For eg:
if name=xyz is first query and
age=26 is second query
It does not results with name=xyz and age=26 . It is only showing results for age=26 when executed for the second time.
Where am I going wrong?
This is the code I am using at the moment:
for (int i=0; i<andOrButtonFilter.length; i++)
{
if(andOrButtonFilter[i]=="and")
{
Conjunction conjunction =Restrictions.conjunction();
if ((column.get(i) != null) && (value.get(i)!=null))
{
conjunction.add(Restrictions.or(Restrictions.eq(column.get(i), value.get(i))));
criteriaQuery.add(conjunction);
}
}
else if(andOrButtonFilter[i]=="or")
{
Disjunction disjunction =Restrictions.disjunction();
if ((column.get(i) != null) && (value.get(i)!=null))
{
disjunction.add(Restrictions.or(Restrictions.eq(column.get(i), value.get(i))));
criteriaQuery.add(disjunction);
}
}
else
{
criteriaQuery.add(Restrictions.eq(column.get(i), value.get(i)));
}
}
I can find a few problems with you code.
1) You compare strings with == instead of equals. So your code always goes into the last section criteriaQuery.add(Restrictions.eq(column.get(i), value.get(i)));
2) In your conjunction/disjunction code you still use Restrictions.or which is kind of wrong. You don't even need Restrictions.or or Restrictions.and because Conjunction is adding the restrictions with AND anyway and Disjunction is adding with OR anyway.
3) On each iteration you add separate disjunction which is basically a single criterion and won't work as you expect.
I would try with something like:
Disjunction disjunction =Restrictions.disjunction();
Conjunction conjunction =Restrictions.conjunction();
for (int i=0; i<andOrButtonFilter.length; i++)
{
if("and".equals(andOrButtonFilter[i]))
{
if ((column.get(i) != null) && (value.get(i)!=null))
{
conjunction.add(Restrictions.eq(column.get(i), value.get(i)));
}
}
else if("or".equals(andOrButtonFilter[i]))
{
if ((column.get(i) != null) && (value.get(i)!=null))
{
disjunction.add(Restrictions.eq(column.get(i), value.get(i)));
}
}
else
{
criteriaQuery.add(Restrictions.eq(column.get(i), value.get(i)));
}
}
criteriaQuery.add(conjunction);
criteriaQuery.add(disjunction);
I am not saying that exact code will work because I haven't tested it ;) but you get the idea and you can debug from there.

How to check if Set B has at least one element that is not in Set A

So I have two sets: A and B. I need to check if set B contains anything that is not in the set A. There are maybe intersections, so I cannot just check if set A contains set B.
I can obviously do this:
for (String string : setA) {
if (!setB.contains(string) {
break;
}
}
or using the Guava library:
Sets.intersection(setA, setB).containsAll(setB); // returns false if there are elements outside.
But is there any way that would perform better or may be just cleaner or more elegant?
Thanks.
“B contains an element not in A” is the exact opposite of “A contains all elements of B”, therefore, the already existing method containsAll is sufficient to answer that question.
if(!setA.containsAll(setB)) {
System.out.println("setB contains an element not in setA");
}
You may shortcut using setB.size()>setA.size() || !setA.containsAll(setB), but this requires that the sets agree on the definition of equality, e.g. if one set is a SortedSet using String.CASE_INSENSITIVE_ORDER as comparator and the other is a HashSet, this won’t work (but the definition of the correct outcome is tricky with such combinations anyway).
If setB is really large, you might get a benefit from using a parallel stream like
if(!setB.parallelStream().allMatch(setA::contains)) {
System.out.println("setB contains an element not in setA");
}
but this is rather rare.
Merge all elements into another set and compare the total elements:
Set ab = new Set(a);
ab.addAll(b);
if (ab.size() != b.size()) break; // that means `a` had some element that was not in b
Another way to use streams (parallel) and functional mix
setB.parallelStream().filter(((Predicate<String>)setA::contains).negate()).findFirst();
same as
setB.parallelStream().filter(bi -> { return !setA.contains(bi);}).findFirst();
Straight Java
Duplicate the "target" set.
duplicateSet.removeAll(otherSet)
If duplicateSet is not empty, then the target contains one or more elements that are not in the "otherSet"
Apache SetUtils
xyz = SetUtils.difference(seta, setb);
if xyz.size() > 0 then seta contains one or more elements that are not in setb.
You can try algorithm with removing elements from setB:
if (setB.size() > setA.size()) {
return true;
}
for (String s : setA) {
//boolean contains = setB.contains(s);
boolean contains = setB.remove(s);
if (contains) return true;
}

How to compare a hashSet within a foreach loop iterating through another hashSet Java

I'm running a test to see if the values I've inputed into a file are the same values I generate from my API. So I have actual values which are generated from my API and I have another list of expected values. The problem I have is that I am not able to make apple to apple comparisons.
Example:
Actual = {red, bleu, yellow, purple}
expected = {bleu, red, purple, yellow}
failure: red != bleu, bleu != red, yellow != purple, purple != yellow
I'm not sure how else to better describe what I'm saying other than showing you my code.
Here is my code:
TreeSet<String> hashSet = (TreeSet<String>) calcGraph.getInputs();
boolean success = true;
String error="";
for(String xpath : hashSet) {
String actual = someApi(response, expression, xpath);
for ( String values : data.getDataOutputs().keySet() ) {
String expected = data.getDataOutputs().get(expectedXpath);
if ( !expected.equals(actual)) {
error+= "\nExpected : " + expected +"\nActual: " +actual+"\n";
success = false;
} if ( !success ) Assert.fail(error);
}
}
How can I compare these lists within 1 foreach loop or equivalent? Any help or assistance would be appreciated.
Edit:
Iterator<String> expectation = expectedList.iterator();
Iterator<String> actuation = actualList.iterator();
while((expectation.hasNext()) && (actuation.hasNext())) {
String exp = expectation.next();
String act = actuation.next();
logger.info("Expected: "+exp);
logger.info("Actual: "+act);
// Validation check
if ( !exp.equals(act)) {
error+= "\nExpected : " + exp +"\nActual: " +act+"\n";
success = false;
} if ( !success ) Assert.fail(error);
}
Order matters, so this will fail...
The question is just so weird. In the title you said HashSet vs HashSet comparison, and in the content you are using TreeSet.
From the question, it seems that you have a Set of actual results, and you want to compare against a Set of expected result, regardless of the iteration order, am I right?
Using contains is surely wrong, as well as using iterator to do comparison.
The solution is in fact straight-forward. From javadoc of Set:
boolean equals(Object o)
Compares the specified object with this set for equality. Returns true
if the specified object is also a set, the two sets have the same
size, and every member of the specified set is contained in this set
(or equivalently, every member of this set is contained in the
specified set). This definition ensures that the equals method works
properly across different implementations of the set interface.
What you need to do is simply
expectedResultSet.equals(actaulResultSet)
You can use
Set.contains(value)
to check if an actual value is in expected value, you only need one for loop to achieve this
See this
http://docs.oracle.com/javase/7/docs/api/java/util/Set.html#contains(java.lang.Object)

Creating a hibernate disjunction query programatically

I have a chunk of java code which hard codes a hibernate disjunction query that looks like this
session = HibernateUtils.beginTransaction("outpatient");
Criteria criteria = session.createCriteria(AugmentToken.class);
session.beginTransaction();
if (type == Constants.ICD9CPT) {
criteria.add(Restrictions.disjunction()
.add(Restrictions.eq("codeType", "d"))
.add(Restrictions.eq("codeType", "p"))
.add(Restrictions.eq("codeType", "c")));
} else if (type == Constants.EM) {
criteria.add(Restrictions.disjunction()
.add(Restrictions.eq("codeType", "eros"))
.add(Restrictions.eq("codeType", "ehpi"))
.add(Restrictions.eq("codeType", "epe")));
}
But this is not very elegant code. What I would like to do is pass an array of codetypes to a method, and dynamically construct the dijunction criteria. Every website I look at provides examples of disjunctive queries that look like the above, but this will not work for me because I don't want to hard code the construction of the restriction for the criteria since the number of code types can vary.
How do I do this?
Thank you,
Elliott
I think I figured this out. You create the disjunction as a variable, then sequentially add to it.
Specifically:
String [] codeTypes = new String[3];
codeTyes[0]="d";
codeTypes[1]="p";
codetypes[2]="c";
/* note the above would normally be passed into the method containing the code below */
Criteria criteria = session.createCriteria(AugmentToken.class);
session.beginTransaction();
Disjunction disjunction = Restrictions.disjunction();
for (int x = 0; x < codeTypes.length; x++ ) {
disjucntion.add(Restrictions.eq("codeType",codeTypes[x]);
}
criteria.add(disjunction);
I found the answer in Beginning Hibernate on page 214. The book is accessible from books.google.com.

lucene ignore queries on fields other than default

i have 2 indexes, one for meta data and one for text, i want to be able to remove all field searches in the query and only use the default fields that the user searched, ie "help AND title:carpool" i want only the help part, ideas?
Traverse over tree of BooleanQuery and remove entries related Term("help")
This is a ballpark of what your code should look like:
public static void removeNonDefault(BooleanQuery query, String defaultField) {
List<BooleanClause> clauses = (List<BooleanClause>)query.clauses();
Iterator<BooleanClause> iter = clauses.iterator();
while(iter.hasNext()) {
BooleanClause clause = iter.next();
Query subQuery = clause.getQuery();
if(subQuery instanceof BooleanQuery) {
removeNonDefault((BooleanQuery)subQuery, defaultField);
} else if(subQuery instanceof TermQuery) {
if (!((TermQuery) subQuery).getTerm().field().equals(defaultField)) {
iter.remove();
}
}
}
}
What this does is removes TermQuerys with the non-default field from the BooleanQuery, and recurses down into sub-boolean queries.
Note that this code is not complete. Depending on your situation, there might be more types of queries you should worry about, like phrase queries and constant score range queries.
Make sure to do query.rewrite() before you call this function, to convert any wildcard queries to boolean queries.

Categories

Resources