Docs 菜单
Docs 主页
/ /
Atlas Device SDKs
/ /

托管 Flexible Sync 订阅 — Java SDK

在此页面上

  • Overview
  • 订阅可查询字段
  • 添加订阅
  • 等待订阅更改同步
  • 使用新查询更新订阅
  • 删除订阅
  • “灵活同步”RQL 的要求和限制
  • 已索引可查询字段订阅的要求
  • 灵活同步中不支持的查询运算符
  • 列出查询
  • 嵌入式对象或关联对象
  • 查询大小限制

Flexible Sync 使用订阅和权限来确定要与应用同步哪些数据。

要在 SDK 中使用 Flexible Sync,请执行以下操作:

  • 在后端配置 Flexible Sync

  • 初始化应用

  • 对客户端项目中的用户进行身份验证

  • 使用 Flexible Sync 配置打开已同步的 Realm

  • 在客户端应用程序中添加订阅

您可以添加、更新和删除查询订阅,以确定哪些数据同步到客户端设备。

提示

另请参阅:

本页详细介绍如何托管Flexible Sync的订阅。

有关将 Atlas Device Sync 与 SDK 结合使用的一般信息,例如如何在背景中同步更改或暂停同步会话,请查看在设备之间同步更改。

有关设置Flexible Sync权限的信息,请查看Flexible Sync规则和权限。

在后端配置 Flexible Sync 时,您可以指定客户端应用程序可以查询哪些字段。 在客户端应用程序中,使用 subscriptions API 托管对可查询字段的特定查询的一组订阅。您可以使用Java SDK 的 Fluent 接口RQL 来构建查询。

重要

Flexible Sync不支持 RQL 中提供的所有操作符。 有关详细信息,请参阅“ Flexible Sync RQL 限制”。

您可以:

  • 添加订阅

  • 响应订阅状态

  • 使用新查询更新订阅

  • 删除对象类型的单个订阅或所有订阅

与订阅匹配的数据(其中用户具有适当的权限)在客户端和后端应用程序之间同步。

您可以为订阅指定一个可选的字符串名称。

提示

始终指定订阅名称

如果您的应用程序使用多个订阅,请始终指定订阅名称。 这使得您的订阅在应用程序的其他位置更容易查找、更新和删除。

创建订阅时,Realm 会查找与特定对象类型的查询匹配的数据。您可以在不同的对象类型上拥有多个订阅集。您还可以对同一对象类型进行多次查询。

您可以使用显式名称创建订阅。然后,您可以按名称搜索该订阅,执行更新或删除操作。

SyncConfiguration config = new SyncConfiguration.Builder(app.currentUser())
.initialSubscriptions(new SyncConfiguration.InitialFlexibleSyncSubscriptions() {
@Override
public void configure(Realm realm, MutableSubscriptionSet subscriptions) {
// add a subscription with a name
subscriptions.add(Subscription.create("frogSubscription",
realm.where(Frog.class)
.equalTo("species", "spring peeper")));
}
})
.build();
Realm.getInstanceAsync(config, new Realm.Callback() {
@Override
public void onSuccess(Realm realm) {
Log.v("EXAMPLE", "Successfully opened a realm.");
// later, you can look up this subscription by name
Subscription subscription = realm.getSubscriptions().find("frogSubscription");
}
});
val config = SyncConfiguration.Builder(app.currentUser())
.initialSubscriptions { realm, subscriptions ->
// add a subscription with a name
subscriptions.add(
Subscription.create(
"frogSubscription",
realm.where(Frog::class.java)
.equalTo("species", "spring peeper")
)
)
}
.build()
Realm.getInstanceAsync(config, object : Realm.Callback() {
override fun onSuccess(realm: Realm) {
Log.v("EXAMPLE", "Successfully opened a realm.")
// later, you can look up this subscription by name
val subscription =
realm.subscriptions.find("frogSubscription")
}
})

您还可以按查询搜索订阅。 如果在创建订阅时省略了名称,则这是查找订阅的唯一方法。

