code: @Override
      public GroovyExpression apply(GroovyExpression expr, OptimizationContext context) {
  
          FunctionCallExpression exprAsFunction = (FunctionCallExpression)expr;
          GroovyExpression result = exprAsFunction.getCaller();
  
          List<GroovyExpression> nonExtractableArguments = new ArrayList<>();
          for(GroovyExpression argument : exprAsFunction.getArguments()) {
  
              if (GremlinQueryOptimizer.isExtractable(argument)) {
                  //Set the caller of the deepest expression in the call hierarchy
                  //of the argument to point to the current result.
                  //For example, if result is "g.V()" and the updatedArgument is "has('x').has('y')",
                  //updatedArgument would be a tree like this:
                  //
                  //                  has('y')
                  //                 /
                  //                / caller
                  //              |/_
                  //           has('x')
                  //             /
                  //            / caller
                  //          |/_
                  //        (null)
                  //
                  //We would set the caller of has('x') to be g.V(), so result would become g.V().has('x').has('y').
                  //
                  // Note: This operation is currently done by making a copy of the argument tree.  That should
                  // be changed.
                  result = GremlinQueryOptimizer.copyWithNewLeafNode(
                          (AbstractFunctionExpression) argument, result);
              } else {
                  logger_.warn("Found non-extractable argument '{}' in the 'and' expression '{}'",argument.toString(), expr.toString());
                  nonExtractableArguments.add(argument);
              }
          }
  
          if (!nonExtractableArguments.isEmpty()) {
              //add a final 'and' call with the arguments that could not be extracted
              result =  factory.generateLogicalExpression(result, "and", nonExtractableArguments);
          }
          return result;
      }
docs: Expands the given and expression.  There is no need to recursively
  expand the children here.  This method is called recursively by
  GremlinQueryOptimier on the children.