Stream Data to Atlas
On this page
If you have a mobile or client application that produces a large volume of data you'd like to stream to MongoDB Atlas, you can sync data unidirectionally using Device Sync. We call the feature that enables this unidirectional sync Data Ingest.
You can use Data Ingest to stream data from the client application to a Device Sync-enabled Atlas App Services App.
You might want to sync data unidirectionally in IoT applications, such as a weather sensor sending data to the cloud. Data Ingest is also useful for writing other types of immutable data where you do not require conflict resolution, such as creating invoices from a retail app or logging application events.
Data Ingest is optimized to provide performance improvements for heavy client-side insert-only workloads.
Sync Data Unidirectionally from a Client Application
To use Data Ingest, stream data to Atlas using insert-only asymmetric objects in a synced database.
Atlas Device Sync completely manages the lifecycle of this data. The SDK maintains it on the device until Data Ingest synchronization is complete. Then, the SDK removes the data from the device.
You cannot read, query, or delete an asymmetric object from a database. Asymmetric objects are incompatible with standard, bi-directional Device Sync or a non-synced database.
Define an Asymmetric Object
You can sync data unidirectionally when you declare an object's
schema as a REALM_ASYMMETRIC_SCHEMA
.
For more information on how to define a REALM_ASYMMETRIC_SCHEMA
,
including limitations when linking to other object types, refer to
Define an Asymmetric Object.
To define an asymmetric object, your objects must implement the IAsymmetricObject interface or derive from the AsymmetricObject class.
For more information on how to define an asymmetric object, refer to Define an Asymmetric Object.
To define an asymmetric object, pass ObjectType.asymmetricObject
to
@RealmModel()
.
For more information on how to define an asymmetric object, refer to Define an Asymmetric Object.
Asymmetric objects sync data unidirectionally. Define an asymmetric object
by setting asymmetric
to true
in your object schema. For more
information, refer to the BaseObjectSchema API reference.
For more information on how to define an asymmetric object, refer to Define an Asymmetric Object.
You can sync data unidirectionally when that object is an
AsymmetricRealmObject
.
Define an asymmetric object by implementing the AsymmetricRealmObject interface.
Asymmetric objects broadly support the same property types as RealmObject
,
with a few exceptions:
Asymmetric objects can only be used in synced databases. However, you cannot create subscriptions to asymmetric objects.
An
AsymmetricRealmObject
can containEmbeddedRealmObject
types, but cannot containRealmObject
types or otherAsymmetricRealmObject
types.AsymmetricRealmObject
types cannot be used as properties in other database objects.
Additionally, asymmetric objects do not function in the same way as other database objects. You cannot add, read, update, or delete an asymmetric object from the database. You can only create an asymmetric object, which then syncs unidirectionally to the Atlas database linked to your App with Device Sync. The SDK then deletes this object after syncing.
You can sync data unidirectionally when that object is an AsymmetricObject
.
Define an AsymmetricObject by deriving from AsymmetricObject.
For more information on how to define an AsymmetricObject
, refer to
Define an AsymmetricObject.
Asymmetric objects sync data unidirectionally. Define an asymmetric object
by setting asymmetric
to true
in your object schema. For more
information, refer to the BaseObjectSchema API reference.
For more information on how to define an asymmetric object, refer to Define an Asymmetric Object.
struct WeatherSensorReading { realm::primary_key<realm::object_id> _id{realm::object_id::generate()}; std::string deviceId; double temperatureInFahrenheit; int64_t windSpeedInMph; }; REALM_ASYMMETRIC_SCHEMA(WeatherSensorReading, _id, deviceId, temperatureInFahrenheit, windSpeedInMph)
private partial class Measurement : IAsymmetricObject { [ ] public Guid Id { get; private set; } = Guid.NewGuid(); public double Value { get; set; } public DateTimeOffset Timestamp { get; private set; } = DateTimeOffset.UtcNow; }
(ObjectType.asymmetricObject)class _WeatherSensor { () "_id") ( late ObjectId id; late String deviceId; late double modtemperatureInFahrenheitel; late double barometricPressureInHg; late double windSpeedInMph; }
class WeatherSensor extends Realm.Object { static schema = { name: "WeatherSensor", // Sync WeatherSensor objects one way from your device // to your Atlas database. asymmetric: true, primaryKey: "_id", properties: { _id: "objectId", deviceId: "string", temperatureInFahrenheit: "int", barometricPressureInHg: "float", windSpeedInMph: "float", }, }; }
// Implements the `AsymmetricRealmObject` interface class WeatherSensor : AsymmetricRealmObject { var id: ObjectId = ObjectId() var deviceId: String = "" var temperatureInFarenheit: Float = 0.0F var barometricPressureInHg: Float = 0.0F var windSpeedInMph: Int = 0 }
class WeatherSensor: AsymmetricObject { true) var _id: ObjectId (primaryKey: var deviceId: String var temperatureInFahrenheit: Float var barometricPressureInHg: Float var windSpeedInMph: Int }
class WeatherSensor extends Realm.Object<WeatherSensor> { _id!: Realm.BSON.ObjectId; deviceId!: string; temperatureInFahrenheit!: number; barometricPressureInHg!: number; windSpeedInMph!: number; static schema: ObjectSchema = { name: "WeatherSensor", // sync WeatherSensor objects one way from your device // to your Atlas database. asymmetric: true, primaryKey: "_id", properties: { _id: "objectId", deviceId: "string", temperatureInFahrenheit: "int", barometricPressureInHg: "float", windSpeedInMph: "float", }, }; }
Connect and Authenticate with an App Services App
To stream data from the client to your backend App, you must connect to Atlas and authenticate a user.
auto appConfig = realm::App::configuration(); appConfig.app_id = APP_ID; auto app = realm::App(appConfig); auto user = app.login(realm::App::credentials::anonymous()).get();
App app = App.Create(myAppId); Realms.Sync.User user = app.LogInAsync( Credentials.Anonymous()).Result;
final appConfig = AppConfiguration(APP_ID); final app = App(appConfig);
final anonCredentials = Credentials.anonymous(); await app.logIn(anonCredentials);
// Create an anonymous credential const credentials = Realm.Credentials.anonymous(); const user = await app.logIn(credentials);
val app = App.create(YOUR_APP_ID) val user = app.login(credentials)
let app = App(id: INSERT_APP_ID_HERE) do { let user = try await login() await openSyncedRealm(user: user) } catch { print("Error logging in: \(error.localizedDescription)") } func login() async throws -> User { let user = try await app.login(credentials: .anonymous) return user }
// Create an anonymous credential const credentials = Realm.Credentials.anonymous(); const user = await app.logIn(credentials);
Open a Synced Database
After you have an authenticated user, open a synced database.
Unlike bi-directional Device Sync, Data Ingest does not use a Sync subscription.
Unlike opening a database for non-asymmetric object types, when you open a
database for Data Ingest, you must specify the asymmetric_object
types
you want to sync.
Tip
Mixed Object and Asymmetric Object Types
You cannot open a single synced database to manage both regular objects and asymmetric objects. You must use different databases to manage these different object types.
Specify the AsymmetricRealmObject
types you want to sync.
If you have non-asymmetric objects in the same database, you can add a Sync subscription query for only those objects.
Specify the AsymmetricObject
types you want to sync.
Note
Mixed Synced and Non-Synced Databases in Projects
The AsymmetricObject
type is incompatible with non-synced databases.
If your project uses both a synced and non-synced database, you must
explicitly pass a subset of classes in your database configuration to exclude the
AsymmetricObject
from your non-synced database.
Automatic schema discovery means that opening a non-synced database
without specifically excluding the AsymmetricObject
from the
configuration can throw an error related to trying to use an
incompatible object type.
auto syncConfig = user.flexible_sync_configuration(); auto realm = realm::open<realm::WeatherSensorReading>(syncConfig);
var config = new FlexibleSyncConfiguration(user) { Schema = new[] { typeof(Measurement) } }; realm = Realm.GetInstance(config);
final currentUser = await app.logIn(credentials); final config = Configuration.flexibleSync(currentUser, [Tricycle.schema], path: 'flex.realm'); final realm = Realm(config);
// The documentation does not currently have this code example in JavaScript. // Please refer to the other languages or related pages for example code.
val config = SyncConfiguration.create(user, setOf(WeatherSensor::class)) val realm = Realm.open(config) Log.v("Successfully opened realm: ${realm.configuration.name}")
func openSyncedRealm(user: User) async { do { var asymmetricConfig = user.flexibleSyncConfiguration() asymmetricConfig.objectTypes = [WeatherSensor.self] let asymmetricRealm = try await Realm(configuration: asymmetricConfig) await useRealm(asymmetricRealm, user) } catch { print("Error opening realm: \(error.localizedDescription)") } }
const realm = await Realm.open({ schema: [WeatherSensor], sync: { user: app.currentUser, flexible: true, }, });
Create Asymmetric Objects
Once you have an open database, you can create an asymmetric_object
and set its values as you would a regular object.
The process for writing asymmetric objects is the same as standard bi-directional Sync. The following code shows creating an asymmetric object and syncing it with the backend. It also shows to queries that generate errors.
Once you have an open database, you can create an asymmetric object inside
a write transaction. Pass your object data to realm.ingest
.
Once you have an open database, you can create an asymmetric object inside
a write transaction using Realm.create().
When creating an asymmetric object, Realm.create()
returns
undefined
rather than the object itself.
Once you have an open database, you can create an AsymmetricRealmObject
inside a write transaction using the insert()
extension method:
Once you have an open database, you can create an AsymmetricObject
inside
a write transaction using create(_ type:, value:).
Once you have an open database, you can create an asymmetric object inside
a write transaction using Realm.create().
When creating an asymmetric object, Realm.create()
returns
undefined
rather than the object itself.
auto weatherSensorReading = realm::WeatherSensorReading{.deviceId = "WX1278UIT", .temperatureInFahrenheit = 64.7, .windSpeedInMph = 7}; realm.write([&] { realm.add(std::move(weatherSensorReading)); });
public void SendMeasurementToRealm() { var measurement = new Measurement { Value = 9.876 }; realm.Write(() => { realm.Add(measurement); }); // The following line will cause a compile time error // _ = realm.All<Measurement>(); // The following line will compile but throw a // Realms.Exceptions.RealmInvalidObjectException at runtime // _ = measurement.Value; }
realm.write(() { realm.ingest( WeatherSensor(weatherSensorId, "WX1278UIT", 66.7, 29.65, 2)); });
// The documentation does not currently have this code example in JavaScript. // Please refer to the other languages or related pages for example code.
// Open a write transaction realm.write { // Create a new asymmetric object val weatherSensor = WeatherSensor().apply { deviceId = "WX1278UIT" temperatureInFarenheit = 6.7F barometricPressureInHg = 29.65F windSpeedInMph = 2 } // Insert the object into the realm with the insert() extension method insert(weatherSensor) // WeatherSensor object is inserted into the realm, then synced to the // App Services backend. You CANNOT access the object locally because it's // deleted from the local realm after sync is complete. }
func useRealm(_ asymmetricRealm: Realm, _ user: User) async { try! asymmetricRealm.write { asymmetricRealm.create(WeatherSensor.self, value: [ "_id": ObjectId.generate(), "deviceId": "WX1278UIT", "temperatureInFahrenheit": 66.7, "barometricPressureInHg": 29.65, "windSpeedInMph": 2 ]) } }
realm.write(() => { realm.create(WeatherSensor, { _id: new BSON.objectId(), deviceId: "WX1278UIT", temperatureInFahrenheit: 66.7, barometricPressureInHg: 29.65, windSpeedInMph: 2, }); });