SyncConfiguration config = new SyncConfiguration.Builder(app.currentUser())
.initialSubscriptions(new SyncConfiguration.InitialFlexibleSyncSubscriptions() {
@Override
public void configure(Realm realm, MutableSubscriptionSet subscriptions) {
// add a subscription without assigning a name
subscriptions.add(Subscription.create(
realm.where(Frog.class)
.equalTo("species", "spring peeper")));
}
})
.build();
Realm.getInstanceAsync(config, new Realm.Callback() {
@Override
public void onSuccess(Realm realm) {
Log.v("EXAMPLE", "Successfully opened a realm.");
// later, you can look up this subscription by query
Subscription subscription = realm.getSubscriptions().find(realm.where(Frog.class)
.equalTo("species", "spring peeper"));
}
});
val config = SyncConfiguration.Builder(app.currentUser())
.initialSubscriptions { realm, subscriptions ->
// add a subscription without assigning a name
subscriptions.add(
Subscription.create(
realm.where(Frog::class.java)
.equalTo("species", "spring peeper")
)
)
}
.build()
Realm.getInstanceAsync(config, object : Realm.Callback() {
override fun onSuccess(realm: Realm) {
Log.v("EXAMPLE", "Successfully opened a realm.")
// later, you can look up this subscription by query
val subscription =
realm.subscriptions.find(
realm.where(
Frog::class.java
).equalTo("species", "spring peeper")
)
}
})

注意

重复订阅

订阅名称必须是唯一名称。 添加与现有订阅同名的订阅会引发错误。

如果您没有显式命名订阅,而是多次订阅相同的未命名查询,则 Realm 不会将重复查询保存到订阅集。

如果您以不同的名称多次订阅同一查询,则 Realm 会将这两个订阅保存到订阅集。

在订阅写入区块中添加订阅。 您将每个新订阅附加到客户端的 Realm 订阅中。

SyncConfiguration config = new SyncConfiguration.Builder(app.currentUser())
.initialSubscriptions(new SyncConfiguration.InitialFlexibleSyncSubscriptions() {
@Override
public void configure(Realm realm, MutableSubscriptionSet subscriptions) {
subscriptions.add(Subscription.create("subscriptionName",
realm.where(Frog.class)
.equalTo("species", "spring peeper")));
}
})
.build();
// instantiate a realm instance with the flexible sync configuration
Realm.getInstanceAsync(config, new Realm.Callback() {
@Override
public void onSuccess(Realm realm) {
Log.v("EXAMPLE", "Successfully opened a realm.");
}
});
val config = SyncConfiguration.Builder(app.currentUser())
.initialSubscriptions { realm, subscriptions ->
subscriptions.add(
Subscription.create(
"subscriptionName",
realm.where(Frog::class.java)
.equalTo("species", "spring peeper")
)
)
}
.build()
// instantiate a realm instance with the flexible sync configuration
Realm.getInstanceAsync(config, object : Realm.Callback() {
override fun onSuccess(realm: Realm) {
Log.v("EXAMPLE", "Successfully opened a realm.")
}
})

注意

对象链接

要查看链接对象,必须将对象及其链接对象都添加到订阅集中。

如果订阅结果包含一个对象,该对象的属性链接到结果中未包含的对象,则该链接显示为空。我们无法区分该属性的值是否为合法空值,或者所链接的对象是否存在但不在查询订阅的视图中。

在本地写入订阅设立的更新只是更改订阅的其中一个组成部分。 本地订阅更改后,客户端会与服务器同步,以解决由于订阅更改而导致的任何数据更新问题。 这可能平均值在同步域中添加或删除数据。 使用waitForInitialRemoteData()构建器方法强制应用程序处于区块状态,直到客户端订阅数据同步到后端,然后再打开域:

SyncConfiguration config = new SyncConfiguration.Builder(app.currentUser())
.initialSubscriptions(new SyncConfiguration.InitialFlexibleSyncSubscriptions() {
@Override
public void configure(Realm realm, MutableSubscriptionSet subscriptions) {
subscriptions.add(Subscription.create("my subscription",
realm.where(Frog.class)
.equalTo("species", "poison dart")));
}
})
.waitForInitialRemoteData(2112, TimeUnit.MILLISECONDS)
.build();
Realm.getInstanceAsync(config, new Realm.Callback() {
@Override
public void onSuccess(Realm realm) {
Log.v("EXAMPLE", "Successfully opened a realm.");
}
});
val config = SyncConfiguration.Builder(app.currentUser())
.initialSubscriptions { realm, subscriptions ->
subscriptions.add(
Subscription.create(
"my subscription",
realm.where(Frog::class.java)
.equalTo("species", "poison dart")
)
)
}
.waitForInitialRemoteData(
2112,
TimeUnit.MILLISECONDS
)
.build()
Realm.getInstanceAsync(config, object : Realm.Callback() {
override fun onSuccess(realm: Realm) {
Log.v("EXAMPLE", "Successfully opened a realm.")
}
})

