private void SetDocument(T entity, MutableDocument mutableDocument)
        {
            var properties = ObjectToDictionaryHelper.ToDictionary(entity);

            foreach (var prop in properties)
            {
                if (prop.Value is int)
                {
                    mutableDocument.SetInt(prop.Key, (int)prop.Value);
                }
                else if (prop.Value is long)
                {
                    mutableDocument.SetLong(prop.Key, (long)prop.Value);
                }
                else if (prop.Value is bool)
                {
                    mutableDocument.SetBoolean(prop.Key, (bool)prop.Value);
                }
                else if (prop.Value is DateTimeOffset)
                {
                    if ((DateTimeOffset)prop.Value != default(DateTimeOffset))
                    {
                        mutableDocument.SetDate(prop.Key, (DateTimeOffset)prop.Value);
                    }
                }
                else if (prop.Value is double)
                {
                    mutableDocument.SetDouble(prop.Key, (double)prop.Value);
                }
                else if (prop.Value is float)
                {
                    mutableDocument.SetFloat(prop.Key, (float)prop.Value);
                }
                else if (prop.Value is string)
                {
                    mutableDocument.SetString(prop.Key, (string)prop.Value);
                }
                else
                {
                    mutableDocument.SetValue(prop.Key, prop.Value);
                }
            }
        }
        public void TestGetFragmentFromDouble()
        {
            var doc = new MutableDocument("doc1");

            doc.SetDouble("double", 100.10);
            SaveDocument(doc, d =>
            {
                var fragment = d["double"];
                fragment.Exists.Should().BeTrue("because this portion of the data exists");
                fragment.String.Should().BeNull("because this fragment is not of this type");
                fragment.Array.Should().BeNull("because this fragment is not of this type");
                fragment.Dictionary.Should().BeNull("because this fragment is not of this type");
                fragment.Int.Should().Be(100, "because that is the converted value");
                fragment.Long.Should().Be(100L, "because that is the converted value");
                fragment.Double.Should().Be(100.10, "because that is the stored value");
                fragment.Float.Should().Be(100.10f, "because that is the default value");
                fragment.Boolean.Should().Be(true, "because that is the converted value");
                fragment.Date.Should().Be(DateTimeOffset.MinValue, "because that is the default value");
                fragment.Value.Should().NotBeNull("because this fragment has a value");
            });
        }
        public void TestPredictionInputOutput()
        {
            var echoModel = new EchoModel();

            echoModel.RegisterModel();

            using (var doc = new MutableDocument()) {
                doc.SetString("name", "Daniel");
                doc.SetInt("number", 2);
                doc.SetDouble("max", Double.MaxValue);
                Db.Save(doc);
            }

            var date  = DateTimeOffset.Now;
            var power = Function.Power(Expression.Property("number"), Expression.Int(2));
            var map   = new Dictionary <string, object>
            {
                ["null"]               = null,
                ["number1"]            = 10,
                ["number2"]            = 10.1,
                ["int_min"]            = Int32.MinValue,
                ["int_max"]            = Int32.MaxValue,
                ["int64_min"]          = Int64.MinValue,
                ["int64_max"]          = Int64.MaxValue,
                ["float_min"]          = Single.MinValue,
                ["float_max"]          = Single.MaxValue, // NOTE: Double limits are not guaranteed
                ["boolean_true"]       = true,
                ["boolean_false"]      = false,
                ["string"]             = "hello",
                ["date"]               = date,
                ["expr_property"]      = Expression.Property("name"),
                ["expr_value_number1"] = Expression.Value(20),
                ["expr_value_number2"] = Expression.Value(20.1),
                ["expr_value_boolean"] = Expression.Value(true),
                ["expr_value_string"]  = Expression.Value("hi"),
                ["expr_value_date"]    = Expression.Value(date),
                ["expr_value_null"]    = Expression.Value(null),
                ["expr_power"]         = power
            };

            var submap = new Dictionary <string, object> {
                ["foo"] = "bar"
            };

            map["dict"] = submap;
            var subList = new[] { "1", "2", "3" };

            map["array"] = subList;

            var subExprMap = new Dictionary <string, object> {
                ["ping"] = "pong"
            };

            map["expr_value_dict"] = Expression.Value(subExprMap);
            var subExprList = new[] { "4", "5", "6" };

            map["expr_value_array"] = Expression.Value(subExprList);

            var input      = Expression.Value(map);
            var model      = nameof(EchoModel);
            var prediction = Function.Prediction(model, input);

            using (var q = QueryBuilder.Select(SelectResult.Expression(prediction))
                           .From(DataSource.Database(Db)))
            {
                var rows = VerifyQuery(q, (n, result) =>
                {
                    var pred = result.GetDictionary(0);
                    pred.Count.Should().Be(map.Count,
                                           "because all properties should be serialized and recovered correctly");
                    pred.GetInt("number1").Should().Be(10);
                    pred.GetDouble("number2").Should().Be(10.1);
                    pred.GetInt("int_min").Should().Be(Int32.MinValue);
                    pred.GetInt("int_max").Should().Be(Int32.MaxValue);
                    pred.GetLong("int64_min").Should().Be(Int64.MinValue);
                    pred.GetLong("int64_max").Should().Be(Int64.MaxValue);
                    pred.GetFloat("float_min").Should().Be(Single.MinValue);
                    pred.GetFloat("float_max").Should().Be(Single.MaxValue);
                    pred.GetBoolean("boolean_true").Should().BeTrue();
                    pred.GetBoolean("boolean_false").Should().BeFalse();
                    pred.GetString("string").Should().Be("hello");
                    pred.GetDate("date").Should().Be(date);
                    pred.GetString("null").Should().BeNull();
                    pred.GetDictionary("dict").Should().Contain(submap);
                    pred.GetArray("array").Should().ContainInOrder(subList);

                    pred.GetString("expr_property").Should().Be("Daniel");
                    pred.GetInt("expr_value_number1").Should().Be(20);
                    pred.GetDouble("expr_value_number2").Should().Be(20.1);
                    pred.GetBoolean("expr_value_boolean").Should().BeTrue();
                    pred.GetString("expr_value_string").Should().Be("hi");
                    pred.GetDate("expr_value_date").Should().Be(date);
                    pred.GetString("expr_value_null").Should().BeNull();
                    pred.GetDictionary("expr_value_dict").Should().Contain(subExprMap);
                    pred.GetArray("expr_value_array").Should().ContainInOrder(subExprList);
                    pred.GetInt("expr_power").Should().Be(4);
                });

                rows.Should().Be(1);
            }
        }