private static int compare(final Val val1, final Val val2, final boolean isCaseSensitive) {
        if (isNull(val1) && isNull(val2)) {
            return 0;
        } else if (isNull(val1)) {
            return 1;
        } else if (isNull(val2)) {
            return -1;
        } else if (Objects.equals(val1.getClass(), val2.getClass())) {
            // Same type so delegate to the type's own comparator
            return val1.getDefaultComparator(isCaseSensitive).compare(val1, val2);
        } else if (bothAreNumeric(val1, val2)) {
            // Mixed number types so need to be a bit careful
            // Could be comparing valString("123") with ValDouble(123.456)
            if (noFractionalParts(val1, val2)) {
                // No fractional parts so compare as long which avoids all precision
                // problems that come with doubles
                return AS_LONG_COMPARATOR.compare(val1, val2);
            } else {
                // Mixed decimal values, e.g. float + double
                // or one decimal one non-decimal, e.g. int + double,
                // so do the comparison as double
                if (haveTypes(val1, val2, ValDouble.class, ValFloat.class)) {
                    // Special case to deal with differing precision in floats/doubles
                    return compareAsDoublesWithTolerance(val1, val2);
                } else {
                    return AS_DOUBLE_COMPARATOR.compare(val1, val2);
                }
            }
        } else {
            // Get the appropriate comparator for the mixed types or fallback to a string
            // comparison if
            final Comparator<Val> comparator = getComparatorForTypes(val1.type(), val2.type(), isCaseSensitive)
                    .orElseGet(() -> getAsStringComparator(isCaseSensitive));
            // e.g. comparing ValString to ValInteger, which doesn't really make any sense
            // but, we might as well compare on something, so compare on the value as a string
//            return AS_TYPE_COMPARATOR.compare(val1, val2);
            return comparator.compare(val1, val2);
        }
    }
A generic comparison method for two {@link Val} instances.
null or ValNull values are considered greater than non-null or
non ValNull values.
If val1 and val2 are the same subclass then the specific comparator
for that type will be delegated to.
stroom-query/stroom-query-language/src/main/java/stroom/query/language/functions/ValComparators.java