您还可以使用SubscriptionSet.waitForSynchronization()SubscriptionSet.waitForSynchronizationAsync()在实例化同步连接后延迟执行,直到订阅同步完成。

此外,您可以使用SubscriptionSet.State枚举查看订阅集的状态。 您可以使用订阅状态执行以下操作:

  • 下载数据时显示进度指示器

  • 了解订阅集何时被取代

您可以使用SubscriptionSet.getState() 访问应用程序订阅集的状态。

注意

订阅状态 Complete(完成)

订阅集状态“完成”并不意味着“同步已完成”或“所有文档已同步”。“完成”意味着发生了以下两件事:

  • 该订阅已成为当前正在与服务器同步的活动订阅集。

  • 在将订阅发送到服务器时与订阅匹配的文档现在位于本地设备上。请注意,这并不一定包括当前与订阅匹配的所有文档。

对于所有与订阅匹配的文档是否已同步到设备,Realm SDK 不提供检查方法。

SUPERSEDED 是另一个线程在订阅集的不同实例上写入订阅时可能发生的SubscriptionSet.State 。 如果状态变为SUPERSEDED ,则必须先获取订阅集的新实例,然后才能写入。

您可以使用SubscriptionSet.update()更新订阅。 在此示例中,我们使用MutableSubscriptionSet.addOrUpdate()更新名为“我的青蛙订阅”的订阅的查询:

realm.getSubscriptions().update(new SubscriptionSet.UpdateCallback() {
@Override
public void update(MutableSubscriptionSet subscriptions) {
// to update a named subscription, create a replacement with
// the same name and add it to the subscription set
subscriptions.addOrUpdate(
Subscription.create("my frog subscription",
realm.where(Frog.class)
.equalTo("name",
"Benedict Cumberburger")));
}
});
realm.subscriptions.update { subscriptions ->
// to update a named subscription, create a replacement with
// the same name and add it to the subscription set
subscriptions.addOrUpdate(
Subscription.create(
"my frog subscription",
realm.where(Frog::class.java)
.equalTo(
"name",
"Benedict Cumberburger"
)
)
)
}

您无法更新在没有名称的情况下创建的订阅。 但是,您可以通过查询查找未命名的订阅,将其从订阅集中删除,然后使用更新的查询添加新订阅:

realm.getSubscriptions().update(new SubscriptionSet.UpdateCallback() {
@Override
public void update(MutableSubscriptionSet subscriptions) {
// to update an unnamed subscription, remove it from the
// subscription set, then add your new query to the set
Subscription mySubscription = subscriptions.find(realm.where(Frog.class)
.equalTo("species",
"cane toad"));
subscriptions.remove(mySubscription);
subscriptions.addOrUpdate(
Subscription.create(
realm.where(Frog.class)
.equalTo("species",
"albino cane toad")));
}
});
realm.subscriptions.update { subscriptions ->
// to update an unnamed subscription, remove it from the
// subscription set, then add your new query to the set
val mySubscription =
subscriptions.find(
realm.where(
Frog::class.java
).equalTo(
"species",
"cane toad"
)
)
subscriptions.remove(mySubscription)
subscriptions.addOrUpdate(
Subscription.create(
realm.where(Frog::class.java)
.equalTo(
"species",
"albino cane toad"
)
)
)
}

要删除订阅,您可以:

  • 删除单个订阅查询

  • 删除特定对象类型的所有订阅

  • 删除所有订阅

当您删除订阅查询时,Realm 会异步删除与客户端设备中的查询匹配的同步数据。

您可以使用MutableSubscriptionSet.remove()删除特定的订阅查询。 您可以按名称查找订阅,然后将返回的订阅传递给remove() ,或将订阅名称直接传递给remove()

realm.getSubscriptions().update(new SubscriptionSet.UpdateCallback() {
@Override
public void update(MutableSubscriptionSet subscriptions) {
Subscription mySubscription = subscriptions.find("mySubscription");
subscriptions.remove(mySubscription);
}
});
realm.subscriptions.update { subscriptions ->
val mySubscription =
subscriptions.find("mySubscription")
subscriptions.remove(mySubscription)
}

