function castUpdateVal(schema, val, op, $conditional, context, path) {
  if (!schema) {
    // non-existing schema path
    if (op in numberOps) {
      try {
        return castNumber(val);
      } catch (err) {
        throw new CastError('number', val, path);
      }
    }
    return val;
  }

  // console.log('CastUpdateVal', path, op, val, schema);

  const cond = schema.caster && op in castOps &&
      (utils.isObject(val) || Array.isArray(val));
  if (cond && !overwriteOps[op]) {
    // Cast values for ops that add data to MongoDB.
    // Ensures embedded documents get ObjectIds etc.
    let schemaArrayDepth = 0;
    let cur = schema;
    while (cur.$isMongooseArray) {
      ++schemaArrayDepth;
      cur = cur.caster;
    }
    let arrayDepth = 0;
    let _val = val;
    while (Array.isArray(_val)) {
      ++arrayDepth;
      _val = _val[0];
    }

    const additionalNesting = schemaArrayDepth - arrayDepth;
    while (arrayDepth < schemaArrayDepth) {
      val = [val];
      ++arrayDepth;
    }

    let tmp = schema.applySetters(Array.isArray(val) ? val : [val], context);

    for (let i = 0; i < additionalNesting; ++i) {
      tmp = tmp[0];
    }
    return tmp;
  }

  if (op in noCastOps) {
    return val;
  }
  if (op in numberOps) {
    // Null and undefined not allowed for $pop, $inc
    if (val == null) {
      throw new CastError('number', val, schema.path);
    }
    if (op === '$inc') {
      // Support `$inc` with long, int32, etc. (gh-4283)
      return schema.castForQuery(
        null,
        val,
        context
      );
    }
    try {
      return castNumber(val);
    } catch (error) {
      throw new CastError('number', val, schema.path);
    }
  }
  if (op === '$currentDate') {
    if (typeof val === 'object') {
      return { $type: val.$type };
    }
    return Boolean(val);
  }

  if (mongodbUpdateOperators.has($conditional)) {
    return schema.castForQuery(
      $conditional,
      val,
      context
    );
  }

  if (overwriteOps[op]) {
    const skipQueryCastForUpdate = val != null && schema.$isMongooseArray && schema.$fullPath != null && !schema.$fullPath.match(/\d+$/);
    const applySetters = schema[schemaMixedSymbol] != null;
    if (skipQueryCastForUpdate || applySetters) {
      return schema.applySetters(val, context);
    }
    return schema.castForQuery(
      null,
      val,
      context
    );
  }

  return schema.castForQuery(null, val, context);
}
Casts `val` according to `schema` and atomic `op`.

@param {SchemaType} schema
@param {Object} val
@param {String} op the atomic operator ($pull, $set, etc)
@param {String} $conditional
@param {Query} context
@param {String} path
@api private