private Bson negate(Expression expression) {
    if (!(expression instanceof Call)) {
      return Filters.not(expression.accept(this));
    }

    Call notCall = (Call) expression;
    Operator notOperator = notCall.operator();

    if (notOperator == Operators.NOT) {
      // NOT NOT a == a
      return notCall.arguments().get(0).accept(this);
    } else if (notOperator == Operators.EQUAL) {
      return newCall(notCall, Operators.NOT_EQUAL);
    } else if (notOperator == Operators.NOT_EQUAL) {
      return newCall(notCall, Operators.EQUAL);
    } else if (notOperator == Operators.IN) {
      return newCall(notCall, Operators.NOT_IN);
    } else if (notOperator == Operators.NOT_IN) {
      return newCall(notCall, Operators.IN);
    } else if (notOperator == Operators.OR) {
      return Filters.nor(notCall.arguments().stream().map(a -> a.accept(this)).collect(Collectors.toList()));
    } else if (notOperator == Operators.AND) {
      // NOT A and B == (NOT A) or (NOT B)
      return Filters.or(notCall.arguments().stream().map(this::negate).collect(Collectors.toList()));
    } else if (notOperator == OptionalOperators.IS_ABSENT || notOperator == OptionalOperators.IS_PRESENT) {
      Operator newOp = notOperator == OptionalOperators.IS_ABSENT ? OptionalOperators.IS_PRESENT : OptionalOperators.IS_ABSENT;
      return newCall(notCall, newOp);
    } else if (notOperator == IterableOperators.IS_EMPTY || notOperator == IterableOperators.NOT_EMPTY) {
      Operator newOp = notOperator == IterableOperators.IS_EMPTY ? IterableOperators.NOT_EMPTY : IterableOperators.IS_EMPTY;
      return newCall(notCall, newOp);
    }

    // don't really know how to negate here
    return Filters.not(notCall.accept(this));
  }
see https://docs.mongodb.com/manual/reference/operator/query/not/
NOT operator is mongo is a little specific and can't be applied on all levels
$not: { $or ... } will not work and should be transformed to $nor
criteria/mongo/src/org/immutables/criteria/mongo/FindVisitor.java