如果要删除对特定对象类型的所有订阅,请将一个类传递给removeAll()方法:

realm.getSubscriptions().update(new SubscriptionSet.UpdateCallback() {
@Override
public void update(MutableSubscriptionSet subscriptions) {
subscriptions.removeAll(Frog.class);
}
});
realm.subscriptions.update { subscriptions ->
subscriptions.removeAll(
Frog::class.java
)
}

要从订阅设立删除所有订阅,请使用不带参数的removeAll()

警告

如果删除所有订阅且未添加新订阅,则会出现错误。使用 Flexible Sync 配置打开的 Realm 至少需要一个订阅才能与服务器同步。

realm.getSubscriptions().update(new SubscriptionSet.UpdateCallback() {
@Override
public void update(MutableSubscriptionSet subscriptions) {
subscriptions.removeAll();
}
});
realm.subscriptions.update { subscriptions -> subscriptions.removeAll() }

向应用添加索引可查询字段可以提高对强分区数据进行简单查询的性能。 例如,如果查询将数据强映射到设备、商店或用户的应用(例如user_id == $0, “641374b03725038381d2e1fb” ,则非常适合使用索引可查询字段。 但是,在查询订阅中使用索引化可查询字段有特定的要求:

  • 每个订阅查询中都必须使用索引化可查询字段。查询中不能缺少该字段。

  • 在订阅查询中,索引化可查询字段必须使用 ==IN 进行至少一次针对常量的比较。例如,user_id == $0, "641374b03725038381d2e1fb"store_id IN $0, {1,2,3}

可以选择包含 AND 比较,前提是使用 ==IN 将索引化可查询字段直接与常量进行至少一次比较。例如,store_id IN {1,2,3} AND region=="Northeast"store_id == 1 AND (active_promotions < 5 OR num_employees < 10)

对索引化可查询字段的无效灵活同步查询包括以下情况的查询:

  • 索引化可查询字段未将 AND 与查询的其余部分结合使用。例如,store_id IN {1,2,3} OR region=="Northeast" 是无效的,因为它使用了 OR 而不是 AND。同样,store_id == 1 AND active_promotions < 5 OR num_employees < 10 也是无效的,因为 AND 仅适用于其旁边的词,而不适用于整个查询。

  • 索引化可查询字段未在相等运算符中使用。例如,store_id > 2 AND region=="Northeast" 是无效的,因为它仅将 > 运算符与索引化可查询字段结合使用,而没有相等比较。

  • 查询中完全没有索引化可查询字段。例如,region=="Northeasttruepredicate 都是无效的,因为它们不含索引化可查询字段。

使用 RQL 操作符时,Flexible Sync有一些限制。当您编写确定要同步哪些数据的查询订阅时,服务器不支持这些查询操作符。但是,您仍然可以使用全部的 RQL 功能来查询客户端应用中的同步数据集。

运算符类型
不支持的运算符
聚合操作符
@avg, @count , @max , @min , @sum
查询后缀
DISTINCT, SORT , LIMIT

不区分大小写的查询 ([c]) 无法有效地使用索引。因此,不建议使用不区分大小写的查询,因为它们可能会导致性能问题。

灵活同步仅支持数组字段的 @count

灵活同步支持使用 IN 运算符来查询列表。

可以查询常量列表,查看其中是否包含可查询字段的值:

// Query a constant list for a queryable field value
"priority IN { 1, 2, 3 }"

如果某个可查询字段具有数组值,则可以通过查询确定其中是否包含常量值:

// Query an array-valued queryable field for a constant value
"'comedy' IN genres"

警告

无法在灵活同步查询中相互比较两个列表。请注意,这是灵活同步查询之外的有效 Realm 查询语言语法。

// Invalid Flexible Sync query. Do not do this!
"{'comedy', 'horror', 'suspense'} IN genres"
// Another invalid Flexible Sync query. Do not do this!
"ANY {'comedy', 'horror', 'suspense'} != ANY genres"

灵活同步不支持查询嵌入式对象或链接中的属性。例如,obj1.field == "foo"

订阅设立任何给定查询订阅的大小限制256 kB 。 超过此限制会导致LimitsExceeded 错误。

后退

配置和打开已同步 Realm