private static void HandleAdd <TOut>(
            Operation operation,
            MapDescription map,
            string path,
            ConversionResult <TOut> conversion)
        {
            var lastDot = path.LastIndexOf(".", StringComparison.InvariantCulture);

            if (lastDot > 0)
            {
                // target location may not exists, however, only last property may not exists, but all previous should
                var existance = path.Substring(0, lastDot);
                conversion.Filters.Add(Builders <TOut> .Filter.Exists(existance));
            }

            object val;

            try
            {
                val = ConvertType(map, operation.value);
            }
            catch
            {
                conversion.Errors.Add(new OperationError(string.Format(ConversionErrorFormat, path), OperationErrorType.TypeError, operation));
                return;
            }
            var update = ConstructTypedSet <TOut>(path, map, val);

            conversion.Updates.Add(update);
        }
        private static object ConvertType(MapDescription map, object value)
        {
            // Actually, i wanted to implement something cool, but after i saw
            // https://github.com/aspnet/JsonPatch/blob/98e2d5d4c729770e5e8e146602ab2b6c5bdc439a/src/Microsoft.AspNetCore.JsonPatch/Adapters/ObjectAdapter.cs#L1012
            // i decided, that everything is ok :)
            var serialized = JsonConvert.SerializeObject(value);

            return(BsonSerializer.Deserialize(serialized, map.Type));
        }
        private static UpdateDefinition <TOut> ConstructTypedSet <TOut>(string path, MapDescription map, object value)
        {
            // TODO add caching
            var genericUpdate   = OperationUpdateDefinitionType.MakeGenericType(typeof(TOut), map.Type);
            var genericField    = GenericStringFieldDefinition.MakeGenericType(typeof(TOut), map.Type);
            var fieldDefinition = Activator.CreateInstance(genericField, path, null);
            //public OperatorUpdateDefinition(string operatorName, FieldDefinition<TDocument, TField> field, TField value)
            var updateDefinition = Activator.CreateInstance(genericUpdate, "$set", fieldDefinition, value);

            return((UpdateDefinition <TOut>)updateDefinition);
        }
        private static void HandleRemove <TOut>(
            Operation op,
            MapDescription map,
            string path,
            ConversionResult <TOut> conversion)
        {
            if (map.IsIndexer)
            {
                conversion.Errors.Add(new OperationError(NotSupportedByMongo, OperationErrorType.NotSupported, op));
                return;
            }
            var filter = Builders <TOut> .Filter.Exists(new StringFieldDefinition <TOut>(path));

            conversion.Filters.Add(filter);
            conversion.Updates.Add(Builders <TOut> .Update.Unset(path));
        }
        private static void HandleReplace <TOut>(
            Operation op,
            MapDescription map,
            string path,
            ConversionResult <TOut> conversion)
        {
            var val = op.value;

            try
            {
                val = ConvertType(map, val);
            }
            catch
            {
                conversion.Errors.Add(new OperationError(string.Format(ConversionErrorFormat, path), OperationErrorType.TypeError, op));
                return;
            }
            var filter = Builders <TOut> .Filter.Exists(new StringFieldDefinition <TOut>(path));

            var update = ConstructTypedSet <TOut>(path, map, val);

            conversion.Filters.Add(filter);
            conversion.Updates.Add(update);
        }