Docs Menu
Docs Home
/ /
Atlas Device SDKs
/

Stream Data to Atlas

On this page

  • Sync Data Unidirectionally from a Client Application
  • Define an Asymmetric Object
  • Connect and Authenticate with an App Services App
  • Open a Synced Database
  • Create Asymmetric Objects

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.

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.

1

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 contain EmbeddedRealmObject types, but cannot contain RealmObject types or other AsymmetricRealmObject 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
{
[PrimaryKey, MapTo("_id")]
public Guid Id { get; private set; } = Guid.NewGuid();
public double Value { get; set; }
public DateTimeOffset Timestamp { get; private set; } = DateTimeOffset.UtcNow;
}
@RealmModel(ObjectType.asymmetricObject)
class _WeatherSensor {
@PrimaryKey()
@MapTo("_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 {
@PersistedName("_id")
@PrimaryKey
var id: ObjectId = ObjectId()
var deviceId: String = ""
var temperatureInFarenheit: Float = 0.0F
var barometricPressureInHg: Float = 0.0F
var windSpeedInMph: Int = 0
}
class WeatherSensor: AsymmetricObject {
@Persisted(primaryKey: true) var _id: ObjectId
@Persisted var deviceId: String
@Persisted var temperatureInFahrenheit: Float
@Persisted var barometricPressureInHg: Float
@Persisted 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",
},
};
}
2

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);
3

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}")
@MainActor
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,
},
});
4

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.
}
@MainActor
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,
});
});

Back

Manage Sync Sessions

Next

Partition-Based Sync