in blogs/sept-2021/converter.cc [237:340]
Variant Converter::ConvertFieldValueToVariant(const FieldValue& from) const {
switch (from.type()) {
// Primitives -- one-to-one mapping.
case FieldValue::Type::kNull:
return Variant::Null();
case FieldValue::Type::kBoolean:
return Variant(from.boolean_value());
case FieldValue::Type::kInteger:
return Variant(from.integer_value());
case FieldValue::Type::kDouble:
return Variant(from.double_value());
// `FieldValue` always owns the underlying string or blob, so the safest
// approach is to copy the value to a `Variant` that will assume
// ownership.
case FieldValue::Type::kString:
return Variant(from.string_value());
case FieldValue::Type::kBlob:
return Variant::FromMutableBlob(from.blob_value(), from.blob_size());
// Containers are converted recursively.
case FieldValue::Type::kArray:
return ConvertArray(from.array_value());
case FieldValue::Type::kMap:
return ConvertMap(from.map_value());
// Firestore-specific structs are encoded as maps with a boolean field
// "special" set to true and a string field "type" indicating the original
// type.
case FieldValue::Type::kTimestamp: {
Timestamp ts = from.timestamp_value();
MapFieldValue as_map = {
{"special", FieldValue::Boolean(true)},
{"type", FieldValue::String("timestamp")},
{"seconds", FieldValue::Integer(ts.seconds())},
{"nanoseconds", FieldValue::Integer(ts.nanoseconds())}};
return ConvertRegularMap(as_map);
// Note: if using the resulting `Variant` with RTDB, you might want to
// convert timestamps to the number of milliseconds since Unix epoch:
// Timestamp ts = from.timestamp_value();
// int64_t millis = ts.seconds() * 1000 + ts.nanoseconds() / (1000*1000);
// return Variant(millis);
}
case FieldValue::Type::kGeoPoint: {
GeoPoint gp = from.geo_point_value();
MapFieldValue as_map = {
{"special", FieldValue::Boolean(true)},
{"type", FieldValue::String("geo_point")},
{"latitude", FieldValue::Double(gp.latitude())},
{"longitude", FieldValue::Double(gp.longitude())}};
return ConvertRegularMap(as_map);
}
case FieldValue::Type::kReference: {
DocumentReference ref = from.reference_value();
std::string path = ref.path();
MapFieldValue as_map = {
{"special", FieldValue::Boolean(true)},
{"type", FieldValue::String("document_reference")},
{"document_path", FieldValue::String(path)}};
return ConvertRegularMap(as_map);
}
// Firestore-specific sentinel values can also be encoded as maps.
case FieldValue::Type::kDelete: {
// Note: if using the resulting `Variant` with RTDB, you might want to
// convert a `delete` sentinel to null:
// return Variant::Null();
MapFieldValue as_map = {{"special", FieldValue::Boolean(true)},
{"type", FieldValue::String("delete")}};
return ConvertRegularMap(as_map);
}
case FieldValue::Type::kServerTimestamp: {
MapFieldValue as_map = {{"special", FieldValue::Boolean(true)},
{"type", FieldValue::String("server_timestamp")}};
// Note: if using the resulting `Variant` with RTDB, you might want to
// convert the server timestamp to the representation used by RTDB:
// MapFieldValue as_map = {{".sv", FieldValue::String("timestamp")}};
return ConvertRegularMap(as_map);
}
// Several Firestore sentinel values cannot be converted losslessly
// (because they don't allow accessing the underlying value(s)). In this
// example, we will simply assert that these values should never be given
// to the converter.
case FieldValue::Type::kArrayUnion:
case FieldValue::Type::kArrayRemove:
case FieldValue::Type::kIncrementInteger:
case FieldValue::Type::kIncrementDouble:
assert(false && "Unsupported FieldValue type");
std::terminate();
default:
assert(false && "Unknown FieldValue type");
std::terminate();
}
}