13 Replies Latest reply on Sep 22, 2014 11:46 AM by deveshmishra

    CompoundCriteria.getCriteria().removeAll(...) leaving empty criteria ()

    deveshmishra

      I am trying to remove criteria from the query, while trying to remove it is adding an empty criteria () thus failing the query.

       

       

      Query:SELECT * FROM T, B WHERE (B.B = 10) AND ((T.A < 10) AND (T.B = 10))

      Criteria to remove: ((T.A < 10) AND (T.B = 10))

       

       

      Expected output:SELECT * FROM T, B WHERE B.B = 10

      Actual output:SELECT * FROM T, B WHERE (B.B = 10) AND ()

       

       

      The following code is used to remove:

       

       

      private void cleanUpOriginalCriteria(final Query output, final Collection<Criteria> criteriaToPullUp)

          {

              PostOrderNavigator.doVisit(output, new LanguageVisitor()

              {

                  @Override

                  public void visit(final CompoundCriteria obj)

                  {

                      obj.getCriteria().removeAll(criteriaToPullUp);

                  }

              });

          }

       

       

      I am attaching a failing test case for this. Please help. Thanks

        • 1. Re: CompoundCriteria.getCriteria().removeAll(...) leaving empty criteria ()
          shawkins

          You are directly manipulating the list under a CompoundCriteria, which is not expected to have less than 2 entries.  You can instead use something like Criteria.combineCriteria on the manipulated list, which will check for the empty and single entry cases as well.

           

          Out of curiosity is this manipulation part of a modification to the engine or with logic to form queries to submit to Teiid?

          • 2. Re: Re: CompoundCriteria.getCriteria().removeAll(...) leaving empty criteria ()
            deveshmishra

            Thanks Steven, the Criteria.combineCriteria works when I use a separateCriteriaByAnd method. something like below.

             

            q.setCriteria(CompoundCriteria.combineCriteria(CompoundCriteria.separateCriteriaByAnd(q.getCriteria())));

             

            q is the final manipulated query (essentially a SELECT * FROM T, B WHERE (B.B = 10) AND ())

             

            The challenge is, it does not work in case of an OR in the Compound criteria. example - SELECT * FROM T, B WHERE (B.B = 10) AND ((T.A < 10) OR(T.B = 10))

             

            I reused (copy-pasted) the separateCriteriaByAnd method in my code and enabled it to work even with OR and it all works good. this looks like a hack, do you have a better approach. Thanks

             

            The queries are manipulated and/or formed based on the logic and then submitted to teiid for processing.

            • 3. Re: Re: CompoundCriteria.getCriteria().removeAll(...) leaving empty criteria ()
              shawkins

              > I reused (copy-pasted) the separateCriteriaByAnd method in my code and enabled it to work even with OR and it all works good. this looks like a hack, do you have a better approach.

               

              When dealing with an AND, the conjuncts can be dealt with individually as their effect is cumulative.  Removing just a single disjunct from OR may not logically be what you want to do.

              • 4. Re: Re: CompoundCriteria.getCriteria().removeAll(...) leaving empty criteria ()
                deveshmishra

                Right, this probably will lead to other unknown issues. Any other approach you can suggest.? Thanks

                • 5. Re: Re: CompoundCriteria.getCriteria().removeAll(...) leaving empty criteria ()
                  deveshmishra

                  I have found another solution (hack?) to this problem.

                   

                  1) fix the orphan compound criteria. like below. if the criteriaCount is zero, i.e. orphan compound criteria. remove it. This solution works too.

                   

                  private void fixOrphanCompoundCrits(final CompoundCriteria obj)

                      {

                          final List<Criteria> crits = new ArrayList<>();

                          for (final Criteria crit : obj.getCriteria())

                          {

                              if (crit instanceof CompoundCriteria)

                              {

                                  if (((CompoundCriteria) crit).getCriteriaCount() == 0)

                                  {

                                      crits.add(crit);

                                  }

                              }

                          }

                          obj.getCriteria().removeAll(crits);

                      }

                   

                  Another approach will be to parse through the query and remove the orphan criteria. In this case we can visit through the CompoundCriteria and iterate through the Criteria. In case of an ExpressionCriteria, if it is empty we can remove it. Something like below.

                   

                  private static void fixQuery(final Query q)

                      {

                          PostOrderNavigator.doVisit(q, new LanguageVisitor()

                          {

                              @Override

                              public void visit(final CompoundCriteria obj)

                              {

                                  Criteria crTooRemove = null;

                                  for (final Criteria crit : obj.getCriteria())

                                  {

                                      if (crit instanceof ExpressionCriteria)

                                      {

                                          final ExpressionCriteria exCrit = (ExpressionCriteria) crit;

                                          if (exCrit.getExpression() instanceof Array)

                                          {

                                              final Array arr = (Array) exCrit.getExpression();

                   

                   

                                              if (arr.getExpressions().size() == 0)

                                              {

                                                  crTooRemove = crit;

                                              }

                                          }

                                      }

                                  }

                                  obj.getCriteria().remove(crTooRemove);

                              }

                          });

                      }

                  • 6. Re: Re: CompoundCriteria.getCriteria().removeAll(...) leaving empty criteria ()
                    shawkins

                    > I have found another solution (hack?) to this problem.

                     

                    It's really up to your logic (what expression/criteria is to be removed) as to what you'll need to do.  You can also look at ExpressionMappingVisitor which is designed to modify expressions in place.  For example a common thing to do is replace a predicate (with true/false/unknown) rather than remove it.  Then it's up to the rewrite logic to simplify.

                    1 of 1 people found this helpful
                    • 7. Re: CompoundCriteria.getCriteria().removeAll(...) leaving empty criteria ()
                      deveshmishra

                      Thanks Steven. I have another questions regarding the PostOrderNavigator behaviour. Consider the following code.

                       

                      final Query q =

                                      (Query) QueryParser.getQueryParser()

                                                         .parseCommand("SELECT * FROM T, B WHERE (B = 20) AND ((T.A = 10) AND (T.B = 20))");

                       

                              PostOrderNavigator.doVisit(q, new LanguageVisitor()

                              {

                                  @Override

                                  public void visit(final CompoundCriteria obj)

                                  {

                                      System.out.println(obj);

                                  }

                              });

                       

                      The output it gives is...

                      (T.A = 10) AND (T.B = 20)

                      (B = 20) AND ((T.A = 10) AND (T.B = 20))

                       

                      In this case, we are getting the repeated inner criteria. (T.A = 10) AND (T.B = 20) was already visited but again it appears in the second line where the entire criteria is visited. Any help is greatly appreciated. Thanks a lot.

                      • 8. Re: CompoundCriteria.getCriteria().removeAll(...) leaving empty criteria ()
                        shawkins

                        That is the expected output.  Every parens () is a nesting level of compound criteria.  They are not flattened by the engine until rewrite.

                        • 9. Re: CompoundCriteria.getCriteria().removeAll(...) leaving empty criteria ()
                          deveshmishra

                          Thanks Steven. Is there a way I can find out if it is a nesting level of Compound Criteria. The problem that I am facing is, I am traversing through the tree and for CompoundCriteria visitor - splitting it and populating another query. In my case, I am getting multiple criteria's added. Thank you.

                          • 10. Re: CompoundCriteria.getCriteria().removeAll(...) leaving empty criteria ()
                            shawkins

                            More than likely this just represents an issue with what you are going after.  It's not any compoundcriteria there's something specific about some part of it in the where clause. For example just visiting the entire query will hit criteria in select clause case statements, join criteria, the having clause, etc.

                            • 11. Re: CompoundCriteria.getCriteria().removeAll(...) leaving empty criteria ()
                              deveshmishra

                              Was just wondering, if I could traverse a criteria (lets not consider the entire query). Just a Criteria with branching.and could filter and pull criteria to build another query.

                               

                              Say below... SELECT * FROM T, B WHERE (B = 20) AND ((T.A = 10) AND (T.B = 20))

                               

                              I want to filter out criteria of B.

                              so the resultant query is to be SELECT * FROM T, B WHERE ((T.A = 10) AND (T.B = 20))

                               

                              but what I am getting is... SELECT * FROM T, B WHERE (T.A = 10) AND (T.B = 20) and (T.A = 10) AND (T.B = 20) and ((T.A = 10) AND (T.B = 20))

                              • 12. Re: CompoundCriteria.getCriteria().removeAll(...) leaving empty criteria ()
                                shawkins

                                > I want to filter out criteria of B.

                                 

                                As long as you are just considering the AND / conjunctive case, then you would first use separateCriteriaByAnd, then remove as needed and call combineCriteria.  If you can have OR, then you need to consider what's the meaning of what copying/removing disjuncts.  In any case you know that if one of the conjuncts returned by separateCriteriaByAnd is a CompoundCriteria, then it's an OR case.  You could just put your handling there and recombine using an OR, or whatever is appropriate.

                                • 13. Re: CompoundCriteria.getCriteria().removeAll(...) leaving empty criteria ()
                                  deveshmishra

                                  Thanks Steven. Works like charm.