diff --git a/README.md b/README.md
index b8e4d36bd0..b3d6ff0bb5 100644
--- a/README.md
+++ b/README.md
@@ -5,8 +5,8 @@
>
-
-
+
+
[](https://search.maven.org/artifact/io.realm/realm-gradle-plugin)
@@ -29,12 +29,14 @@ The [Realm Kotlin SDK](https://github.com/realm/realm-kotlin) is now GA and can
## Getting Started
-Please see the [detailed instructions in our docs](https://www.mongodb.com/docs/atlas/device-sdks/sdk/java/install/) to add Realm to your project.
+Please see the [Quick Start](docs/guides/quick-start-local.md) to add Realm to your project.
## Documentation
-Documentation for Realm can be found at [mongodb.com/docs/atlas/device-sdks/sdk/java/](https://www.mongodb.com/docs/atlas/device-sdks/sdk/java/).
-The API reference is located at [mongodb.com/docs/atlas/device-sdks/sdk/java/api/](https://www.mongodb.com/docs/atlas/device-sdks/sdk/java/api/).
+Documentation for Realm can be found in the [docs/](docs/README.md) directory.
+
+The Javadoc and Kotlin Extensions API Reference docs can be generated
+from source.
## Getting Help
@@ -222,8 +224,6 @@ that you can run `./gradlew :realm:realm-library:compileBaseDebugAndroidTestSour
The `./examples` folder contains many example projects showing how Realm can be used. If this is the first time you checkout or pull a new version of this repository to try the examples, you must call `./gradlew installRealmJava` from the top-level directory first. Otherwise, the examples will not compile as they depend on all Realm artifacts being installed in `mavenLocal()`.
-Standalone examples can be [downloaded from website](https://www.mongodb.com/docs/realm/sdk/java/quick-starts/quick-start-local/#complete-example).
-
## Running Tests on a Device
To run these tests, you must have a device connected to the build computer, and the `adb` command must be in your `PATH`
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000000..2297402101
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,39 @@
+# Realm SDK for Java
+Use the Realm SDK for Java to develop Android apps in Java or Kotlin.
+To develop multiplatform apps using Kotlin Multiplatform (KMP), refer to the
+Kotlin SDK.
+
+## SDK in Maintenance Mode
+This SDK is in best-effort maintenance mode and **no longer receives
+new development or non-critical bug fixes**. To develop your app with new
+features, use the Kotlin SDK. You can use the Java SDK with the Kotlin SDK.
+
+## Develop Apps with the SDK
+Use the SDK's open-source database - Realm - as an object store on the device.
+
+### Install the Java SDK
+Use the Gradle build system to
+install the Java SDK in your project.
+
+### Define an Object Schema
+Use Java or Kotlin to idiomatically define an object schema.
+
+### Open a Database
+The SDK's database - Realm - stores objects in files on your device.
+Or you can open an in-memory database which does not create a file.
+To get started reading and writing data,
+configure and open a database.
+
+### Read and Write Data
+Create, read, update, and
+delete objects from the database.
+Use Android-native queries to filter data.
+
+### React to Changes
+Live objects mean that your data is always up-to-date.
+You can register a notification handler to watch for changes and perform some
+logic, such as updating your UI.
+
+## Examples
+
+See the [examples/](..examples/) directory.
diff --git a/docs/guides/adapters.md b/docs/guides/adapters.md
new file mode 100644
index 0000000000..de56c8a274
--- /dev/null
+++ b/docs/guides/adapters.md
@@ -0,0 +1,460 @@
+# Display Collections - Java SDK
+Android apps often populate the UI using
+[RecyclerView](https://developer.android.com/reference/androidx/recyclerview/widget/RecyclerView.html)
+or [ListView](https://developer.android.com/reference/android/widget/ListView) components.
+Realm offers **adapters** to display realm object
+collections. These collections implement
+the `OrderedRealmCollections` interface. RealmResults
+and RealmList are examples of these adaptors.
+With these adapters, UI components update when your app changes
+Realm objects.
+
+## Install Adapters
+Add these dependencies to your application level `build.gradle` file:
+
+```gradle
+dependencies {
+ implementation 'io.realm:android-adapters:4.0.0'
+ implementation 'androidx.recyclerview:recyclerview:1.1.0'
+}
+```
+
+Realm hosts these adapters on the
+[JCenter](https://mvnrepository.com/repos/jcenter)
+artifact repository. To use `jcenter` in your Android app, add it to your
+project-level `build.gradle` file:
+
+```gradle
+buildscript {
+ repositories {
+ jcenter()
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+```
+
+> Seealso:
+> Source code: [realm/realm-android-adapters](https://github.com/realm/realm-android-adapters) on GitHub.
+>
+
+## Example Models
+The examples on this page use a Realm object named `Item`.
+This class contains a string named "name" and an identifier number named
+"id":
+
+#### Java
+
+```java
+
+import io.realm.RealmObject;
+
+public class Item extends RealmObject {
+ int id;
+ String name;
+
+ public Item() {}
+
+ public int getId() { return id; }
+ public void setId(int id) { this.id = id; }
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+}
+
+```
+
+#### Kotlin
+
+```kotlin
+
+import io.realm.RealmObject
+
+open class Item(var id: Int = 0,
+ var name: String? = null): RealmObject()
+
+```
+
+## Display Collections in a ListView
+Display Realm objects in a
+[ListView](https://developer.android.com/reference/android/widget/ListView) by extending
+[RealmBaseAdapter](https://github.com/realm/realm-android-adapters/blob/master/adapters/src/main/java/io/realm/RealmBaseAdapter.java).
+The adapter uses the `ListAdapter` interface. Implementation works
+like any `ListAdapter`. This provides support for automatically-updating
+Realm objects.
+
+Subclass `RealmBaseAdapter` to display
+Item objects in a `ListView`:
+
+#### Java
+
+```java
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+import com.mongodb.realm.examples.model.java.Item;
+import io.realm.OrderedRealmCollection;
+import io.realm.RealmBaseAdapter;
+
+class ExampleListAdapter extends RealmBaseAdapter implements ListAdapter {
+ String TAG = "REALM_LIST_ADAPTER";
+
+ ExampleListAdapter(OrderedRealmCollection realmResults) {
+ super(realmResults);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ViewHolder viewHolder;
+ if (convertView == null) {
+ Log.i(TAG, "Creating view holder");
+ // create a top-level layout for our item views
+ LinearLayout layout = new LinearLayout(parent.getContext());
+ layout.setLayoutParams(
+ new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+
+ // create a text view to display item names
+ TextView titleView = new TextView(parent.getContext());
+ titleView.setLayoutParams(
+ new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+
+ // attach the text view to the item view layout
+ layout.addView(titleView);
+ convertView = layout;
+ viewHolder = new ViewHolder(titleView);
+ convertView.setTag(viewHolder);
+ } else {
+ viewHolder = (ViewHolder) convertView.getTag();
+ }
+
+ // as long as we
+ if (adapterData != null) {
+ final Item item = adapterData.get(position);
+ viewHolder.title.setText(item.getName());
+ Log.i(TAG, "Populated view holder with data: " + item.getName());
+ } else {
+ Log.e(TAG, "No data in adapter! Failed to populate view holder.");
+ }
+ return convertView;
+ }
+
+ private static class ViewHolder {
+ TextView title;
+
+ public ViewHolder(TextView textView) {
+ title = textView;
+ }
+ }
+}
+
+```
+
+To display list data in an activity, instantiate a `ListView`. Then,
+attach an `ExampleListAdapter`:
+
+```java
+// instantiate a ListView programmatically
+ListView listView = new ListView(activity.getApplicationContext());
+listView.setLayoutParams(
+ new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+
+// create an adapter with a RealmResults collection
+// and attach it to the ListView
+ExampleListAdapter adapter =
+ new ExampleListAdapter(
+ realm.where(Item.class).findAll());
+listView.setAdapter(adapter);
+ViewGroup.LayoutParams layoutParams =
+ new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+activity.addContentView(listView, layoutParams);
+
+```
+
+#### Kotlin
+
+```kotlin
+import android.util.Log
+import android.view.View
+import android.view.ViewGroup
+import android.widget.LinearLayout
+import android.widget.ListAdapter
+import android.widget.TextView
+import com.mongodb.realm.examples.model.kotlin.Item
+import io.realm.OrderedRealmCollection
+import io.realm.RealmBaseAdapter
+
+internal class ExampleListAdapter(realmResults: OrderedRealmCollection?) :
+ RealmBaseAdapter(realmResults), ListAdapter {
+ var TAG = "REALM_LIST_ADAPTER"
+
+ override fun getView(position: Int,
+ convertView: View?,
+ parent: ViewGroup): View {
+ var convertView = convertView
+ val viewHolder: ViewHolder
+ if (convertView == null) {
+ Log.i(TAG, "Creating view holder")
+ // create a top-level layout for our item views
+ val layout = LinearLayout(parent.context)
+ layout.layoutParams = ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
+
+ // create a text view to display item names
+ val titleView = TextView(parent.context)
+ titleView.layoutParams = ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
+
+ // attach the text view to the item view layout
+ layout.addView(titleView)
+ convertView = layout
+ viewHolder = ViewHolder(titleView)
+ convertView.tag = viewHolder
+ } else {
+ viewHolder = convertView.tag as ViewHolder
+ }
+
+ // as long as we
+ if (adapterData != null) {
+ val item = adapterData!![position]!!
+ viewHolder.title.text = item.name
+ Log.i(TAG, "Populated view holder with data: ${item.name}")
+ } else {
+ Log.e(TAG, "No data in adapter! Failed to populate view holder.")
+ }
+ return convertView
+ }
+
+ private class ViewHolder(var title: TextView)
+}
+
+```
+
+To display list data in an activity, instantiate a `ListView`. Then,
+attach an `ExampleListAdapter`:
+
+```kotlin
+// instantiate a ListView programmatically
+val listView = ListView(activity!!.applicationContext)
+listView.layoutParams = ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+)
+
+// create an adapter with a RealmResults collection
+// and attach it to the ListView
+val adapter = ExampleListAdapter(realm.where(Item::class.java).findAll())
+listView.adapter = adapter
+val layoutParams = ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+)
+activity!!.addContentView(listView, layoutParams)
+
+```
+
+## Display Collections in a RecyclerView
+Display Realm objects in a
+[RecyclerView](https://developer.android.com/reference/androidx/recyclerview/widget/RecyclerView.html)
+by extending [RealmRecyclerViewAdapter](https://github.com/realm/realm-android-adapters/blob/master/adapters/src/main/java/io/realm/RealmRecyclerViewAdapter.java).
+The adapter extends `RecyclerView.Adapter`. Implementation works like any
+`RecyclerView` adapter. This provides support
+for automatically-updating Realm objects.
+
+Subclass `RealmRecyclerViewAdapter` to display
+Item objects in a `RecyclerView`:
+
+#### Java
+
+```java
+import android.util.Log;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import androidx.recyclerview.widget.RecyclerView;
+import com.mongodb.realm.examples.model.java.Item;
+import io.realm.OrderedRealmCollection;
+import io.realm.RealmRecyclerViewAdapter;
+
+/*
+ * ExampleRecyclerViewAdapter: extends the Realm-provided
+ * RealmRecyclerViewAdapter to provide data
+ * for a RecyclerView to display
+ * Realm objects on screen to a user.
+ */
+class ExampleRecyclerViewAdapter
+ extends RealmRecyclerViewAdapter {
+ String TAG = "REALM_RECYCLER_ADAPTER";
+
+ ExampleRecyclerViewAdapter(OrderedRealmCollection data) {
+ super(data, true);
+ Log.i(TAG, "Created RealmRecyclerViewAdapter for "
+ + getData().size() + " items.");
+ }
+
+ @Override
+ public ExampleViewHolder onCreateViewHolder(ViewGroup parent,
+ int viewType) {
+ Log.i(TAG, "Creating view holder");
+ TextView textView = new TextView(parent.getContext());
+ textView.setLayoutParams(
+ new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+ return new ExampleViewHolder(textView);
+ }
+
+ @Override
+ public void onBindViewHolder(ExampleViewHolder holder,
+ int position) {
+ final Item obj = getItem(position);
+ Log.i(TAG, "Binding view holder: " + obj.getName());
+ holder.data = obj;
+ holder.title.setText(obj.getName());
+ }
+
+ @Override
+ public long getItemId(int index) {
+ return getItem(index).getId();
+ }
+
+ class ExampleViewHolder extends RecyclerView.ViewHolder {
+ TextView title;
+ public Item data;
+
+ ExampleViewHolder(TextView view) {
+ super(view);
+ title = view;
+ }
+ }
+}
+
+```
+
+To display list data in an activity, instantiate a `RecyclerView`. Then,
+attach an `ExampleRecyclerViewAdapter`:
+
+```java
+// instantiate a RecyclerView programmatically
+RecyclerView recyclerView =
+ new RecyclerView(activity.getApplicationContext());
+recyclerView.setLayoutManager(
+ new LinearLayoutManager(activity.getApplicationContext()));
+recyclerView.setHasFixedSize(true);
+recyclerView.addItemDecoration(new DividerItemDecoration(
+ activity.getApplicationContext(),
+ DividerItemDecoration.VERTICAL));
+
+// create an adapter with a RealmResults collection
+// and attach it to the RecyclerView
+ExampleRecyclerViewAdapter adapter =
+ new ExampleRecyclerViewAdapter(
+ realm.where(Item.class).findAll());
+recyclerView.setAdapter(adapter);
+ViewGroup.LayoutParams layoutParams =
+ new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+activity.addContentView(recyclerView, layoutParams);
+
+```
+
+#### Kotlin
+
+```kotlin
+import android.util.Log
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.mongodb.realm.examples.model.kotlin.Item
+import io.realm.OrderedRealmCollection
+import io.realm.RealmRecyclerViewAdapter
+
+/*
+ * ExampleRecyclerViewAdapter: extends the Realm-provided
+ * RealmRecyclerViewAdapter to provide data
+ * for a RecyclerView to display
+ * Realm objects on screen to a user.
+ */
+internal class ExampleRecyclerViewAdapter(data: OrderedRealmCollection?) :
+ RealmRecyclerViewAdapter(data, true) {
+ var TAG = "REALM_RECYCLER_ADAPTER"
+
+ override fun onCreateViewHolder(parent: ViewGroup,
+ viewType: Int): ExampleViewHolder {
+ Log.i(TAG, "Creating view holder")
+ val textView = TextView(parent.context)
+ textView.layoutParams = ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ )
+ return ExampleViewHolder(textView)
+ }
+
+ override fun onBindViewHolder(holder: ExampleViewHolder, position: Int) {
+ val obj = getItem(position)
+ Log.i(TAG, "Binding view holder: ${obj!!.name}")
+ holder.data = obj
+ holder.title.text = obj.name
+ }
+
+ override fun getItemId(index: Int): Long {
+ return getItem(index)!!.id.toLong()
+ }
+
+ internal inner class ExampleViewHolder(var title: TextView)
+ : RecyclerView.ViewHolder(title) {
+ var data: Item? = null
+ }
+
+ init {
+ Log.i(TAG,
+ "Created RealmRecyclerViewAdapter for ${getData()!!.size} items.")
+ }
+}
+
+```
+
+To display list data in an activity, instantiate a `RecyclerView`. Then,
+attach an `ExampleRecyclerViewAdapter`:
+
+```kotlin
+// instantiate a RecyclerView programmatically
+val recyclerView = RecyclerView(activity!!.applicationContext)
+recyclerView.layoutManager =
+ LinearLayoutManager(activity!!.applicationContext)
+recyclerView.setHasFixedSize(true)
+recyclerView.addItemDecoration(
+ DividerItemDecoration(activity!!.applicationContext,
+ DividerItemDecoration.VERTICAL))
+
+// create an adapter with a RealmResults collection
+// and attach it to the RecyclerView
+val adapter = ExampleRecyclerViewAdapter(realm.where(Item::class.java).findAll())
+recyclerView.adapter = adapter
+val layoutParams = ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+)
+activity!!.addContentView(recyclerView, layoutParams)
+
+```
+
diff --git a/docs/guides/async-api.md b/docs/guides/async-api.md
new file mode 100644
index 0000000000..6a925de5d2
--- /dev/null
+++ b/docs/guides/async-api.md
@@ -0,0 +1,238 @@
+# Asynchronous API - Java SDK
+The Java SDK lets you access network and disk
+resources in two ways: **synchronously** and **asynchronously**. While
+synchronous, or "sync", requests block execution until the request returns
+success or failure, asynchronous, or "async", requests assign a
+callback and proceed execution to the next line of code. When
+the request returns, the callback runs to process results.
+In the callback, you can check if the request executed
+successfully and either access the returned results or the returned
+error.
+
+## Asynchronous Calls
+Asynchronous API requests in the SDK end with the suffix "Async".
+There are several different ways an asynchronous request can behave,
+depending on which part of the SDK you're using.
+
+### Realm.Callback
+Asynchronous calls to open a realm,
+use a final parameter of type `Realm.Callback`. To retrieve returned values after the
+request completes, implement the `onSuccess()` method in the callback
+object passed as the final parameter to these asynchronous methods. You
+should also implement the `onError()` method to handle request failures,
+but it is not required.
+
+#### Java
+
+```java
+Realm.getInstanceAsync(config, new Realm.Callback() {
+ @Override
+ public void onSuccess(@NotNull Realm realm) {
+ Log.v("EXAMPLE", "Successfully fetched realm instance.");
+ }
+ public void onError(Exception e) {
+ Log.e("EXAMPLE", "Failed to get realm instance: " + e);
+ }
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+Realm.getInstanceAsync(config, object : Realm.Callback() {
+ override fun onSuccess(realm: Realm) {
+ Log.v("EXAMPLE", "Successfully fetched realm instance.")
+ }
+
+ fun onError(e: java.lang.Exception) {
+ Log.e("EXAMPLE", "Failed to get realm instance: $e")
+ }
+})
+
+```
+
+### RealmAsyncTask
+Asynchronous calls to execute transactions on a realm return
+an instance of `RealmAsyncTask`. You can optionally specify an error
+handler or a
+success notification for `RealmAsyncTask` by
+passing additional parameters to the asynchronous call. Additionally,
+you use the `cancel()`
+method to stop a transaction from completing. The lambda function passed
+to a `RealmAsyncTask` contains the write operations to include in the
+transaction.
+
+#### Java
+
+```java
+// transaction logic, success notification, error handler all via lambdas
+realm.executeTransactionAsync(transactionRealm -> {
+ Item item = transactionRealm.createObject(Item.class);
+}, () -> {
+ Log.v("EXAMPLE", "Successfully completed the transaction");
+}, error -> {
+ Log.e("EXAMPLE", "Failed the transaction: " + error);
+});
+
+// using class instances for transaction, success, error
+realm.executeTransactionAsync(new Realm.Transaction() {
+ @Override
+ public void execute(Realm transactionRealm) {
+ Item item = transactionRealm.createObject(Item.class);
+ }
+}, new Realm.Transaction.OnSuccess() {
+ @Override
+ public void onSuccess() {
+ Log.v("EXAMPLE", "Successfully completed the transaction");
+ }
+}, new Realm.Transaction.OnError() {
+ @Override
+ public void onError(Throwable error) {
+ Log.e("EXAMPLE", "Failed the transaction: " + error);
+ }
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+// using class instances for transaction, success, error
+realm.executeTransactionAsync(Realm.Transaction { transactionRealm ->
+ val item: Item = transactionRealm.createObject()
+}, Realm.Transaction.OnSuccess {
+ Log.v("EXAMPLE", "Successfully completed the transaction")
+}, Realm.Transaction.OnError { error ->
+ Log.e("EXAMPLE", "Failed the transaction: $error")
+})
+
+// transaction logic, success notification, error handler all via lambdas
+realm.executeTransactionAsync(
+ { transactionRealm ->
+ val item = transactionRealm.createObject()
+ },
+ { Log.v("EXAMPLE", "Successfully completed the transaction") },
+ { error ->
+ Log.e("EXAMPLE", "Failed the transaction: $error")
+ })
+
+```
+
+### RealmResults
+Asynchronous reads from a realm using `findAllAsync()` immediately return an empty
+`[RealmResults` instance. The SDK
+executes the query on a background thread and populates the
+`RealmResults` instance with the results when the query completes. You
+can register a listener with `addChangeListener()`
+to receive a notification when the query completes.
+
+#### Java
+
+```java
+RealmResults items = realm.where(Item.class).findAllAsync();
+// length of items is zero when initially returned
+items.addChangeListener(new RealmChangeListener>() {
+ @Override
+ public void onChange(RealmResults items) {
+ Log.v("EXAMPLE", "Completed the query.");
+ // items results now contains all matched objects (more than zero)
+ }
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+val items = realm.where().findAllAsync()
+// length of items is zero when initially returned
+items.addChangeListener(RealmChangeListener {
+ Log.v("EXAMPLE", "Completed the query.")
+ // items results now contains all matched objects (more than zero)
+})
+
+```
+
+### RealmResultTask
+You can cancel `RealmResultTask` instances just like
+`RealmAsyncTask`. To access the values returned by your query, you
+can use:
+
+- `get()` to
+block until the operation completes
+- `getAsync()`
+to handle the result via an App.Callback
+instance
+
+#### Java
+
+```java
+Document queryFilter = new Document("type", "perennial");
+mongoCollection.findOne(queryFilter).getAsync(task -> {
+ if (task.isSuccess()) {
+ Plant result = task.get();
+ Log.v("EXAMPLE", "successfully found a document: " + result);
+ } else {
+ Log.e("EXAMPLE", "failed to find document with: ", task.getError());
+ }
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+val queryFilter = Document("type", "perennial")
+mongoCollection.findOne(queryFilter)
+ .getAsync { task ->
+ if (task.isSuccess) {
+ val result = task.get()
+ Log.v("EXAMPLE", "successfully found a document: $result")
+ } else {
+ Log.e("EXAMPLE", "failed to find document with: ${task.error}")
+ }
+ }
+
+```
+
+## Coroutines
+The SDK provides a set of Kotlin extensions to request
+asynchronously using coroutines and flows instead of callbacks. You can
+use these extensions to execute transactions, watch for changes, read,
+and write.
+
+```kotlin
+// open a realm asynchronously
+Realm.getInstanceAsync(config, object : Realm.Callback() {
+ override fun onSuccess(realm: Realm) {
+ Log.v("EXAMPLE", "Successfully fetched realm instance")
+
+ CoroutineScope(Dispatchers.Main).launch {
+ // asynchronous transaction
+ realm.executeTransactionAwait(Dispatchers.IO) { transactionRealm: Realm ->
+ if (isActive) {
+ val item = transactionRealm.createObject()
+ }
+ }
+ }
+ // asynchronous query
+ val items: Flow> = realm.where().findAllAsync().toFlow()
+ }
+
+ fun onError(e: Exception) {
+ Log.e("EXAMPLE", "Failed to get realm instance: $e")
+ }
+})
+
+```
+
+> Tip:
+> The `toFlow()` extension method passes frozen Realm objects to safely
+communicate between threads.
+>
+
+> Seealso:
+> The SDK also includes Kotlin extensions that make specifying type
+parameters for Realm reads and writes easier.
+>
diff --git a/docs/guides/crud.md b/docs/guides/crud.md
new file mode 100644
index 0000000000..5268d53319
--- /dev/null
+++ b/docs/guides/crud.md
@@ -0,0 +1,120 @@
+# CRUD - Java SDK
+## Write Operations
+You can **create** objects in a realm,
+**update** objects in a realm, and eventually **delete**
+objects from a realm. Because these operations modify the
+state of the realm, we call them writes.
+
+Realm handles writes in terms of **transactions**. A
+transaction is a list of read and write operations that
+Realm treats as a single indivisible operation. In other
+words, a transaction is *all or nothing*: either all of the
+operations in the transaction succeed or none of the
+operations in the transaction take effect.
+
+> Note:
+> All writes must happen in a transaction.
+>
+
+A realm allows only one open write transaction at a time. Realm
+blocks other writes on other threads until the open
+transaction is complete. Consequently, there is no race
+condition when reading values from the realm within a
+transaction.
+
+When you are done with your transaction, Realm either
+**commits** it or **cancels** it:
+
+- When Realm **commits** a transaction, Realm writes
+all changes to disk.
+- When Realm **cancels** a write transaction or an operation in
+the transaction causes an error, all changes are discarded
+(or "rolled back").
+
+> Tip:
+> Whenever you create, update, or delete a Realm object,
+your changes update the representation of that object in
+Realm and emit
+notifications to any subscribed
+listeners. As a result, you should only write to Realm
+objects when necessary to persist data.
+>
+
+> Important:
+> By default, you can only read or write to a realm in your
+application's UI thread using
+asynchronous transactions. That is,
+you can only use `Realm` methods whose name ends with the word
+`Async` in the main thread of your Android application unless you
+explicitly allow the use of synchronous methods.
+>
+> This restriction exists for the benefit of your application users:
+performing read and write operations on the UI thread can lead to
+unresponsive or slow UI interactions, so it's usually best to handle
+these operations either asynchronously or in a background thread.
+
+## Managed Objects
+**Managed objects** are live Realm objects that update
+based on changes to underlying data in Realm. Managed
+objects can only come from an open realm, and receive updates
+as long as that realm remains open. Managed objects *cannot be passed
+between threads*.
+
+## Unmanaged objects
+**Unmanaged objects** are instances of Realm objects that are
+not live. You can get an unmanaged object by manually constructing a
+Realm object yourself, or by calling
+`[Realm.copyFromRealm()`.
+Unmanaged objects *can be passed between threads*.
+
+## Run a Transaction
+Realm represents each transaction as a callback function
+that contains zero or more read and write operations. To run
+a transaction, define a transaction callback and pass it to
+the realm's `write` method. Within this callback, you are
+free to create, read, update, and delete on the realm. If
+the code in the callback throws an exception when Realm runs
+it, Realm cancels the transaction. Otherwise, Realm commits
+the transaction immediately after the callback.
+
+> Example:
+> The following code shows how to run a transaction with
+`executeTransaction()`
+or `executeTransactionAsync()`.
+If the code in the callback throws an exception, Realm
+cancels the transaction. Otherwise, Realm commits the
+transaction.
+>
+> #### Java
+>
+> ```java
+> realm.executeTransaction(r -> {
+> // Create a turtle enthusiast named Ali.
+> TurtleEnthusiast ali = r.createObject(TurtleEnthusiast.class, new ObjectId());
+> ali.setName("Ali");
+> // Find turtles younger than 2 years old
+> RealmResults hatchlings = r.where(Turtle.class).lessThan("age", 2).findAll();
+> // Give all hatchlings to Ali.
+> hatchlings.setObject("owner", ali);
+> });
+>
+> ```
+>
+>
+> #### Kotlin
+>
+> ```kotlin
+> realm.executeTransaction { r: Realm ->
+> // Create a turtle enthusiast named Ali.
+> val ali = r.createObject(TurtleEnthusiast::class.java, ObjectId())
+> ali.name = "Ali"
+> // Find turtles younger than 2 years old
+> val hatchlings =
+> r.where(Turtle::class.java).lessThan("age", 2).findAll()
+> // Give all hatchlings to Ali.
+> hatchlings.setObject("owner", ali)
+> }
+>
+> ```
+>
+>
diff --git a/docs/guides/crud/create.md b/docs/guides/crud/create.md
new file mode 100644
index 0000000000..6c101149f7
--- /dev/null
+++ b/docs/guides/crud/create.md
@@ -0,0 +1,200 @@
+# CRUD - Create - Java SDK
+## About the Examples on this Page
+The examples on this page use the data model of a project
+management app that has two Realm object types: `Project`
+and `Task`. A `Project` has zero or more `Tasks`.
+
+See the schema for these two classes, `Project` and
+`Task`, below:
+
+#### Java
+
+```java
+
+import org.bson.types.ObjectId;
+
+import io.realm.RealmObject;
+import io.realm.annotations.PrimaryKey;
+import io.realm.annotations.RealmClass;
+import io.realm.annotations.Required;
+
+public class ProjectTask extends RealmObject {
+ @PrimaryKey
+ public ObjectId _id;
+ @Required
+ public String name;
+ public String assignee;
+ public int progressMinutes;
+ public boolean isComplete;
+ public int priority;
+ @Required
+ public String _partition;
+}
+
+```
+
+```java
+
+import org.bson.types.ObjectId;
+
+import io.realm.RealmList;
+import io.realm.RealmObject;
+import io.realm.annotations.PrimaryKey;
+import io.realm.annotations.RealmClass;
+import io.realm.annotations.Required;
+
+public class Project extends RealmObject {
+ @PrimaryKey
+ public ObjectId _id;
+ @Required
+ public String name;
+ public RealmList tasks = new RealmList<>();
+}
+
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+import io.realm.annotations.Required
+import org.bson.types.ObjectId
+
+open class ProjectTask(
+ @PrimaryKey
+ var _id: ObjectId = ObjectId(),
+ @Required
+ var name: String = "",
+ var assignee: String? = null,
+ var progressMinutes: Int = 0,
+ var isComplete: Boolean = false,
+ var priority: Int = 0,
+ var _partition: String = ""
+): RealmObject()
+
+```
+
+```kotlin
+import io.realm.RealmList
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+import io.realm.annotations.Required
+import org.bson.types.ObjectId
+
+open class Project(
+ @PrimaryKey
+ var _id: ObjectId = ObjectId(),
+ @Required
+ var name: String = "",
+ var tasks: RealmList = RealmList(),
+): RealmObject()
+
+```
+
+## Create a New Object
+Use `realm.createObject()`
+in a transaction to create a persistent instance of a Realm object in a
+realm. You can then modify the returned object with other field values
+using accessors and mutators.
+
+The following example demonstrates how to create an object with
+`createObject()`:
+
+#### Java
+
+```java
+realm.executeTransaction(r -> {
+ // Instantiate the class using the factory function.
+ Turtle turtle = r.createObject(Turtle.class, new ObjectId());
+ // Configure the instance.
+ turtle.setName("Max");
+ // Create a TurtleEnthusiast with a primary key.
+ ObjectId primaryKeyValue = new ObjectId();
+ TurtleEnthusiast turtleEnthusiast = r.createObject(TurtleEnthusiast.class, primaryKeyValue);
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+realm.executeTransaction { r: Realm ->
+ // Instantiate the class using the factory function.
+ val turtle = r.createObject(Turtle::class.java, ObjectId())
+ // Configure the instance.
+ turtle.name = "Max"
+ // Create a TurtleEnthusiast with a primary key.
+ val primaryKeyValue = ObjectId()
+ val turtleEnthusiast = r.createObject(
+ TurtleEnthusiast::class.java,
+ primaryKeyValue
+ )
+}
+
+```
+
+You can also insert objects into a realm from JSON. Realm
+supports creating objects from `String`,
+[JSONObject](https://developer.android.com/reference/org/json/JSONObject.html), and
+[InputStream](https://developer.android.com/reference/java/io/InputStream.html) types.
+Realm ignores any properties present in the JSON that are
+not defined in the Realm object schema.
+
+The following example demonstrates how to create a single object from JSON with
+`createObjectFromJson()`
+or multiple objects from JSON with
+`createAllFromJson()`:
+
+#### Java
+
+```java
+// Insert from a string
+realm.executeTransaction(new Realm.Transaction() {
+ @Override
+ public void execute(Realm realm) {
+ realm.createObjectFromJson(Frog.class,
+ "{ name: \"Doctor Cucumber\", age: 1, species: \"bullfrog\", owner: \"Wirt\" }");
+ }
+});
+
+// Insert multiple items using an InputStream
+realm.executeTransaction(new Realm.Transaction() {
+ @Override
+ public void execute(Realm realm) {
+ try {
+ InputStream inputStream = new FileInputStream(
+ new File("path_to_file"));
+ realm.createAllFromJson(Frog.class, inputStream);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+// Insert from a string
+realm.executeTransaction { realm ->
+ realm.createObjectFromJson(
+ Frog::class.java,
+ "{ name: \"Doctor Cucumber\", age: 1, species: \"bullfrog\", owner: \"Wirt\" }"
+ )
+}
+
+// Insert multiple items using an InputStream
+realm.executeTransaction { realm ->
+ try {
+ val inputStream: InputStream =
+ FileInputStream(File("path_to_file"))
+ realm.createAllFromJson(Frog::class.java, inputStream)
+ } catch (e: IOException) {
+ throw RuntimeException(e)
+ }
+}
+
+```
+
diff --git a/docs/guides/crud/delete.md b/docs/guides/crud/delete.md
new file mode 100644
index 0000000000..9d37cac278
--- /dev/null
+++ b/docs/guides/crud/delete.md
@@ -0,0 +1,269 @@
+# CRUD - Delete - Java SDK
+## About the Examples on this Page
+The examples on this page use the data model of a project
+management app that has two Realm object types: `Project`
+and `Task`. A `Project` has zero or more `Tasks`.
+
+See the schema for these two classes, `Project` and
+`Task`, below:
+
+#### Java
+
+```java
+
+import org.bson.types.ObjectId;
+
+import io.realm.RealmObject;
+import io.realm.annotations.PrimaryKey;
+import io.realm.annotations.RealmClass;
+import io.realm.annotations.Required;
+
+public class ProjectTask extends RealmObject {
+ @PrimaryKey
+ public ObjectId _id;
+ @Required
+ public String name;
+ public String assignee;
+ public int progressMinutes;
+ public boolean isComplete;
+ public int priority;
+ @Required
+ public String _partition;
+}
+
+```
+
+```java
+
+import org.bson.types.ObjectId;
+
+import io.realm.RealmList;
+import io.realm.RealmObject;
+import io.realm.annotations.PrimaryKey;
+import io.realm.annotations.RealmClass;
+import io.realm.annotations.Required;
+
+public class Project extends RealmObject {
+ @PrimaryKey
+ public ObjectId _id;
+ @Required
+ public String name;
+ public RealmList tasks = new RealmList<>();
+}
+
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+import io.realm.annotations.Required
+import org.bson.types.ObjectId
+
+open class ProjectTask(
+ @PrimaryKey
+ var _id: ObjectId = ObjectId(),
+ @Required
+ var name: String = "",
+ var assignee: String? = null,
+ var progressMinutes: Int = 0,
+ var isComplete: Boolean = false,
+ var priority: Int = 0,
+ var _partition: String = ""
+): RealmObject()
+
+```
+
+```kotlin
+import io.realm.RealmList
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+import io.realm.annotations.Required
+import org.bson.types.ObjectId
+
+open class Project(
+ @PrimaryKey
+ var _id: ObjectId = ObjectId(),
+ @Required
+ var name: String = "",
+ var tasks: RealmList = RealmList(),
+): RealmObject()
+
+```
+
+## Delete an Object
+To delete an object from a realm, use either the dynamic or static
+versions of the `deleteFromRealm()` method of a `RealmObject` subclass.
+
+The following example shows how to delete one object from
+its realm with `deleteFromRealm()`:
+
+#### Java
+
+```java
+realm.executeTransaction(r -> {
+ // Get a turtle named "Tony".
+ Turtle tony = r.where(Turtle.class).equalTo("name", "Tony").findFirst();
+ tony.deleteFromRealm();
+ // discard the reference
+ tony = null;
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+realm.executeTransaction { r: Realm ->
+ // Get a turtle named "Tony".
+ var tony = r.where(Turtle::class.java)
+ .equalTo("name", "Tony")
+ .findFirst()
+ tony!!.deleteFromRealm()
+ // discard the reference
+ tony = null
+}
+
+```
+
+> Tip:
+> The SDK throws an error if you try to use an object after
+it has been deleted.
+>
+
+## Delete Multiple Objects
+To delete an object from a realm, use the `deleteAllFromRealm()`
+method of the `RealmResults`
+instance that contains the objects you would like to delete. You can
+filter the `RealmResults` down to a subset of objects using the
+`where()` method.
+
+The following example demonstrates how to delete a
+collection from a realm with `deleteAllFromRealm()`:
+
+#### Java
+
+```java
+realm.executeTransaction(r -> {
+ // Find turtles older than 2 years old.
+ RealmResults oldTurtles = r.where(Turtle.class).greaterThan("age", 2).findAll();
+ oldTurtles.deleteAllFromRealm();
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+realm.executeTransaction { r: Realm ->
+ // Find turtles older than 2 years old.
+ val oldTurtles = r.where(Turtle::class.java)
+ .greaterThan("age", 2)
+ .findAll()
+ oldTurtles.deleteAllFromRealm()
+}
+
+```
+
+## Delete an Object and its Dependent Objects
+Sometimes, you have dependent objects that you want to delete when
+you delete the parent object. We call this a **chaining
+delete**. Realm does not delete the dependent
+objects for you. If you do not delete the objects yourself,
+they will remain orphaned in your realm. Whether or not
+this is a problem depends on your application's needs.
+
+Currently, the best way to delete dependent objects is to
+iterate through the dependencies and delete them before
+deleting the parent object.
+
+The following example demonstrates how to perform a
+chaining delete by first deleting all of Ali's turtles,
+then deleting Ali:
+
+#### Java
+
+```java
+realm.executeTransaction(r -> {
+ // Find a turtle enthusiast named "Ali"
+ TurtleEnthusiast ali = r.where(TurtleEnthusiast.class).equalTo("name", "Ali").findFirst();
+ // Delete all of ali's turtles
+ ali.getTurtles().deleteAllFromRealm();
+ ali.deleteFromRealm();
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+realm.executeTransaction { r: Realm ->
+ // Find a turtle enthusiast named "Ali"
+ val ali = r.where(TurtleEnthusiast::class.java)
+ .equalTo("name", "Ali").findFirst()
+ // Delete all of ali's turtles
+ ali!!.turtles!!.deleteAllFromRealm()
+ ali.deleteFromRealm()
+}
+
+```
+
+## Delete All Objects of a Specific Type
+Realm supports deleting all instances of a Realm type from a realm.
+
+The following example demonstrates how to delete all
+Turtle instances from a realm with `delete()`:
+
+#### Java
+
+```java
+realm.executeTransaction(r -> {
+ r.delete(Turtle.class);
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+realm.executeTransaction { r: Realm ->
+ r.delete(Turtle::class.java)
+}
+
+```
+
+## Delete All Objects in a Realm
+It is possible to delete all objects from the realm. This
+does not affect the schema of the realm. This is useful for
+quickly clearing out your realm while prototyping.
+
+The following example demonstrates how to delete everything
+from a realm with `deleteAll()`:
+
+#### Java
+
+```java
+realm.executeTransaction(r -> {
+ r.deleteAll();
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+realm.executeTransaction { r: Realm ->
+ r.deleteAll()
+}
+
+```
+
+## Delete an Object Using an Iterator
+Because realm collections always reflect the latest state, they
+can appear, disappear, or change while you iterate over a collection.
+To get a stable collection you can iterate over, you can create a
+**snapshot** of a collection's data. A snapshot guarantees the order of
+elements will not change, even if an element is deleted.
+
+For an example, refer to Iteration.
diff --git a/docs/guides/crud/filter-data.md b/docs/guides/crud/filter-data.md
new file mode 100644
index 0000000000..fd883d226f
--- /dev/null
+++ b/docs/guides/crud/filter-data.md
@@ -0,0 +1,803 @@
+# Filter Data - Java SDK
+## Query Engine
+To filter data in your realm, use the Realm query engine.
+
+There are two ways to access the query engine with the Java SDK:
+
+- Fluent interface
+- Realm Query Language
+
+## Fluent Interface
+The Java SDK uses a [Fluent interface](https://en.wikipedia.org/wiki/Fluent_interface)
+to construct multi-clause queries that are passed to the query engine.
+
+See RealmQuery API
+for a complete list of available methods.
+
+There are several types of operators available to filter a
+Realm collection.
+Filters work by **evaluating** an operator expression for
+every object in the collection being
+filtered. If the expression resolves to `true`, Realm
+Database includes the object in the results collection.
+
+An **expression** consists of one of the following:
+
+- The name of a property of the object currently being evaluated.
+- An operator and up to two argument expression(s).
+- A literal string, number, or date.
+
+### About the Examples In This Section
+The examples in this section use a simple data set for a
+task list app. The two Realm object types are `Project`
+and `Task`. A `Task` has a name, assignee's name, and
+completed flag. There is also an arbitrary number for
+priority (higher is more important) and a count of
+minutes spent working on it. A `Project` has zero or more
+`Tasks`.
+
+See the schema for these two classes, `Project` and
+`Task`, below:
+
+#### Java
+
+```java
+
+import org.bson.types.ObjectId;
+
+import io.realm.RealmObject;
+import io.realm.annotations.PrimaryKey;
+import io.realm.annotations.RealmClass;
+import io.realm.annotations.Required;
+
+public class ProjectTask extends RealmObject {
+ @PrimaryKey
+ public ObjectId _id;
+ @Required
+ public String name;
+ public String assignee;
+ public int progressMinutes;
+ public boolean isComplete;
+ public int priority;
+ @Required
+ public String _partition;
+}
+
+```
+
+```java
+
+import org.bson.types.ObjectId;
+
+import io.realm.RealmList;
+import io.realm.RealmObject;
+import io.realm.annotations.PrimaryKey;
+import io.realm.annotations.RealmClass;
+import io.realm.annotations.Required;
+
+public class Project extends RealmObject {
+ @PrimaryKey
+ public ObjectId _id;
+ @Required
+ public String name;
+ public RealmList tasks = new RealmList<>();
+}
+
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+import io.realm.annotations.Required
+import org.bson.types.ObjectId
+
+open class ProjectTask(
+ @PrimaryKey
+ var _id: ObjectId = ObjectId(),
+ @Required
+ var name: String = "",
+ var assignee: String? = null,
+ var progressMinutes: Int = 0,
+ var isComplete: Boolean = false,
+ var priority: Int = 0,
+ var _partition: String = ""
+): RealmObject()
+
+```
+
+```kotlin
+import io.realm.RealmList
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+import io.realm.annotations.Required
+import org.bson.types.ObjectId
+
+open class Project(
+ @PrimaryKey
+ var _id: ObjectId = ObjectId(),
+ @Required
+ var name: String = "",
+ var tasks: RealmList = RealmList(),
+): RealmObject()
+
+```
+
+### Comparison Operators
+The most straightforward operation in a search is to compare
+values.
+
+|Operator|Description|
+| --- | --- |
+|`between`|Evaluates to `true` if the left-hand numerical or date expression is between or equal to the right-hand range. For dates, this evaluates to `true` if the left-hand date is within the right-hand date range.|
+|equalTo|Evaluates to `true` if the left-hand expression is equal to the right-hand expression.|
+|greaterThan|Evaluates to `true` if the left-hand numerical or date expression is greater than the right-hand numerical or date expression. For dates, this evaluates to `true` if the left-hand date is later than the right-hand date.|
+|greaterThanOrEqualTo|Evaluates to `true` if the left-hand numerical or date expression is greater than or equal to the right-hand numerical or date expression. For dates, this evaluates to `true` if the left-hand date is later than or the same as the right-hand date.|
+|`in`|Evaluates to `true` if the left-hand expression is in the right-hand list.|
+|lessThan|Evaluates to `true` if the left-hand numerical or date expression is less than the right-hand numerical or date expression. For dates, this evaluates to `true` if the left-hand date is earlier than the right-hand date.|
+|lessThanOrEqualTo|Evaluates to `true` if the left-hand numeric expression is less than or equal to the right-hand numeric expression. For dates, this evaluates to `true` if the left-hand date is earlier than or the same as the right-hand date.|
+|notEqualTo|Evaluates to `true` if the left-hand expression is not equal to the right-hand expression.|
+
+> Example:
+> The following example uses the query engine's
+comparison operators to:
+>
+> - Find high priority tasks by comparing the value of the `priority` property value with a threshold number, above which priority can be considered high.
+> - Find just-started or short-running tasks by seeing if the `progressMinutes` property falls within a certain range.
+> - Find unassigned tasks by finding tasks where the `assignee` property is equal to `null`.
+> - Find tasks assigned to specific teammates Ali or Jamie by seeing if the `assignee` property is in a list of names.
+>
+> #### Java
+>
+> ```java
+> RealmQuery tasksQuery = realm.where(ProjectTask.class);
+> Log.i("EXAMPLE", "High priority tasks: " + tasksQuery.greaterThan("priority", 5).count());
+> Log.i("EXAMPLE", "Just-started or short tasks: " + tasksQuery.between("progressMinutes", 1, 10).count());
+> Log.i("EXAMPLE", "Unassigned tasks: " + tasksQuery.isNull("assignee").count());
+> Log.i("EXAMPLE", "Ali or Jamie's tasks: " + tasksQuery.in("assignee", new String[]{"Ali", "Jamie"}).count());
+>
+> ```
+>
+>
+> #### Kotlin
+>
+> ```kotlin
+> val tasksQuery = realm.where(ProjectTask::class.java)
+> Log.i("EXAMPLE", "High priority tasks: " + tasksQuery.greaterThan("priority", 5).count())
+> Log.i("EXAMPLE", "Just-started or short tasks: " + tasksQuery.between("progressMinutes", 1, 10).count())
+> Log.i("EXAMPLE", "Unassigned tasks: " + tasksQuery.isNull("assignee").count())
+> Log.i("EXAMPLE", "Ali or Jamie's tasks: " + tasksQuery.`in`("assignee", arrayOf("Ali", "Jamie")).count())
+>
+> ```
+>
+>
+
+### Logical Operators
+You can make compound predicates using logical operators.
+
+|Operator|Description|
+| --- | --- |
+|and|Evaluates to `true` if both left-hand and right-hand expressions are `true`.|
+|not|Negates the result of the given expression.|
+|or|Evaluates to `true` if either expression returns `true`.|
+
+> Example:
+> We can use the query language's logical operators to find
+all of Ali's completed tasks. That is, we find all tasks
+where the `assignee` property value is equal to 'Ali' AND
+the `isComplete` property value is `true`:
+>
+> #### Java
+>
+> ```java
+> RealmQuery tasksQuery = realm.where(ProjectTask.class);
+> Log.i("EXAMPLE", "Ali has completed " +
+> tasksQuery.equalTo("assignee", "Ali").and().equalTo("isComplete", true).findAll().size() +
+> " tasks.");
+>
+> ```
+>
+>
+> #### Kotlin
+>
+> ```kotlin
+> val tasksQuery = realm.where(ProjectTask::class.java)
+> Log.i("EXAMPLE", "Ali has completed " +
+> tasksQuery.equalTo("assignee", "Ali").and()
+> .equalTo("isComplete", true).findAll().size + " tasks.")
+>
+> ```
+>
+>
+
+### String Operators
+You can compare string values using these string operators.
+Regex-like wildcards allow more flexibility in search.
+
+|Operator|Description|
+| --- | --- |
+|beginsWith|Evaluates to `true` if the left-hand string expression begins with the right-hand string expression. This is similar to `contains`, but only matches if the left-hand string expression is found at the beginning of the right-hand string expression.|
+|`contains`|Evaluates to `true` if the left-hand string expression is found anywhere in the right-hand string expression.|
+|endsWith|Evaluates to `true` if the left-hand string expression ends with the right-hand string expression. This is similar to `contains`, but only matches if the left-hand string expression is found at the very end of the right-hand string expression.|
+|like|Evaluates to `true` if the left-hand string expression matches the right-hand string wildcard string expression. A wildcard string expression is a string that uses normal characters with two special wildcard characters: The `*` wildcard matches zero or more of any character The `?` wildcard matches any character. For example, the wildcard string "d?g" matches "dog", "dig", and "dug", but not "ding", "dg", or "a dog".|
+|equalTo|Evaluates to `true` if the left-hand string is lexicographically equal to the right-hand string.|
+
+> Example:
+> We use the query engine's string operators to find
+projects with a name starting with the letter 'e' and
+projects with names that contain 'ie':
+>
+> #### Java
+>
+> ```java
+> RealmQuery projectsQuery = realm.where(Project.class);
+> // Pass Case.INSENSITIVE as the third argument for case insensitivity.
+> Log.i("EXAMPLE", "Projects that start with 'e': "
+> + projectsQuery.beginsWith("name", "e", Case.INSENSITIVE).count());
+> Log.i("EXAMPLE", "Projects that contain 'ie': "
+> + projectsQuery.contains("name", "ie").count());
+>
+> ```
+>
+>
+> #### Kotlin
+>
+> ```kotlin
+> val projectsQuery = realm.where(Project::class.java)
+> // Pass Case.INSENSITIVE as the third argument for case insensitivity.
+> Log.i("EXAMPLE", "Projects that start with 'e': "
+> + projectsQuery.beginsWith("name", "e", Case.INSENSITIVE).count())
+> Log.i("EXAMPLE", "Projects that contain 'ie': "
+> + projectsQuery.contains("name", "ie").count())
+>
+> ```
+>
+>
+
+> Note:
+> Case-insensitive string operators only support the
+`Latin Basic`, `Latin Supplement`, `Latin Extended A`, and
+`Latin Extended B (UTF-8 range 0-591)` character sets. Setting
+the case insensitive flag in queries when using `equalTo`,
+`notEqualTo`, `contains`, `endsWith`, `beginsWith`, or
+`like` only works on English locale characters.
+>
+
+### Aggregate Operators
+You can apply an aggregate operator to a collection property
+of a Realm object. Aggregate operators traverse a
+collection and reduce it
+to a single value.
+
+|Operator|Description|
+| --- | --- |
+|average|Evaluates to the average value of a given numerical property across a collection.|
+|count|Evaluates to the number of objects in the given collection.|
+|max|Evaluates to the highest value of a given numerical property across a collection.|
+|min|Evaluates to the lowest value of a given numerical property across a collection.|
+|sum|Evaluates to the sum of a given numerical property across a collection.|
+
+> Example:
+> We create a couple of filters to show different facets of
+the data:
+>
+> - Projects with average tasks priority above 5.
+> - Long running projects.
+>
+> #### Java
+>
+> ```java
+> RealmQuery tasksQuery = realm.where(ProjectTask.class);
+> /*
+> Aggregate operators do not support dot-notation, so you
+> cannot directly operate on a property of all of the objects
+> in a collection property.
+>
+> You can operate on a numeric property of the top-level
+> object, however:
+> */
+> Log.i("EXAMPLE", "Tasks average priority: " + tasksQuery.average("priority"));
+>
+> ```
+>
+>
+> #### Kotlin
+>
+> ```kotlin
+> val tasksQuery = realm.where(ProjectTask::class.java)
+> /*
+> Aggregate operators do not support dot-notation, so you
+> cannot directly operate on a property of all of the objects
+> in a collection property.
+>
+> You can operate on a numeric property of the top-level
+> object, however:
+> */Log.i("EXAMPLE", "Tasks average priority: " + tasksQuery.average("priority"))
+>
+> ```
+>
+>
+
+## Filter, Sort, Limit, Unique, and Chain Queries
+### About the Examples in This Section
+The examples in this section use two Realm object types: `Teacher`
+and `Student`.
+
+See the schema for these two classes below:
+
+#### Java
+
+```java
+import io.realm.RealmList;
+import io.realm.RealmObject;
+
+public class Teacher extends RealmObject {
+ private String name;
+ private Integer numYearsTeaching;
+ private String subject;
+ private RealmList students;
+ public Teacher() {}
+
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public Integer getNumYearsTeaching() { return numYearsTeaching; }
+ public void setNumYearsTeaching(Integer numYearsTeaching) { this.numYearsTeaching = numYearsTeaching; }
+ public String getSubject() { return subject; }
+ public void setSubject(String subject) { this.subject = subject; }
+ public RealmList getStudents() { return students; }
+ public void setStudents(RealmList students) { this.students = students; }
+}
+
+```
+
+```java
+import io.realm.RealmObject;
+import io.realm.RealmResults;
+import io.realm.annotations.LinkingObjects;
+
+public class Student extends RealmObject {
+ private String name;
+ private Integer year;
+ @LinkingObjects("students")
+ private final RealmResults teacher = null;
+ public Student() {}
+
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public Integer getYear() { return year; }
+ public void setYear(Integer year) { this.year = year; }
+ public RealmResults getTeacher() { return teacher; }
+}
+
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmList
+import io.realm.RealmObject
+
+open class Teacher : RealmObject() {
+ var name: String? = null
+ var numYearsTeaching: Int? = null
+ var subject: String? = null
+ var students: RealmList? = null
+}
+
+```
+
+```kotlin
+import io.realm.RealmObject
+import io.realm.RealmResults
+import io.realm.annotations.LinkingObjects
+
+open class Student : RealmObject() {
+ var name: String? = null
+ var year: Int? = null
+
+ @LinkingObjects("students")
+ val teacher: RealmResults? = null
+}
+
+```
+
+### Filters
+You can build filters using the operator methods of the
+[fluent interface](https://en.wikipedia.org/wiki/Fluent_interface) exposed by the
+`RealmQuery` class:
+
+#### Java
+
+```java
+// Build the query looking at all teachers:
+RealmQuery query = realm.where(Teacher.class);
+
+// Add query conditions:
+query.equalTo("name", "Ms. Langtree");
+query.or().equalTo("name", "Mrs. Jacobs");
+
+// Execute the query:
+RealmResults result1 = query.findAll();
+
+// Or alternatively do the same all at once (the "Fluent interface"):
+RealmResults result2 = realm.where(Teacher.class)
+ .equalTo("name", "Ms. Langtree")
+ .or()
+ .equalTo("name", "Mrs. Jacobs")
+ .findAll();
+
+```
+
+#### Kotlin
+
+```kotlin
+// Build the query looking at all teachers:
+val query = realm.where(Teacher::class.java)
+
+// Add query conditions:
+query.equalTo("name", "Ms. Langtree")
+query.or().equalTo("name", "Mrs. Jacobs")
+
+// Execute the query:
+val result1 = query.findAll()
+
+// Or alternatively do the same all at once (the "Fluent interface"):
+val result2 = realm.where(Teacher::class.java)
+ .equalTo("name", "Ms. Langtree")
+ .or()
+ .equalTo("name", "Mrs. Jacobs")
+ .findAll()
+
+```
+
+This gives you a new instance of the class `RealmResults`,
+containing teachers with the name "Ms. Langtree" or "Mrs. Jacobs".
+
+`RealmQuery` includes several methods that can execute queries:
+
+- `findAll()` blocks until
+it finds all objects that meet the query conditions
+- `findAllAsync()`
+returns immediately and finds all objects that meet the query
+conditions asynchronously on a background thread
+- `findFirst()` blocks
+until it finds the first object that meets the query conditions
+- `findFirstAsync()`
+returns immediately and finds the first object that meets the query
+conditions asynchronously on a background thread
+
+Queries return a list of references to the matching Realm
+objects using the RealmResults type.
+
+#### Link Queries
+When referring to an object property, you can use **dot notation** to refer
+to child properties of that object. You can refer to the properties of
+embedded objects and relationships with dot notation.
+
+For example, consider a query for all teachers with a student named
+"Wirt" or "Greg":
+
+#### Java
+
+```java
+// Find all teachers who have students with the names "Wirt" or "Greg"
+RealmResults result = realm.where(Teacher.class)
+ .equalTo("students.name", "Wirt")
+ .or()
+ .equalTo("students.name", "Greg")
+ .findAll();
+
+```
+
+#### Kotlin
+
+```kotlin
+// Find all teachers who have students with the names "Wirt" or "Greg"
+val result = realm.where(Teacher::class.java)
+ .equalTo("students.name", "Wirt")
+ .or()
+ .equalTo("students.name", "Greg")
+ .findAll()
+
+```
+
+You can even use dot notation to query inverse relationships:
+
+#### Java
+
+```java
+// Find all students who have teachers with the names "Ms. Langtree" or "Mrs. Jacobs"
+RealmResults result = realm.where(Student.class)
+ .equalTo("teacher.name", "Ms. Langtree")
+ .or()
+ .equalTo("teacher.name", "Mrs. Jacobs")
+ .findAll();
+
+```
+
+#### Kotlin
+
+```kotlin
+// Find all students who have teachers with the names "Ms. Langtree" or "Mrs. Jacobs"
+val result = realm.where(Student::class.java)
+ .equalTo("teacher.name", "Ms. Langtree")
+ .or()
+ .equalTo("teacher.name", "Mrs. Jacobs")
+ .findAll()
+
+```
+
+### Sort Results
+> Important:
+> Realm applies the `distinct()`, `sort()` and
+`limit()` methods in the order you specify. Depending on the
+data set this can alter the query result. Generally, you should
+apply `limit()` last to avoid unintended result sets.
+>
+
+You can define the order of query results using the
+`sort()`
+method:
+
+#### Java
+
+```java
+// Find all students in year 7, and sort them by name
+RealmResults result = realm.where(Student.class)
+ .equalTo("year", 7)
+ .sort("name")
+ .findAll();
+
+// Alternatively, find all students in year 7
+RealmResults unsortedResult = realm.where(Student.class)
+ .equalTo("year", 7)
+ .findAll();
+// then sort the results set by name
+RealmResults sortedResult = unsortedResult.sort("name");
+
+```
+
+#### Kotlin
+
+```kotlin
+// Find all students in year 7, and sort them by name
+val result: RealmResults = realm.where(Student::class.java)
+ .equalTo("year", 7L)
+ .sort("name")
+ .findAll()
+
+// Alternatively, find all students in year 7
+val unsortedResult: RealmResults = realm.where(Student::class.java)
+ .equalTo("year", 7L)
+ .findAll()
+// then sort the results set by name
+val sortedResult = unsortedResult.sort("name")
+
+```
+
+Sorts organize results in ascending order by default. To organize results
+in descending order, pass `Sort.DESCENDING` as a second argument.
+You can resolve sort order ties between identical property values
+by passing an array of properties instead of a single property: in the
+event of a tie, Realm sorts the tied objects by subsequent
+properties in order.
+
+> Note:
+> Realm uses non-standard sorting for upper and lowercase
+letters, sorting them together rather than sorting uppercase first.
+As a result, `'- !"#0&()*,./:;?_+<=>123aAbBcC...xXyYzZ` is the
+actual sorting order in Realm. Additionally, sorting
+strings only supports the `Latin Basic`, `Latin Supplement`,
+`Latin Extended A`, and `Latin Extended B (UTF-8 range 0–591)`
+character sets.
+>
+
+### Limit Results
+You can cap the number of query results to a specific maximum number
+using the `limit()`
+method:
+
+#### Java
+
+```java
+// Find all students in year 8, and limit the results collection to 10 items
+RealmResults result = realm.where(Student.class)
+ .equalTo("year", 8)
+ .limit(10)
+ .findAll();
+
+```
+
+#### Kotlin
+
+```kotlin
+// Find all students in year 8, and limit the results collection to 10 items
+val result: RealmResults = realm.where(Student::class.java)
+ .equalTo("year", 8L)
+ .limit(10)
+ .findAll()
+
+```
+
+Limited result collections automatically update like any other query
+result. Consequently, objects might drop out of the collection as
+underlying data changes.
+
+> Tip:
+> Some databases encourage paginating results with limits to avoid
+reading unnecessary data from disk or using too much memory.
+>
+> Since Realm queries are lazy, there is no need to
+take such measures. Realm only loads objects from query
+results when they are explicitly accessed.
+>
+
+> Tip:
+> Collection notifications
+report objects as deleted when they drop out of the result set.
+This does not necessarily mean that they have been deleted from the
+underlying realm, just that they are no longer part of the
+query result.
+>
+
+### Unique Results
+You can reduce query results to unique values for a given field or fields
+using the `distinct()` method:
+
+#### Java
+
+```java
+// Find all students in year 9, and cap the result collection at 10 items
+RealmResults result = realm.where(Student.class)
+ .equalTo("year", 9)
+ .distinct("name")
+ .findAll();
+
+```
+
+#### Kotlin
+
+```kotlin
+// Find all students in year 9, and cap the result collection at 10 items
+val result: RealmResults = realm.where(Student::class.java)
+ .equalTo("year", 9L)
+ .distinct("name")
+ .findAll()
+
+```
+
+You can only call `distinct()` on integer, long, short, and `String`
+fields; other field types will throw an exception. As with sorting,
+you can specify multiple fields to resolve ties.
+
+### Chain Queries
+You can apply additional filters to a results collection by calling the
+`where()` method:
+
+#### Java
+
+```java
+// Find all students in year 9 and resolve the query into a results collection
+RealmResults result = realm.where(Student.class)
+ .equalTo("year", 9)
+ .findAll();
+
+// filter the students results again by teacher name
+RealmResults filteredResults = result.where().equalTo("teacher.name", "Ms. Langtree").findAll();
+
+```
+
+#### Kotlin
+
+```kotlin
+// Find all students in year 9 and resolve the query into a results collection
+val result: RealmResults = realm.where(Student::class.java)
+ .equalTo("year", 9L)
+ .findAll()
+
+// filter the students results again by teacher name
+val filteredResults =
+ result.where().equalTo("teacher.name", "Ms. Langtree").findAll()
+
+```
+
+The `where()` method returns a `RealmQuery` that you can resolve into
+a `RealmResults` using a `find` method. Filtered results can only
+return objects of the same type as the original results set, but are
+otherwise able to use any filters.
+
+## Query with Realm Query Language
+> Version added: 10.4.0
+
+You can also query realms using Realm Query Language, a string-based
+query language to constrain searches when retrieving objects from a realm.
+
+You can use `RealmQuery.rawPredicate()`.
+For more information about syntax, usage and limitations,
+refer to the Realm Query Language reference.
+
+Realm Query Language can use either the class and property names defined
+in your Realm Model classes or the internal names defined with `@RealmField`.
+You can combine raw predicates with other raw predicates or type-safe
+predicates created with `RealmQuery`:
+
+#### Java
+
+```java
+// Build a RealmQuery based on the Student type
+RealmQuery query = realm.where(Student.class);
+
+// Simple query
+RealmResults studentsNamedJane =
+ query.rawPredicate("name = 'Jane'").findAll();
+
+// Multiple predicates
+RealmResults studentsNamedJaneOrJohn =
+ query.rawPredicate("name = 'Jane' OR name = 'John'").findAll();
+
+// Collection queries
+RealmResults studentsWithTeachers =
+ query.rawPredicate("teacher.@count > 0").findAll();
+RealmResults studentsWithSeniorTeachers =
+ query.rawPredicate("ALL teacher.numYearsTeaching > 5").findAll();
+
+// Sub queries
+RealmResults studentsWithMathTeachersNamedSteven =
+ query.rawPredicate("SUBQUERY(teacher, $teacher, $teacher.subject = 'Mathematics' AND $teacher.name = 'Mr. Stevens').@count > 0").findAll();
+
+// Sort, Distinct, Limit
+RealmResults students =
+ query.rawPredicate("teacher.@count > 0 SORT(year ASCENDING) DISTINCT(name) LIMIT(5)").findAll();
+
+// Combine two raw predicates
+RealmResults studentsNamedJaneOrHenry =
+ query.rawPredicate("name = 'Jane'")
+ .rawPredicate("name = 'Henry'").findAll();
+
+// Combine raw predicate with type-safe predicate
+RealmResults studentsNamedJaneOrHenryAgain =
+ query.rawPredicate("name = 'Jane'")
+ .equalTo("name", "Henry").findAll();
+
+```
+
+#### Kotlin
+
+```kotlin
+// Build a RealmQuery based on the Student type
+val query = realm.where(Student::class.java)
+
+// Simple query
+val studentsNamedJane = query.rawPredicate("name = 'Jane'").findAll()
+
+// Multiple predicates
+val studentsNamedJaneOrJohn =
+ query.rawPredicate("name = 'Jane' OR name = 'John'").findAll()
+
+// Collection queries
+val studentsWithTeachers =
+ query.rawPredicate("teacher.@count > 0").findAll()
+val studentsWithSeniorTeachers =
+ query.rawPredicate("ALL teacher.numYearsTeaching > 5").findAll()
+
+// Sub queries
+val studentsWithMathTeachersNamedSteven =
+ query.rawPredicate("SUBQUERY(teacher, \$teacher, \$teacher.subject = 'Mathematics' AND \$teacher.name = 'Mr. Stevens').@count > 0")
+ .findAll()
+
+// Sort, Distinct, Limit
+val students =
+ query.rawPredicate("teacher.@count > 0 SORT(year ASCENDING) DISTINCT(name) LIMIT(5)")
+ .findAll()
+
+// Combine two raw predicates
+val studentsNamedJaneOrHenry = query.rawPredicate("name = 'Jane'")
+ .rawPredicate("name = 'Henry'").findAll()
+
+// Combine raw predicate with type-safe predicate
+val studentsNamedJaneOrHenryAgain =
+ query.rawPredicate("name = 'Jane'")
+ .equalTo("name", "Henry").findAll()
+
+```
diff --git a/docs/guides/crud/read.md b/docs/guides/crud/read.md
new file mode 100644
index 0000000000..1abd26b178
--- /dev/null
+++ b/docs/guides/crud/read.md
@@ -0,0 +1,608 @@
+# CRUD - Read - Java SDK
+## Read Operations
+You can read back the data that you have
+stored in Realm.
+The standard data access pattern across Realm
+SDKs is to find, filter, and sort objects, in that order. To
+get the best performance from Realm as your app grows and
+your queries become more complex, design your app's data
+access patterns around a solid understanding of Realm
+read characteristics.
+
+### Read Characteristics
+When you design your app's data access patterns around the
+following three key characteristics of reads in Realm,
+you can be confident you are reading data as
+efficiently as possible.
+
+### Results Are Not Copies
+Results to a query are not copies of your data: modifying
+the results of a query will modify the data on disk
+directly. This memory mapping also means that results are
+**live**: that is, they always reflect the current state on
+disk.
+
+### Results Are Lazy
+Realm defers execution of a query until you access the
+results. You can chain several filter and sort operations
+without requiring extra work to process the intermediate
+state.
+
+### References Are Retained
+One benefit of Realm's object model is that
+Realm automatically retains all of an object's
+relationships as
+direct references, so you can traverse your graph of
+relationships directly through the results of a query.
+
+A **direct reference**, or pointer, allows you to access a
+related object's properties directly through the reference.
+
+Other databases typically copy objects from database storage
+into application memory when you need to work with them
+directly. Because application objects contain direct
+references, you are left with a choice: copy the object
+referred to by each direct reference out of the database in
+case it's needed, or just copy the foreign key for each
+object and query for the object with that key if it's
+accessed. If you choose to copy referenced objects into
+application memory, you can use up a lot of resources for
+objects that are never accessed, but if you choose to only
+copy the foreign key, referenced object lookups can cause
+your application to slow down.
+
+Realm bypasses all of this using zero-copy
+live objects. Realm object accessors point directly into
+database storage using memory mapping, so there is no distinction
+between the objects in Realm and the results of your query in
+application memory. Because of this, you can traverse direct references
+across an entire realm from any query result.
+
+## About the Examples on this Page
+The examples on this page use the data model of a project
+management app that has two Realm object types: `Project`
+and `Task`. A `Project` has zero or more `Tasks`.
+
+See the schema for these two classes, `Project` and
+`Task`, below:
+
+#### Java
+
+```java
+
+import org.bson.types.ObjectId;
+
+import io.realm.RealmObject;
+import io.realm.annotations.PrimaryKey;
+import io.realm.annotations.RealmClass;
+import io.realm.annotations.Required;
+
+public class ProjectTask extends RealmObject {
+ @PrimaryKey
+ public ObjectId _id;
+ @Required
+ public String name;
+ public String assignee;
+ public int progressMinutes;
+ public boolean isComplete;
+ public int priority;
+ @Required
+ public String _partition;
+}
+
+```
+
+```java
+
+import org.bson.types.ObjectId;
+
+import io.realm.RealmList;
+import io.realm.RealmObject;
+import io.realm.annotations.PrimaryKey;
+import io.realm.annotations.RealmClass;
+import io.realm.annotations.Required;
+
+public class Project extends RealmObject {
+ @PrimaryKey
+ public ObjectId _id;
+ @Required
+ public String name;
+ public RealmList tasks = new RealmList<>();
+}
+
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+import io.realm.annotations.Required
+import org.bson.types.ObjectId
+
+open class ProjectTask(
+ @PrimaryKey
+ var _id: ObjectId = ObjectId(),
+ @Required
+ var name: String = "",
+ var assignee: String? = null,
+ var progressMinutes: Int = 0,
+ var isComplete: Boolean = false,
+ var priority: Int = 0,
+ var _partition: String = ""
+): RealmObject()
+
+```
+
+```kotlin
+import io.realm.RealmList
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+import io.realm.annotations.Required
+import org.bson.types.ObjectId
+
+open class Project(
+ @PrimaryKey
+ var _id: ObjectId = ObjectId(),
+ @Required
+ var name: String = "",
+ var tasks: RealmList = RealmList(),
+): RealmObject()
+
+```
+
+## Read from Realm
+A read from a realm generally consists of the following
+steps:
+
+- Get all objects of a certain type from the realm.
+- Optionally, filter the results using the query engine.
+- Optionally, sort the results.
+
+All query, filter, and sort operations return a
+results collection. The results
+collections are live, meaning they always contain the latest
+results of the associated query.
+
+> Important:
+> By default, you can only read or write to a realm in your
+application's UI thread using
+asynchronous transactions. That is,
+you can only use `Realm` methods whose name ends with the word
+`Async` in the main thread of your Android application unless you
+explicitly allow the use of synchronous methods.
+>
+> This restriction exists for the benefit of your application users:
+performing read and write operations on the UI thread can lead to
+unresponsive or slow UI interactions, so it's usually best to handle
+these operations either asynchronously or in a background thread.
+
+### Find a Specific Object by Primary Key
+To find an object with a specific primary key value, open a realm
+and query the primary key field for the desired primary key value
+using the `RealmQuery.equalTo()` method:
+
+#### Java
+
+```java
+ProjectTask task = realm.where(ProjectTask.class).equalTo("_id", PRIMARY_KEY_VALUE.get()).findFirst();
+Log.v("EXAMPLE", "Fetched object by primary key: " + task);
+
+```
+
+#### Kotlin
+
+```kotlin
+val task = realm.where(ProjectTask::class.java)
+ .equalTo("_id", ObjectId.get()).findFirst()
+Log.v("EXAMPLE", "Fetched object by primary key: $task")
+
+```
+
+### Query All Objects of a Given Type
+The first step of any read is to **get all objects** of a
+certain type in a realm. With this results collection, you
+can operate on all instances on a type or filter and sort to
+refine the results.
+
+In order to access all instances of `ProjectTask` and `Project`, use
+the `where()` method
+to specify a class:
+
+#### Java
+
+```java
+RealmQuery tasksQuery = realm.where(ProjectTask.class);
+RealmQuery projectsQuery = realm.where(Project.class);
+
+```
+
+#### Kotlin
+
+```kotlin
+val tasksQuery = realm.where(ProjectTask::class.java)
+val projectsQuery = realm.where(Project::class.java)
+
+```
+
+### Filter Queries Based on Object Properties
+A **filter** selects a subset of results based on the
+value(s) of one or more object properties. Realm provides a
+full-featured query engine you
+can use to define filters. The most common use case is to
+find objects where a certain property matches a certain
+value. Additionally, you can compare strings, aggregate over
+collections of numbers, and use logical operators to build
+up complex queries.
+
+In the following example, we use the query
+engine's comparison operators to:
+
+- Find high priority tasks by comparing the value of the `priority` property value with a threshold number, above which priority can be considered high.
+- Find just-started or short-running tasks by seeing if the `progressMinutes` property falls within a certain range.
+- Find unassigned tasks by finding tasks where the `assignee` property is equal to null.
+- Find tasks assigned to specific teammates Ali or Jamie by seeing if the `assignee` property is in a list of names.
+
+#### Java
+
+```java
+RealmQuery tasksQuery = realm.where(ProjectTask.class);
+Log.i("EXAMPLE", "High priority tasks: " + tasksQuery.greaterThan("priority", 5).count());
+Log.i("EXAMPLE", "Just-started or short tasks: " + tasksQuery.between("progressMinutes", 1, 10).count());
+Log.i("EXAMPLE", "Unassigned tasks: " + tasksQuery.isNull("assignee").count());
+Log.i("EXAMPLE", "Ali or Jamie's tasks: " + tasksQuery.in("assignee", new String[]{"Ali", "Jamie"}).count());
+
+```
+
+#### Kotlin
+
+```kotlin
+val tasksQuery = realm.where(ProjectTask::class.java)
+Log.i(
+ "EXAMPLE", "High priority tasks: " + tasksQuery.greaterThan(
+ "priority",
+ 5
+ ).count()
+)
+Log.i(
+ "EXAMPLE", "Just-started or short tasks: " + tasksQuery.between(
+ "progressMinutes",
+ 1,
+ 10
+ ).count()
+)
+Log.i(
+ "EXAMPLE",
+ "Unassigned tasks: " + tasksQuery.isNull("assignee").count()
+)
+Log.i(
+ "EXAMPLE", "Ali or Jamie's tasks: " + tasksQuery.`in`(
+ "assignee", arrayOf(
+ "Ali",
+ "Jamie"
+ )
+ ).count()
+)
+
+```
+
+### Sort Query Results
+A **sort** operation allows you to configure the order in
+which Realm returns queried objects. You can sort based on
+one or more properties of the objects in the results
+collection.
+
+Realm only guarantees a consistent order of results when the
+results are sorted.
+
+The following code sorts the projects by name in reverse
+alphabetical order (i.e. "descending" order).
+
+#### Java
+
+```java
+RealmQuery projectsQuery = realm.where(Project.class);
+RealmResults results = projectsQuery.sort("name", Sort.DESCENDING).findAll();
+
+```
+
+#### Kotlin
+
+```kotlin
+val projectsQuery = realm.where(Project::class.java)
+val results = projectsQuery.sort("name", Sort.DESCENDING).findAll()
+
+```
+
+### Query a Relationship
+#### Java
+
+Consider the following relationship between classes `Human` and
+`Cat`. This arrangement allows each human to own a single cat:
+
+```java
+import org.bson.types.ObjectId;
+
+import io.realm.RealmObject;
+import io.realm.annotations.PrimaryKey;
+
+public class Human extends RealmObject {
+ @PrimaryKey
+ private ObjectId _id = new ObjectId();
+ private String name;
+ private Cat cat;
+
+ public Human(String name) {
+ this.name = name;
+ }
+
+ public Human() {
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Cat getCat() {
+ return cat;
+ }
+
+ public void setCat(Cat cat) {
+ this.cat = cat;
+ }
+
+ public ObjectId get_id() {
+ return _id;
+ }
+}
+
+```
+
+```java
+import org.bson.types.ObjectId;
+
+import io.realm.RealmObject;
+import io.realm.RealmResults;
+import io.realm.annotations.LinkingObjects;
+import io.realm.annotations.PrimaryKey;
+
+public class Cat extends RealmObject {
+ @PrimaryKey
+ private ObjectId _id = new ObjectId();
+ private String name = null;
+ @LinkingObjects("cat")
+ private final RealmResults owner = null;
+ public Cat(String name) {
+ this.name = name;
+ }
+ public Cat() {
+ }
+
+ public ObjectId get_id() {
+ return _id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public RealmResults getOwner() {
+ return owner;
+ }
+}
+
+```
+
+To query this relationship, use dot notation in a
+query to access any property
+of the linked object.
+
+#### Kotlin
+
+Consider the following relationship between classes `Person` and
+`Dog`. This arrangement allows each person to own a single dog:
+
+```kotlin
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+import org.bson.types.ObjectId
+
+open class Person(var name : String? = null) : RealmObject() {
+ @PrimaryKey
+ var _id : ObjectId = ObjectId()
+ var dog: Dog? = null
+}
+
+```
+
+```kotlin
+import io.realm.RealmObject
+import io.realm.RealmResults
+import io.realm.annotations.LinkingObjects
+import io.realm.annotations.PrimaryKey
+import org.bson.types.ObjectId
+
+open class Dog(var name : String? = null): RealmObject() {
+ @PrimaryKey
+ var _id : ObjectId = ObjectId()
+ @LinkingObjects("dog")
+ val owner: RealmResults? = null
+}
+
+```
+
+To query this relationship, use dot notation in a
+query to access any property
+of the linked object.
+
+### Query an Inverse Relationship
+#### Java
+
+Consider the following relationship between classes `Cat` and
+`Human`. In this example, all cats link to their human (or
+multiple humans, if multiple human objects refer to the same cat).
+Realm calculates the owners of each cat for you based on the field
+name you provide to the `@LinkingObjects` annotation:
+
+```java
+import org.bson.types.ObjectId;
+
+import io.realm.RealmObject;
+import io.realm.RealmResults;
+import io.realm.annotations.LinkingObjects;
+import io.realm.annotations.PrimaryKey;
+
+public class Cat extends RealmObject {
+ @PrimaryKey
+ private ObjectId _id = new ObjectId();
+ private String name = null;
+ @LinkingObjects("cat")
+ private final RealmResults owner = null;
+ public Cat(String name) {
+ this.name = name;
+ }
+ public Cat() {
+ }
+
+ public ObjectId get_id() {
+ return _id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public RealmResults getOwner() {
+ return owner;
+ }
+}
+```
+
+```java
+import org.bson.types.ObjectId;
+
+import io.realm.RealmObject;
+import io.realm.annotations.PrimaryKey;
+
+public class Human extends RealmObject {
+ @PrimaryKey
+ private ObjectId _id = new ObjectId();
+ private String name;
+ private Cat cat;
+
+ public Human(String name) {
+ this.name = name;
+ }
+
+ public Human() {
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Cat getCat() {
+ return cat;
+ }
+
+ public void setCat(Cat cat) {
+ this.cat = cat;
+ }
+
+ public ObjectId get_id() {
+ return _id;
+ }
+}
+```
+
+To query this relationship, use dot notation in a
+query to access any property
+of the linked object.
+
+#### Kotlin
+
+Consider the following relationship between classes `Dog` and
+`Person`. In this example, all dogs link to their owner (or
+multiple owners, if multiple person objects refer to the same dog).
+Realm calculates the owners of each dog for you based on the field
+name you provide to the `@LinkingObjects` annotation:
+
+```kotlin
+import io.realm.RealmObject
+import io.realm.RealmResults
+import io.realm.annotations.LinkingObjects
+import io.realm.annotations.PrimaryKey
+import org.bson.types.ObjectId
+
+open class Dog(var name : String? = null): RealmObject() {
+ @PrimaryKey
+ var _id : ObjectId = ObjectId()
+ @LinkingObjects("dog")
+ val owner: RealmResults? = null
+}
+```
+
+```kotlin
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+import org.bson.types.ObjectId
+
+open class Person(var name : String? = null) : RealmObject() {
+ @PrimaryKey
+ var _id : ObjectId = ObjectId()
+ var dog: Dog? = null
+}
+```
+
+To query this relationship, use dot notation in a
+query to access any property
+of the linked object.
+
+### Aggregate Data
+#### Java
+
+```java
+RealmQuery tasksQuery = realm.where(ProjectTask.class);
+/*
+Aggregate operators do not support dot-notation, so you
+cannot directly operate on a property of all of the objects
+in a collection property.
+
+You can operate on a numeric property of the top-level
+object, however:
+*/
+Log.i("EXAMPLE", "Tasks average priority: " + tasksQuery.average("priority"));
+
+```
+
+#### Kotlin
+
+```kotlin
+val tasksQuery = realm.where(ProjectTask::class.java)
+/*
+Aggregate operators do not support dot-notation, so you
+cannot directly operate on a property of all of the objects
+in a collection property.
+
+You can operate on a numeric property of the top-level
+object, however:
+*/Log.i("EXAMPLE", "Tasks average priority: " + tasksQuery.average("priority"))
+
+```
+
diff --git a/docs/guides/crud/threading.md b/docs/guides/crud/threading.md
new file mode 100644
index 0000000000..04a97ea9fe
--- /dev/null
+++ b/docs/guides/crud/threading.md
@@ -0,0 +1,290 @@
+# Threading - Java SDK
+To make your Android apps fast and responsive, you must
+balance the computing time needed to lay out the visuals and
+handle user interactions with the time needed to process
+your data and run your business logic. Typically, app
+developers spread this work across multiple threads: the
+main or UI thread for all of the user interface-related
+work, and one or more background threads to compute heavier
+workloads before sending it to the UI thread for
+presentation. By offloading heavy work to background
+threads, the UI thread can remain highly responsive
+regardless of the size of the workload.
+
+## Three Rules to Keep in Mind
+Realm enables simple and safe multithreaded code when you
+follow these three rules:
+
+You can write to a
+realm from any thread, but there can be only one
+writer at a time. Consequently, write transactions block
+each other. A write on the UI thread may result in your
+app appearing unresponsive while it waits for a write on a
+background thread to complete.
+
+Live objects, collections, and realm instances are
+**thread-confined**: that is, they are only valid on the
+thread on which they were created. Practically speaking,
+this means you cannot pass live instances to other
+threads. However, Realm offers several mechanisms for
+sharing objects across threads.
+
+Realm's Multiversion Concurrency Control (MVCC) architecture eliminates the need to lock for read operations. The
+values you read will never be corrupted or in a
+partially-modified state. You can freely read from realms
+on any thread without the need for locks or mutexes.
+Unnecessarily locking would be a performance bottleneck
+since each thread might need to wait its turn before
+reading.
+
+## Communication Across Threads
+Live objects, collections, and realms are **thread-confined**. If
+you need to work with the same data across multiple threads, you should
+open the same realm on multiple threads as separate realm
+instances. The Java SDK consolidates underlying connections across
+threads where possible to make this pattern
+more efficient.
+
+When you need to communicate across threads, you have
+several options depending on your use case:
+
+- To modify the data on two threads, query
+for the object on both threads using a
+primary key.
+- To send a fast, read-only view of an object to other threads,
+freeze the object.
+- To keep and share many read-only views of the object in your app, copy
+the object from the realm.
+- To react to changes made on any thread, use
+notifications.
+- To see changes from other threads in the realm on the current
+thread, refresh your realm
+instance (event loop threads refresh automatically).
+
+### Intents
+Managed `RealmObject` instances are
+not thread-safe or `Parcelable`, so you cannot pass them between
+activities or threads via an `Intent`. Instead, you can pass an object
+identifier, like a primary key,
+in the `Intent` extras bundle, and then open a new realm instance
+in the separate thread to query for that identifier. Alternatively, you
+can freeze Realm objects.
+
+> Seealso:
+> You can find working examples in the [Passing Objects](https://github.com/realm/realm-java/blob/master/examples/threadExample/src/main/java/io/realm/examples/threads/PassingObjectsFragment.java)
+portion of the [Java SDK Threading Example](https://github.com/realm/realm-java/tree/master/examples/threadExample).
+The example shows you how to pass IDs and retrieve a `RealmObject`
+in common Android use cases.
+>
+
+### Frozen Objects
+Live, thread-confined objects work fine in most cases.
+However, some apps -- those based on reactive, event
+stream-based architectures, for example -- need to send
+immutable copies across threads. In this case,
+you can **freeze** objects, collections, and realms.
+
+Freezing creates an immutable view of a specific object,
+collection, or realm that still exists on disk and does not
+need to be deeply copied when passed around to other
+threads. You can freely share a frozen object across threads
+without concern for thread issues.
+
+Frozen objects are not live and do not automatically update. They are
+effectively snapshots of the object state at the time of freezing. When
+you freeze a realm all child objects and collections also become
+frozen. You can't modify frozen objects, but you can read the primary
+key from a frozen object, query a live realm for the underlying
+object, and then update that live object instance.
+
+Frozen objects remain valid for as long as the realm that
+spawned them stays open. Avoid closing realms that contain frozen
+objects until all threads are done working with those frozen objects.
+
+> Warning:
+> When working with frozen objects, an attempt to do any of
+the following throws an exception:
+>
+> - Opening a write transaction on a frozen realm.
+> - Modifying a frozen object.
+> - Adding a change listener to a frozen realm, collection, or object.
+>
+
+Once frozen, you cannot unfreeze an object. You
+can use `isFrozen()` to check if an object is frozen.
+This method is always thread-safe.
+
+To freeze an object, collection, or realm, use the
+`freeze()` method:
+
+#### Java
+
+```java
+Realm realm = Realm.getInstance(config);
+
+// Get an immutable copy of the realm that can be passed across threads
+Realm frozenRealm = realm.freeze();
+Assert.assertTrue(frozenRealm.isFrozen());
+
+RealmResults frogs = realm.where(Frog.class).findAll();
+// You can freeze collections
+RealmResults frozenFrogs = frogs.freeze();
+Assert.assertTrue(frozenFrogs.isFrozen());
+
+// You can still read from frozen realms
+RealmResults frozenFrogs2 = frozenRealm.where(Frog.class).findAll();
+Assert.assertTrue(frozenFrogs2.isFrozen());
+
+Frog frog = frogs.first();
+Assert.assertTrue(!frog.getRealm().isFrozen());
+
+// You can freeze objects
+Frog frozenFrog = frog.freeze();
+Assert.assertTrue(frozenFrog.isFrozen());
+// Frozen objects have a reference to a frozen realm
+Assert.assertTrue(frozenFrog.getRealm().isFrozen());
+
+```
+
+#### Kotlin
+
+```kotlin
+val realm = Realm.getInstance(config)
+
+// Get an immutable copy of the realm that can be passed across threads
+val frozenRealm = realm.freeze()
+Assert.assertTrue(frozenRealm.isFrozen)
+val frogs = realm.where(Frog::class.java).findAll()
+// You can freeze collections
+val frozenFrogs = frogs.freeze()
+Assert.assertTrue(frozenFrogs.isFrozen)
+
+// You can still read from frozen realms
+val frozenFrogs2 =
+ frozenRealm.where(Frog::class.java).findAll()
+Assert.assertTrue(frozenFrogs2.isFrozen)
+val frog: Frog = frogs.first()!!
+Assert.assertTrue(!frog.realm.isFrozen)
+
+// You can freeze objects
+val frozenFrog: Frog = frog.freeze()
+Assert.assertTrue(frozenFrog.isFrozen)
+Assert.assertTrue(frozenFrog.realm.isFrozen)
+
+```
+
+> Important:
+> Frozen objects preserve an entire copy of the realm that contains
+them at the moment they were frozen. As a result, freezing a large
+number of objects can cause a realm to consume more memory and
+storage than it might have without frozen objects. If you need to
+separately freeze a large number of objects for long periods of time,
+consider copying what you need out of the realm instead.
+>
+
+## Refreshing Realms
+When you open a realm, it reflects the most recent successful write
+commit and remains on that version until it is **refreshed**. This means
+that the realm will not see changes that happened on another thread
+until the next refresh. Realms on any event loop thread
+(including the UI thread) automatically refresh themselves at the
+beginning of that thread's loop. However, you must manually refresh
+realm instances that are tied to non-looping threads or that have
+auto-refresh disabled. To refresh a realm, call
+`Realm.refresh()`:
+
+#### Java
+
+```java
+if (!realm.isAutoRefresh()) {
+ // manually refresh
+ realm.refresh();
+}
+
+```
+
+#### Kotlin
+
+```kotlin
+if (!realm.isAutoRefresh) {
+ // manually refresh
+ realm.refresh()
+}
+
+```
+
+> Tip:
+> Realms also automatically refresh after completing a write transaction.
+>
+
+## Realm's Threading Model in Depth
+Realm provides safe, fast, lock-free, and concurrent access
+across threads with its [Multiversion Concurrency
+Control (MVCC)](https://en.wikipedia.org/wiki/Multiversion_concurrency_control)
+architecture.
+
+### Compared and Contrasted with Git
+If you are familiar with a distributed version control
+system like [Git](https://git-scm.com/), you may already
+have an intuitive understanding of MVCC. Two fundamental
+elements of Git are:
+
+- Commits, which are atomic writes.
+- Branches, which are different versions of the commit history.
+
+Similarly, Realm has atomically-committed writes in the form
+of transactions. Realm also has many
+different versions of the history at any given time, like
+branches.
+
+Unlike Git, which actively supports distribution and
+divergence through forking, a realm only has one true latest
+version at any given time and always writes to the head of
+that latest version. Realm cannot write to a previous
+version. This makes sense: your data should converge on one
+latest version of the truth.
+
+### Internal Structure
+A realm is implemented using a [B+ tree](https://en.wikipedia.org/wiki/B%2B_tree) data structure. The top-level node represents a
+version of the realm; child nodes are objects in that
+version of the realm. The realm has a pointer to its latest
+version, much like how Git has a pointer to its HEAD commit.
+
+Realm uses a copy-on-write technique to ensure
+[isolation](https://en.wikipedia.org/wiki/Isolation_(database_systems)) and
+[durability](https://en.wikipedia.org/wiki/Durability_(database_systems)).
+When you make changes, Realm copies the relevant part of the
+tree for writing, then commits the changes in two phases:
+
+- Write changes to disk and verify success.
+- Set the latest version pointer to point to the newly-written version.
+
+This two-step commit process guarantees that even if the
+write failed partway, the original version is not corrupted
+in any way because the changes were made to a copy of the
+relevant part of the tree. Likewise, the realm's root
+pointer will point to the original version until the new
+version is guaranteed to be valid.
+
+Realm uses zero-copy techniques
+like memory mapping to handle data. When you read a value
+from the realm, you are virtually looking at the value on
+the actual disk, not a copy of it. This is the basis for
+live objects. This is also why a realm
+head pointer can be set to point to the new version after
+the write to disk has been validated.
+
+## Summary
+- Realm enables simple and safe multithreaded code when you follow
+these rules: Don't pass live objects to other threads, and don't lock to read.
+- In order to see changes made on other threads in your realm
+instance, you must manually **refresh** realm instances that do
+not exist on "loop" threads or that have auto-refresh disabled.
+- For apps based on reactive, event-stream-based architectures, you can
+**freeze** objects, collections, and realms in order to pass
+copies around efficiently to different threads for processing.
+- Realm's multiversion concurrency control (MVCC)
+architecture is similar to Git's. Unlike Git, Realm has
+only one true latest version for each realm.
+- Realm commits in two stages to guarantee isolation and
+durability.
diff --git a/docs/guides/crud/update.md b/docs/guides/crud/update.md
new file mode 100644
index 0000000000..d014440d26
--- /dev/null
+++ b/docs/guides/crud/update.md
@@ -0,0 +1,365 @@
+# CRUD - Update - Java SDK
+## About the Examples on this Page
+The examples on this page use the data model of a project
+management app that has two Realm object types: `Project`
+and `Task`. A `Project` has zero or more `Tasks`.
+
+See the schema for these two classes, `Project` and
+`Task`, below:
+
+#### Java
+
+```java
+
+import org.bson.types.ObjectId;
+
+import io.realm.RealmObject;
+import io.realm.annotations.PrimaryKey;
+import io.realm.annotations.RealmClass;
+import io.realm.annotations.Required;
+
+public class ProjectTask extends RealmObject {
+ @PrimaryKey
+ public ObjectId _id;
+ @Required
+ public String name;
+ public String assignee;
+ public int progressMinutes;
+ public boolean isComplete;
+ public int priority;
+ @Required
+ public String _partition;
+}
+
+```
+
+```java
+
+import org.bson.types.ObjectId;
+
+import io.realm.RealmList;
+import io.realm.RealmObject;
+import io.realm.annotations.PrimaryKey;
+import io.realm.annotations.RealmClass;
+import io.realm.annotations.Required;
+
+public class Project extends RealmObject {
+ @PrimaryKey
+ public ObjectId _id;
+ @Required
+ public String name;
+ public RealmList tasks = new RealmList<>();
+}
+
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+import io.realm.annotations.Required
+import org.bson.types.ObjectId
+
+open class ProjectTask(
+ @PrimaryKey
+ var _id: ObjectId = ObjectId(),
+ @Required
+ var name: String = "",
+ var assignee: String? = null,
+ var progressMinutes: Int = 0,
+ var isComplete: Boolean = false,
+ var priority: Int = 0,
+ var _partition: String = ""
+): RealmObject()
+
+```
+
+```kotlin
+import io.realm.RealmList
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+import io.realm.annotations.Required
+import org.bson.types.ObjectId
+
+open class Project(
+ @PrimaryKey
+ var _id: ObjectId = ObjectId(),
+ @Required
+ var name: String = "",
+ var tasks: RealmList = RealmList(),
+): RealmObject()
+
+```
+
+## Modify an Object
+Within a transaction, you can update a Realm object the same
+way you would update any other object in your language of
+choice. Just assign a new value to the property or update
+the property.
+
+The following example changes the turtle's name to "Archibald" and
+sets Archibald's age to 101 by assigning new values to properties:
+
+#### Java
+
+```java
+realm.executeTransaction(r -> {
+ // Get a turtle to update.
+ Turtle turtle = r.where(Turtle.class).findFirst();
+ // Update properties on the instance.
+ // This change is saved to the realm.
+ turtle.setName("Archibald");
+ turtle.setAge(101);
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+realm.executeTransaction { r: Realm ->
+ // Get a turtle to update.
+ val turtle = r.where(Turtle::class.java).findFirst()
+ // Update properties on the instance.
+ // This change is saved to the realm.
+ turtle!!.name = "Archibald"
+ turtle.age = 101
+}
+
+```
+
+## Upsert an Object
+An **upsert** is a write operation that either inserts a new object
+with a given primary key or updates an existing object that already has
+that primary key. We call this an upsert because it is an "**update** or
+**insert**" operation. This is useful when an object may or may not
+already exist, such as when bulk importing a dataset into an existing
+realm. Upserting is an elegant way to update existing entries while
+adding any new entries.
+
+The following example demonstrates how to upsert an object with
+realm. We create a new turtle enthusiast named "Drew" and then
+update their name to "Andy" using `insertOrUpdate()`:
+
+#### Java
+
+```java
+realm.executeTransaction(r -> {
+ ObjectId id = new ObjectId();
+ TurtleEnthusiast drew = new TurtleEnthusiast();
+ drew.set_id(id);
+ drew.setName("Drew");
+ drew.setAge(25);
+ // Add a new turtle enthusiast to the realm. Since nobody with this id
+ // has been added yet, this adds the instance to the realm.
+ r.insertOrUpdate(drew);
+ TurtleEnthusiast andy = new TurtleEnthusiast();
+ andy.set_id(id);
+ andy.setName("Andy");
+ andy.setAge(56);
+ // Judging by the ID, it's the same turtle enthusiast, just with a different name.
+ // As a result, you overwrite the original entry, renaming "Drew" to "Andy".
+ r.insertOrUpdate(andy);
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+realm.executeTransaction { r: Realm ->
+ val id = ObjectId()
+ val drew = TurtleEnthusiast()
+ drew._id = id
+ drew.name = "Drew"
+ drew.age = 25
+ // Add a new turtle enthusiast to the realm. Since nobody with this id
+ // has been added yet, this adds the instance to the realm.
+ r.insertOrUpdate(drew)
+ val andy = TurtleEnthusiast()
+ andy._id = id
+ andy.name = "Andy"
+ andy.age = 56
+ // Judging by the ID, it's the same turtle enthusiast, just with a different name.
+ // As a result, you overwrite the original entry, renaming "Drew" to "Andy".
+ r.insertOrUpdate(andy)
+}
+
+```
+
+You can also use `copyToRealmOrUpdate()` to
+either create a new object based on a supplied object or update an
+existing object with the same primary key value. Use the
+`CHECK_SAME_VALUES_BEFORE_SET`
+`ImportFlag` to only update fields
+that are different in the supplied object:
+
+The following example demonstrates how to insert an object or, if an object already
+exists with the same primary key, update only those fields that differ:
+
+#### Java
+
+```java
+realm.executeTransaction(r -> {
+ ObjectId id = new ObjectId();
+ TurtleEnthusiast drew = new TurtleEnthusiast();
+ drew.set_id(id);
+ drew.setName("Drew");
+ drew.setAge(25);
+ // Add a new turtle enthusiast to the realm. Since nobody with this id
+ // has been added yet, this adds the instance to the realm.
+ r.insertOrUpdate(drew);
+ TurtleEnthusiast andy = new TurtleEnthusiast();
+ andy.set_id(id);
+ andy.setName("Andy");
+ // Judging by the ID, it's the same turtle enthusiast, just with a different name.
+ // As a result, you overwrite the original entry, renaming "Drew" to "Andy".
+ // the flag passed ensures that we only write the updated name field to the db
+ r.copyToRealmOrUpdate(andy, ImportFlag.CHECK_SAME_VALUES_BEFORE_SET);
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+realm.executeTransaction { r: Realm ->
+ val id = ObjectId()
+ val drew = TurtleEnthusiast()
+ drew._id = id
+ drew.name = "Drew"
+ drew.age = 25
+ // Add a new turtle enthusiast to the realm. Since nobody with this id
+ // has been added yet, this adds the instance to the realm.
+ r.insertOrUpdate(drew)
+ val andy = TurtleEnthusiast()
+ andy._id = id
+ andy.name = "Andy"
+ // Judging by the ID, it's the same turtle enthusiast, just with a different name.
+ // As a result, you overwrite the original entry, renaming "Drew" to "Andy".
+ r.copyToRealmOrUpdate(andy,
+ ImportFlag.CHECK_SAME_VALUES_BEFORE_SET)
+}
+
+```
+
+## Update a Collection
+Realm supports collection-wide updates. A collection update
+applies the same update to specific properties of several
+objects in a collection at once.
+
+The following example demonstrates how to update a
+collection. Thanks to the implicit inverse
+relationship between the Turtle's
+`owner` property and the TurtleEnthusiast's `turtles` property,
+Realm automatically updates Josephine's list of turtles
+when you use `setObject()`
+to update the "owner" property for all turtles in the collection.
+
+#### Java
+
+```java
+realm.executeTransaction(r -> {
+ // Create a turtle enthusiast named Josephine.
+ TurtleEnthusiast josephine = r.createObject(TurtleEnthusiast.class, new ObjectId());
+ josephine.setName("Josephine");
+
+ // Get all turtles named "Pierogi".
+ RealmResults turtles = r.where(Turtle.class).equalTo("name", "Pierogi").findAll();
+
+ // Give all turtles named "Pierogi" to Josephine
+ turtles.setObject("owner", josephine);
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+realm.executeTransaction { r: Realm ->
+ // Create a turtle enthusiast named Josephine.
+ val josephine = realm.createObject(
+ TurtleEnthusiast::class.java,
+ ObjectId()
+ )
+ josephine.name = "Josephine"
+
+ // Get all turtles named "Pierogi".
+ val turtles = r.where(Turtle::class.java)
+ .equalTo("name", "Pierogi")
+ .findAll()
+
+ // Give all turtles named "Pierogi" to Josephine
+ turtles.setObject("owner", josephine)
+}
+
+```
+
+## Iteration
+Because realm collections always reflect the latest state, they
+can appear, disappear, or change while you iterate over a collection.
+To get a stable collection you can iterate over, you can create a
+**snapshot** of a collection's data. A snapshot guarantees the order of
+elements will not change, even if an element is deleted or modified.
+
+`Iterator` objects created from `RealmResults` use snapshots
+automatically. `Iterator` objects created from `RealmList`
+instances do *not* use snapshots. Use
+`RealmList.createSnapshot()`
+or
+`RealmResults.createSnapshot()`
+to manually generate a snapshot you can iterate over manually:
+
+The following example demonstrates how to iterate over a collection
+safely using either an implicit snapshot created from a `RealmResults`
+`Iterator` or a manual snapshot created from a `RealmList`:
+
+#### Java
+
+```java
+RealmResults frogs = realm.where(Frog.class)
+ .equalTo("species", "bullfrog")
+ .findAll();
+
+// Use an iterator to rename the species of all bullfrogs
+realm.executeTransaction(r -> {
+ for (Frog frog : frogs) {
+ frog.setSpecies("Lithobates catesbeiana");
+ }
+});
+
+// Use a snapshot to rename the species of all bullfrogs
+realm.executeTransaction(r -> {
+ OrderedRealmCollectionSnapshot frogsSnapshot = frogs.createSnapshot();
+ for (int i = 0; i < frogsSnapshot.size(); i++) {
+ frogsSnapshot.get(i).setSpecies("Lithobates catesbeiana");
+ }
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+val frogs = realm.where(Frog::class.java)
+ .equalTo("species", "bullfrog")
+ .findAll()
+
+// Use an iterator to rename the species of all bullfrogs
+realm.executeTransaction {
+ for (frog in frogs) {
+ frog.species = "Lithobates catesbeiana"
+ }
+}
+
+// Use a snapshot to rename the species of all bullfrogs
+realm.executeTransaction {
+ val frogsSnapshot = frogs.createSnapshot()
+ for (i in frogsSnapshot.indices) {
+ frogsSnapshot[i]!!.species = "Lithobates catesbeiana"
+ }
+}
+
+```
+
diff --git a/docs/guides/install.md b/docs/guides/install.md
new file mode 100644
index 0000000000..376e9b6608
--- /dev/null
+++ b/docs/guides/install.md
@@ -0,0 +1,205 @@
+# Install Realm - Java SDK
+> Note:
+> The Java SDK is in best-effort maintenance mode and **no longer receives
+new development or non-critical bug fixes. To develop your app with new
+features, use the Kotlin SDK. You can use the Java SDK
+with the Kotlin SDK in the same project.
+>
+> Learn more about how to Migrate from the Java SDK to the Kotlin SDK.
+>
+
+## Overview
+This page details how to install Realm using the Java SDK in your project
+and get started.
+
+You can use multiple SDKs in your project. Because the Java SDK is no longer
+receiving new development, this is useful if you want to
+use new features in your app.
+
+## Prerequisites
+- [Android Studio](https://developer.android.com/studio/index.html) version 1.5.1 or higher.
+- Java Development Kit (JDK) 11 or higher.
+- An emulated or hardware Android device for testing.
+- Android API Level 16 or higher (Android 4.1 and above).
+
+## Installation
+Realm only supports the Gradle build system. Follow these steps
+to add the Realm Java SDK to your project.
+
+> Note:
+> Because Realm provides a ProGuard configuration as part
+of the Realm library, you do not need to add any
+Realm-specific rules to your ProGuard configuration.
+>
+
+### Project Gradle Configuration
+To add local realm to your application, make
+the following changes to your project-level Gradle build
+file, typically found at /build.gradle:
+
+#### Gradle Plugin
+
+> Tip:
+> The Java SDK does not yet support the Gradle Plugin syntax. Fortunately,
+you can still add the SDK to projects that use this syntax.
+>
+
+- Add a `buildscript` block that contains a `repositories` block and a `dependencies` block.
+- Add the `mavenCentral()` repository to the `buildscript.repositories` block.
+- Add the `io.realm:realm-gradle-plugin` dependency to the `buildscript.dependencies` block.
+
+```groovy
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+ dependencies {
+ classpath "io.realm:realm-gradle-plugin:10.18.0"
+ }
+}
+
+plugins {
+ id 'com.android.application' version '7.1.2' apply false
+ id 'com.android.library' version '7.1.2' apply false
+ id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
+ id "org.jetbrains.kotlin.kapt" version "1.6.20" apply false
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
+```
+
+#### Gradle Legacy
+
+- Add the `io.realm:realm-gradle-plugin` dependency to the `buildscript.dependencies` block.
+- Add the `mavenCentral()` repository to the `allprojects.repositories` block.
+
+```groovy
+buildscript {
+ repositories {
+ google()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:3.5.1"
+ classpath "io.realm:realm-gradle-plugin:10.18.0"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+ dependencies {
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
+```
+
+### Application Module Gradle Configuration
+Then, make the following changes to your application-level
+Gradle build file, typically found at /app/build.gradle:
+
+#### Gradle Plugin
+
+- Apply the `kotlin-kapt` plugin if your application uses Kotlin
+- Beneath the `plugins` block, apply the `realm-android` plugin.
+
+```groovy
+plugins {
+ id 'com.android.application'
+ id 'org.jetbrains.kotlin.android'
+ id 'org.jetbrains.kotlin.kapt'
+}
+
+apply plugin: "realm-android"
+
+android {
+ compileSdk 31
+ defaultConfig {
+ applicationId "com.mongodb.example-realm-application"
+ minSdk 28
+ targetSdk 31
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_11
+ targetCompatibility JavaVersion.VERSION_11
+ }
+ kotlinOptions {
+ jvmTarget = '11'
+ }
+}
+
+dependencies {
+ implementation 'io.realm:realm-gradle-plugin:10.10.1'
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.1'
+ implementation 'com.google.android.material:material:1.5.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+}
+```
+
+#### Gradle Legacy
+
+- Apply the `kotlin-kapt` plugin if your application uses Kotlin
+- Apply the `realm-android` plugin
+
+```groovy
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-kapt'
+apply plugin: 'realm-android'
+
+android {
+ compileSdkVersion 31
+ buildToolsVersion "29.0.2"
+ defaultConfig {
+ applicationId "com.mongodb.example-realm-application"
+ minSdkVersion 28
+ targetSdkVersion 31
+ }
+ compileOptions {
+ sourceCompatibility 1.11
+ targetCompatibility 1.11
+ }
+ kotlinOptions {
+ jvmTarget = '11'
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation "androidx.appcompat:appcompat:1.1.0"
+ implementation "androidx.core:core-ktx:1.2.0"
+}
+```
+
+After updating the `build.gradle` files, resolve the dependencies by
+clicking File > Sync Project with Gradle Files.
+
+## Supported Platforms
+Realm's Java SDK enables you to build apps for the
+following platforms:
+
+- Android
+- Wear OS
+- Android Automotive OS
+- Android TV
+- Android Things
diff --git a/docs/guides/model-data.md b/docs/guides/model-data.md
new file mode 100644
index 0000000000..58e51b3608
--- /dev/null
+++ b/docs/guides/model-data.md
@@ -0,0 +1,152 @@
+# Model Data - Java SDK
+
+An **object schema** is a configuration object that defines the fields and
+relationships of a Realm object type. Android
+Realm applications define object schemas with Java or Kotlin
+classes using Realm Schemas.
+
+Object schemas specify constraints on object fields such as the data
+type of each field, whether a
+field is required, and default field values. Schemas can also define
+relationships between object types in
+a realm.
+
+Modifying your application's Realm Schema requires you to
+migrate data from older
+versions of your Realm Schema to the new version.
+
+## Realm Apps
+Every App has a Realm Schema
+composed of a list of object schemas for each type of object that the
+realms in that application may contain.
+
+Realm guarantees that all objects in a realm conform to the
+schema for their object type and validates objects whenever they're
+created, modified, or deleted.
+
+## Relationships
+You can model **one-to-one** relationships in realm with
+`RealmObject` fields.
+You can model **one-to-many** and **many-to-one** relationships
+`RealmList` fields.
+Inverse relationships are the opposite end of a **one-to-many** or
+**many-to-one** relationship.
+You can make **inverse** relationships traversable with the
+`@LinkingObjects`
+annotation on a `RealmResults`
+field. In an instance of a `RealmObject`, inverse relationship fields
+contain the set of Realm objects that point to that object
+instance through the described relationship. You can find the same set
+of Realm objects with a manual query, but the inverse
+relationship field reduces boilerplate query code and capacity for error.
+
+## Realm Objects
+Unlike normal Java objects, which contain their own data, a
+Realm object doesn't contain data. Instead,
+Realm objects read and write properties directly to
+Realm.
+
+Instances of Realm objects can be either **managed** or **unmanaged**.
+
+- **Managed** objects are: persisted in Realmalways up to datethread-confinedgenerally more lightweight than the unmanaged version, as they take
+up less space on the Java heap.
+- **Unmanaged** objects are just like ordinary Java objects, since
+they are not persisted and never update automatically.
+You can move unmanaged objects freely across threads.
+
+You can convert between the two states using
+`realm.copyToRealm()`
+and `realm.copyFromRealm()`.
+
+### RealmProxy
+The `RealmProxy` classes are the Realm SDK's way of
+ensuring that Realm objects don't contain any data
+themselves. Instead, each class's `RealmProxy` accesses data directly
+in the database.
+
+For every model class in your project, the Realm annotation
+processor generates a corresponding `RealmProxy` class. This class
+extends your model class and is returned when you call
+`Realm.createObject()`. In your code, this object works just like your
+model class.
+
+### Realm Object Limitations
+Realm objects:
+
+- cannot contain fields that use the `final` or `volatile` modifiers
+(except for inverse relationship
+fields).
+- cannot extend any object other than `RealmObject`.
+- must contain an empty constructor (if your class does not include any
+constructor, the automatically generated empty constructor will suffice)
+
+Naming limitations:
+
+- Class names cannot exceed 57 characters.
+- Class names must be unique within realm modules
+- Field names cannot exceed 63 characters.
+
+Size limitations:
+
+- `String` or `byte[]` fields cannot exceed 16 MB.
+
+Usage limitations:
+
+- Because Realm objects are live and can change at any time,
+their `hashCode()` value can change over time. As a result, you
+should not use `RealmObject` instances as a key in any map or set.
+
+## Incremental Builds
+The bytecode transformer used by Realm supports incremental
+builds, but your application requires a full rebuild when adding or
+removing the following from a Realm object field:
+
+- an `@Ignore` annotation
+- the `static` keyword
+- the `transient` keyword
+
+You can perform a full rebuild with Build > Clean Project
+and Build > Rebuild Project in these cases.
+
+## Schema Version
+A **schema version** identifies the state of a Realm Schema at some point in time. Realm tracks the schema
+version of each realm and uses it to map the objects in each realm
+to the correct schema.
+
+Schema versions are integers that you may include
+in the realm configuration when you open a realm. If a client
+application does not specify a version number when it opens a realm then
+the realm defaults to version `0`.
+
+> Important:
+> Migrations must update a realm to a
+higher schema version. Realm throws an error if a client
+application opens a realm with a schema version that is lower than
+the realm's current version or if the specified schema version is the
+same as the realm's current version but includes different
+object schemas.
+>
+
+## Migrations
+A **local migration** is a migration for a realm with
+another realm. Local migrations have access to the existing
+Realm Schema, version, and objects and define logic that
+incrementally updates the realm to its new schema version.
+To perform a local migration you must specify a new schema
+version that is higher than the current version and provide
+a migration function when you open the out-of-date realm.
+
+With the SDK, you can update underlying data to reflect schema changes
+using manual migrations. During such a manual migration, you can
+define new and deleted properties when they are added or removed from
+your schema. The editable schema exposed via a
+`DynamicRealm` provides
+convenience functions for renaming fields. This gives you full control
+over the behavior of your data during complex schema migrations.
+
+> Tip:
+> During development of an application, `RealmObject` classes can
+change frequently. You can use `Realm.deleteRealm()`to
+delete the database file and eliminate the need to write a full
+migration for testing data.
+>
diff --git a/docs/guides/model-data/data-types.md b/docs/guides/model-data/data-types.md
new file mode 100644
index 0000000000..e5389af2c6
--- /dev/null
+++ b/docs/guides/model-data/data-types.md
@@ -0,0 +1,14 @@
+# Realm Data Types - Java SDK
+
+Explore detailed guidance for each Realm Java SDK data type and related concepts:
+
+- [Field Types](./data-types/field-types.md)
+- [Collections](./data-types/collections.md)
+- [Counters](./data-types/counters.md)
+- [Dictionaries](./data-types/realmdictionary.md)
+- [Sets](./data-types/realmset.md)
+- [Mixed (RealmAny)](./data-types/realmany.md)
+- [Enums](./data-types/enums.md)
+- [Embedded Objects](./data-types/embedded-objects.md)
+
+Each linked page covers usage details, constraints, and example code where applicable.
diff --git a/docs/guides/model-data/data-types/collections.md b/docs/guides/model-data/data-types/collections.md
new file mode 100644
index 0000000000..6e24a9277d
--- /dev/null
+++ b/docs/guides/model-data/data-types/collections.md
@@ -0,0 +1,171 @@
+# Collections - Java SDK
+A Realm collection is an
+object that contains zero or more instances of one
+type. Realm collections
+are homogenous, i.e. all objects in a collection are of the
+same type.
+
+You can filter and sort any collection using Realm's
+query engine. Collections are
+live, so they always reflect the
+current state of the realm instance on the current
+thread. You can also listen for changes in the collection by subscribing
+to collection notifications.
+
+Realm has two kinds of collections: **lists** and **results**.
+
+## Lists
+Realm objects can contain lists of non-Realm-object data
+types. You can model these collections with the type `RealmList`,
+where `T` can be the following types:
+
+- `String`
+- `Integer`
+- `UUID`
+- `ObjectId`
+- `Boolean`
+- `Float`
+- `Double`
+- `Short`
+- `Long`
+- `Byte`
+- `byte[]`
+- `Date`
+
+> Seealso:
+> Lists
+>
+
+### List Collections
+A **list collection** represents a to-many
+relationship between two Realm
+types. Lists are mutable: within a write transaction, you
+can add and remove elements on a list. Lists are not
+associated with a query.
+
+### Results Collections
+A **results collection** represents the lazily-evaluated
+results of a query operation. Results are immutable: you
+cannot add or remove elements on the results collection.
+Results have an associated query that determines their
+contents.
+
+The `RealmResults` class inherits from
+[AbstractList](https://developer.android.com/reference/java/util/AbstractList) and behaves
+in similar ways. For example, `RealmResults` are ordered, and you can
+access the individual objects through an index. If a query has no
+matches, the returned `RealmResults` object will be a list of length
+0, not a `null` object reference.
+
+You can only modify or delete objects in a `RealmResults` set
+in a write transaction.
+
+## Iteration
+Because Realm collections are live, objects may move as you
+iterate over a collection. You can use
+snapshots to iterate over collections safely.
+
+## Adapters
+Realm offers adapters to help bind data
+to standard UI widgets. These classes work with any class that
+implements the `OrderedRealmCollection` interface, which includes
+the built-in `RealmResults` and `RealmList` classes. For more
+information on adapters, see the documentation on
+Displaying Collections.
+
+> Important:
+> The Realm adapters only accept *managed*
+Realm object instances tied to an instance of a realm.
+To display non-managed objects, use the general-use Android
+`RecyclerView.Adapter` for recycler views or `ArrayAdapter` for
+list views.
+>
+
+## Collections are Live
+Like live objects, Realm collections
+are usually **live**:
+
+- Live results collections always reflect the current results of the associated query.
+- Live lists always reflect the current state of the relationship on the realm instance.
+
+There are three cases when a collection is **not** live:
+
+- The collection is unmanaged, e.g. a List property of a Realm object that has not been added to a realm yet or that has been copied from a realm.
+- The collection is frozen.
+- The collection is part of a snapshot.
+
+Combined with collection notifications, live collections enable clean,
+reactive code. For example, suppose your view displays the
+results of a query. You can keep a reference to the results
+collection in your view class, then read the results
+collection as needed without having to refresh it or
+validate that it is up-to-date.
+
+> Warning:
+> Results update themselves automatically. If you
+store the positional index of an object in a collection
+or the count of objects in a collection, the stored index
+or count value could be outdated by the time you use it.
+>
+
+## Results are Lazily Evaluated
+Realm only runs a query when you actually request the
+results of that query, e.g. by accessing elements of the
+results collection. This lazy evaluation enables you to
+write elegant, highly performant code for handling large
+data sets and complex queries.
+
+### Limiting Query Results
+As a result of lazy evaluation, you do not need any special
+mechanism to limit query results with Realm. For example, if
+your query matches thousands of objects, but you only want
+to load the first ten, simply access only the first ten
+elements of the results collection.
+
+### Pagination
+Thanks to lazy evaluation, the common task of pagination
+becomes quite simple. For example, suppose you have a
+results collection associated with a query that matches
+thousands of objects in your realm. You display one hundred
+objects per page. To advance to any page, simply access the
+elements of the results collection starting at the index
+that corresponds to the target page.
+
+## List vs. Results
+When you need a collection, you can use the following rule
+of thumb to determine whether a list or a results collection
+is appropriate:
+
+- When you define the properties of your Realm objects, use lists to define to-many relationships except implicit inverse relationships.
+- Use results everywhere else.
+
+To understand these different use cases, consider whether
+you should be able to add or remove objects directly. Lists
+allow you to add and remove objects directly, because you
+control the relationships. Results collections do not allow
+you to add or remove objects directly, because their contents
+are determined by a query.
+
+> Example:
+> Consider a Realm type called Person with a field called
+`emails` that is a collection of strings representing
+email addresses. You control this data. Your application
+needs to add and remove email addresses from your Person
+instances. Therefore, use a **list** to define the field
+type of `emails`.
+>
+> On the other hand, when you query the realm for all
+Persons over the age of 25, it would not make sense for
+you to add or remove Persons directly to the resulting
+collection. The contents of that collection only change
+when the query matches a different set of Persons.
+Therefore, Realm gives you a **results** collection.
+>
+
+> Note:
+> Since Realm automatically determines the contents of
+implicit inverse relationship collections, you may not add
+or remove objects from such a collection.
+Therefore, the type of such a one-to-many relationship
+property is actually a results collection, not a list.
+>
diff --git a/docs/guides/model-data/data-types/counters.md b/docs/guides/model-data/data-types/counters.md
new file mode 100644
index 0000000000..1f4934f8c4
--- /dev/null
+++ b/docs/guides/model-data/data-types/counters.md
@@ -0,0 +1,142 @@
+# Counters - Java SDK
+Realm offers `MutableRealmInteger`, a wrapper around numeric values,
+to help better synchronize numeric changes across multiple clients.
+
+Typically, incrementing or decrementing a
+`byte`, `short`, `int`, or `long` field of a Realm
+object looks something like this:
+
+1. Read the current value of the field.
+2. Update that value in memory to a new value based on the increment or
+decrement.
+3. Write a new value back to the field.
+
+When multiple distributed clients attempt this at the same time,
+updates reaching clients in different orders can
+result in different values on different clients. `MutableRealmInteger`
+improves on this by translating numeric updates into sync operations
+that can be executed in any order to converge to the same value.
+
+`MutableRealmInteger` fields are backed by traditional numeric types,
+so no migration is required when changing a field from `byte`, `short`,
+`int` or `long` to `MutableRealmInteger`.
+
+The following example demonstrates a `MutableRealmInteger` field that
+counts the number of ghosts found in a haunted house:
+
+#### Java
+
+```java
+import io.realm.MutableRealmInteger;
+import io.realm.RealmObject;
+import io.realm.annotations.Required;
+
+public class HauntedHouse extends RealmObject {
+ @Required
+ private final MutableRealmInteger ghosts = MutableRealmInteger.valueOf(0);
+ public HauntedHouse() {}
+ public MutableRealmInteger getGhosts() { return ghosts; }
+}
+
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.MutableRealmInteger
+import io.realm.RealmObject
+import io.realm.annotations.Required
+
+open class HauntedHouse: RealmObject() {
+ @Required
+ val ghosts: MutableRealmInteger = MutableRealmInteger.valueOf(0)
+}
+
+```
+
+> Important:
+> `MutableRealmInteger` is a live object like `RealmObject`,
+`RealmResults` and `RealmList`. This means the value contained
+inside the `MutableRealmInteger` can change when a realm is
+written to. For this reason `MutableRealmInteger` fields must be
+marked final in Java and `val` in Kotlin.
+>
+
+## Usage
+The `counter.increment()`
+and `counter.decrement()`
+operators ensure that increments and decrements from multiple distributed
+clients are aggregated correctly.
+
+To change a `MutableRealmInteger` value, call `increment()` or
+`decrement()` within a write transaction:
+
+#### Java
+
+```java
+HauntedHouse house = realm.where(HauntedHouse.class)
+ .findFirst();
+realm.executeTransaction(r -> {
+ Log.v("EXAMPLE", "Number of ghosts: " + house.getGhosts().get()); // 0
+ house.getGhosts().increment(1);
+ Log.v("EXAMPLE", "Number of ghosts: " + house.getGhosts().get()); // 1
+ house.getGhosts().increment(5);
+ Log.v("EXAMPLE", "Number of ghosts: " + house.getGhosts().get()); // 6
+ house.getGhosts().decrement(2);
+ Log.v("EXAMPLE", "Number of ghosts: " + house.getGhosts().get()); // 4
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+val house = realm.where(HauntedHouse::class.java)
+ .findFirst()!!
+realm.executeTransaction {
+ Log.v("EXAMPLE", "Number of ghosts: ${house.ghosts.get()}") // 0
+ house.ghosts.increment(1)
+ Log.v("EXAMPLE", "Number of ghosts: ${house.ghosts.get()}") // 1
+ house.ghosts.increment(5)
+ Log.v("EXAMPLE", "Number of ghosts: ${house.ghosts.get()}") // 6
+ house.ghosts.decrement(2)
+ Log.v("EXAMPLE", "Number of ghosts: ${house.ghosts.get()}") // 4
+}
+
+```
+
+You can assign a `MutableRealmInteger` a new value with a call to
+`counter.set()`
+within a write transaction.
+
+> Warning:
+> Use the `set()` operator with extreme care. `set()` ignores
+the effects of any prior calls to `increment()` or `decrement()`.
+Although the value of a `MutableRealmInteger` always converges
+across devices, the specific value on which it converges depends on
+the actual order in which operations took place.
+Mixing `set()` with `increment()` and `decrement()` is
+not advised unless fuzzy counting is acceptable.
+>
+
+#### Java
+
+```java
+realm.executeTransaction(r -> {
+ house.getGhosts().set(42);
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+realm.executeTransaction {
+ house!!.ghosts.set(42)
+}
+
+```
+
+Since `MutableRealmInteger` instances retain a reference to their
+parent object, neither object can be garbage collected while you still
+retain a reference to the `MutableRealmInteger`.
diff --git a/docs/guides/model-data/data-types/embedded-objects.md b/docs/guides/model-data/data-types/embedded-objects.md
new file mode 100644
index 0000000000..15b9bf3f6c
--- /dev/null
+++ b/docs/guides/model-data/data-types/embedded-objects.md
@@ -0,0 +1,326 @@
+# Embedded Objects - Java SDK
+An embedded object is a special type of Realm object that models complex data about a specific object.
+Embedded objects are similar to relationships, but they provide additional constraints and
+map more naturally to the denormalized document model.
+
+Realm enforces unique ownership constraints that treat each embedded
+object as nested data inside of a single, specific parent object. An
+embedded object inherits the lifecycle of its parent object and cannot
+exist as an independent Realm object. Realm automatically deletes
+embedded objects if their parent object is deleted or when overwritten
+by a new embedded object instance.
+
+> Warning:
+> When you delete a Realm object, Realm automatically deletes any
+embedded objects referenced by that object. Any objects that your
+application must persist after the deletion of their parent object
+should use relationships
+instead.
+>
+
+## Embedded Object Data Models
+You can define embedded object types using either Realm object models or
+a server-side document schema. Embedded object types are reusable and
+composable. You can use the same embedded object type in multiple parent
+object types and you can embed objects inside of other embedded objects.
+
+> Important:
+> Embedded objects cannot have a primary key.
+>
+
+### Realm Object Models
+To define an embedded object, derive a class from `RealmObject` and set the `embedded` property of the
+`RealmClass` annotation
+to `true`. You can reference an embedded object type from parent
+object types in the same way as you would define a relationship:
+
+#### Java
+
+```java
+// Define an embedded object
+@RealmClass(embedded = true)
+public class Address extends RealmObject {
+ String street;
+ String city;
+ String country;
+ String postalCode;
+
+ public Address(String street, String city, String country, String postalCode) {
+ this.street = street;
+ this.city = city;
+ this.country = country;
+ this.postalCode = postalCode;
+ }
+
+ public Address() {}
+}
+
+// Define an object containing one embedded object
+public class Contact extends RealmObject {
+ @PrimaryKey
+ private ObjectId _id = new ObjectId();
+ String name = "";
+
+ // Embed a single object.
+ // Embedded object properties must be marked optional
+ Address address;
+
+ public Contact(String name, Address address) {
+ this.name = name;
+ this.address = address;
+ }
+
+ public Contact() {}
+}
+
+// Define an object containing an array of embedded objects
+public class Business extends RealmObject {
+ @PrimaryKey
+ private ObjectId _id = new ObjectId();
+ String name = "";
+
+ // Embed an array of objects
+ RealmList addresses = new RealmList();
+
+ public Business(String name, RealmList addresses) {
+ this.name = name;
+ this.addresses = addresses;
+ }
+
+ public Business() {}
+}
+
+```
+
+#### Kotlin
+
+```kotlin
+// Define an embedded object
+@RealmClass(embedded = true)
+open class Address(
+ var street: String? = null,
+ var city: String? = null,
+ var country: String? = null,
+ var postalCode: String? = null
+): RealmObject() {}
+
+// Define an object containing one embedded object
+open class Contact(_name: String = "", _address: Address? = null) : RealmObject() {
+ @PrimaryKey var _id: ObjectId = ObjectId()
+ var name: String = _name
+
+ // Embed a single object.
+ // Embedded object properties must be marked optional
+ var address: Address? = _address
+}
+
+// Define an object containing an array of embedded objects
+open class Business(_name: String = "", _addresses: RealmList = RealmList()) : RealmObject() {
+ @PrimaryKey var _id: ObjectId = ObjectId()
+ var name: String = _name
+
+ // Embed an array of objects
+ var addresses: RealmList = _addresses
+}
+
+```
+
+### JSON Schema
+Embedded objects map to embedded documents in the parent type's schema.
+
+```json
+{
+ "title": "Contact",
+ "bsonType": "object",
+ "required": ["_id"],
+ "properties": {
+ "_id": { "bsonType": "objectId" },
+ "name": { "bsonType": "string" },
+ "address": {
+ "title": "Address",
+ "bsonType": "object",
+ "properties": {
+ "street": { "bsonType": "string" },
+ "city": { "bsonType": "string" },
+ "country": { "bsonType": "string" },
+ "postalCode": { "bsonType": "string" }
+ }
+ }
+ }
+}
+```
+
+```json
+{
+ "title": "Business",
+ "bsonType": "object",
+ "required": ["_id", "name"],
+ "properties": {
+ "_id": { "bsonType": "objectId" },
+ "name": { "bsonType": "string" },
+ "addresses": {
+ "bsonType": "array",
+ "items": {
+ "title": "Address",
+ "bsonType": "object",
+ "properties": {
+ "street": { "bsonType": "string" },
+ "city": { "bsonType": "string" },
+ "country": { "bsonType": "string" },
+ "postalCode": { "bsonType": "string" }
+ }
+ }
+ }
+ }
+}
+```
+
+## Read and Write Embedded Objects
+### Create an Embedded Object
+To create an embedded object, assign an instance of the embedded object
+to a parent object's property.
+
+#### Java
+
+```java
+// open realm
+
+Address address = new Address("123 Fake St.", "Springfield", "USA", "90710");
+Contact contact = new Contact("Nick Riviera", address);
+
+realm.executeTransaction(transactionRealm -> {
+ transactionRealm.insert(contact);
+});
+
+realm.close();
+
+```
+
+#### Kotlin
+
+```kotlin
+// open realm
+
+val address = Address("123 Fake St.", "Springfield", "USA", "90710")
+val contact = Contact("Nick Riviera", address)
+
+realm.executeTransaction { transactionRealm ->
+ transactionRealm.insert(contact)
+}
+
+realm.close()
+
+```
+
+### Update an Embedded Object Property
+To update a property in an embedded object, modify the property in a
+write transaction:
+
+#### Java
+
+```java
+// assumes that at least one contact already exists in this partition
+Contact resultContact = realm.where(Contact.class).findFirst();
+
+realm.executeTransaction(transactionRealm -> {
+ resultContact.address.street = "Hollywood Upstairs Medical College";
+ resultContact.address.city = "Los Angeles";
+ resultContact.address.postalCode = "90210";
+ Log.v("EXAMPLE", "Updated contact: " + resultContact);
+});
+
+realm.close();
+
+```
+
+#### Kotlin
+
+```kotlin
+// assumes that at least one contact already exists in this partition
+val result = realm.where().findFirst()!!
+
+realm.executeTransaction { transactionRealm ->
+ result.address?.street = "Hollywood Upstairs Medical College"
+ result.address?.city = "Los Angeles"
+ result.address?.postalCode = "90210"
+ Log.v("EXAMPLE", "Updated contact: ${result.name}")
+}
+
+realm.close()
+
+```
+
+### Overwrite an Embedded Object
+To overwrite an embedded object, reassign the embedded object property
+of a party to a new instance in a write transaction:
+
+#### Java
+
+```java
+// assumes that at least one contact already exists in this partition
+Contact oldContact = realm.where(Contact.class).findFirst();
+
+realm.executeTransaction(transactionRealm -> {
+ Address newAddress = new Address(
+ "Hollywood Upstairs Medical College",
+ "Los Angeles",
+ "USA"
+ "90210"
+ );
+ oldContact.address = newAddress;
+ Log.v("EXAMPLE", "Replaced contact: " + oldContact);
+});
+
+realm.close();
+
+```
+
+#### Kotlin
+
+```kotlin
+// assumes that at least one contact already exists
+val oldContact = realm.where().findFirst()!!
+
+realm.executeTransaction { transactionRealm ->
+ val newAddress = Address(
+ "Hollywood Upstairs Medical College",
+ "Los Angeles",
+ "USA",
+ "90210")
+ oldContact.address = newAddress
+ Log.v("EXAMPLE", "Updated contact: $oldContact")
+}
+
+realm.close()
+
+```
+
+### Query a Collection on Embedded Object Properties
+Use dot notation to filter or sort a collection of objects based on an embedded object
+property value:
+
+> Note:
+> It is not possible to query embedded objects directly. Instead,
+access embedded objects through a query for the parent object type.
+>
+
+#### Java
+
+```java
+RealmResults losAngelesContacts = realm.where(Contact.class)
+ .equalTo("address.city", "Los Angeles")
+ .sort("address.street").findAll();
+Log.v("EXAMPLE", "Los Angeles contacts: " + losAngelesContacts);
+
+```
+
+#### Kotlin
+
+```kotlin
+val losAngelesContacts = realm.where()
+ .equalTo("address.city", "Los Angeles")
+ .sort("address.street").findAll()
+Log.v("EXAMPLE", "Los Angeles Contacts: $losAngelesContacts")
+
+```
+
diff --git a/docs/guides/model-data/data-types/enums.md b/docs/guides/model-data/data-types/enums.md
new file mode 100644
index 0000000000..703e5858ea
--- /dev/null
+++ b/docs/guides/model-data/data-types/enums.md
@@ -0,0 +1,123 @@
+# Enumerations - Java SDK
+Enumerations, also known as enums, are not supported natively in the
+Java SDK. However, you can use Java and Kotlin enums in your
+Realm objects if you follow these steps.
+
+## Usage
+To use an enum in a Realm object class, define a field
+with a type matching the underlying data type of your enum. Create
+getters and setters for the field that convert the field value between
+the underlying value and the enum type. You can use the Java's built-in
+[Enum.valueOf()](https://docs.oracle.com/javase/7/docs/api/java/lang/Enum.html#valueOf(java.lang.Class,%20java.lang.String))
+method to convert from the underlying type to the enum type.
+
+#### Java
+
+```kotlin
+public enum FrogState {
+ TADPOLE("Tadpole"),
+ FROG("Frog"),
+ OLD_FROG("Old Frog");
+
+ private String state;
+ FrogState(String state) {
+ this.state = state;
+ }
+ public String getState() {
+ return state;
+ }
+}
+
+```
+
+```java
+import io.realm.RealmObject;
+
+public class Frog extends RealmObject {
+ String name;
+ String state = FrogState.TADPOLE.getState();
+ // realm-required empty constructor
+ public Frog() {}
+
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public FrogState getState() {
+ // because state is actually a String and another client could assign an invalid value,
+ // default the state to "TADPOLE" if the state is unreadable
+ FrogState currentState = null;
+ try {
+ // fetches the FrogState enum value associated with the current internal string value
+ currentState = FrogState.valueOf(state);
+ } catch (IllegalArgumentException e) {
+ currentState = FrogState.TADPOLE;
+ }
+ return currentState;
+ }
+ public void setState(FrogState value) {
+ // users set state using a FrogState, but it is saved as a string internally
+ this.state = value.getState();
+ }
+}
+
+```
+
+```java
+Frog frog = realm.createObject(Frog.class);
+frog.setName("Jonathan Livingston Applesauce");
+// set the state using the enum
+frog.setState(FrogState.FROG);
+
+// fetching the state returns an enum
+FrogState currentJonathanState = frog.getState();
+
+```
+
+#### Kotlin
+
+```kotlin
+enum class FrogState(val state: String) {
+ TADPOLE("Tadpole"),
+ FROG("Frog"),
+ OLD_FROG("Old Frog");
+}
+
+```
+
+```kotlin
+import io.realm.RealmObject
+import java.lang.IllegalArgumentException
+
+open class Frog // realm-required empty constructor
+ : RealmObject() {
+ var name: String? = null
+ private var state: String = FrogState.TADPOLE.state
+ var stateEnum: FrogState
+ get() {
+ // because state is actually a String and another client could assign an invalid value,
+ // default the state to "TADPOLE" if the state is unreadable
+ return try {
+ // fetches the FrogState enum value associated with the current internal string value
+ FrogState.valueOf(state)
+ } catch (e: IllegalArgumentException) {
+ FrogState.TADPOLE
+ }
+ }
+ set(value) {
+ // users set state using a FrogState, but it is saved as a string internally
+ state = value.state
+ }
+}
+
+```
+
+```kotlin
+val frog = realm.createObject(Frog::class.java)
+frog.name = "Jonathan Livingston Applesauce"
+// set the state using the enum
+frog.stateEnum = FrogState.FROG
+
+// fetching the state returns an enum
+val currentJonathanState: FrogState = frog.stateEnum
+
+```
+
diff --git a/docs/guides/model-data/data-types/field-types.md b/docs/guides/model-data/data-types/field-types.md
new file mode 100644
index 0000000000..8926ad7fcd
--- /dev/null
+++ b/docs/guides/model-data/data-types/field-types.md
@@ -0,0 +1,44 @@
+# Field Types - Java SDK
+Realm supports the following field data types:
+
+- `Boolean` or `boolean`
+- `Integer` or `int`
+- `Short` or `short`
+- `Long` or `long`
+- `Byte` or `byte[]`
+- `Double` or `double`
+- `Float` or `float`
+- `String`
+- `Date`
+- `Decimal128` from `org.bson.types`
+- `ObjectId` from `org.bson.types`
+- `UUID` from `java.util.UUID`
+- Any `RealmObject` subclass
+- `RealmList`
+- `RealmAny`
+- `RealmSet`
+- `RealmDictionary`
+
+The `Byte`, `Short`, `Integer`, and `Long` types and their
+lowercase primitive alternatives are all stored as `Long` values
+within Realm. Similarly, Realm stores objects
+of the `Float` and `float` types as type `Double`.
+
+Realm does not support fields with modifiers `final` and
+`volatile`, though you can use fields with those modifiers if you
+ignore them. If you choose to provide custom
+constructors, you must declare a public constructor with no arguments.
+
+## Updating Strings and Byte Arrays
+Since Realm operates on fields as a whole, it's not possible
+to directly update individual elements of strings or byte arrays. Instead,
+you'll need to read the whole field, make your modification to individual
+elements, and then write the entire field back again in a transaction block.
+
+## Object IDs and UUIDs
+`ObjectId` and `UUID` (Universal Unique Identifier) both provide
+unique values that can be used as identifiers for objects.
+`ObjectId` is a
+12-byte unique value. `UUID` is a [standardized](https://tools.ietf.org/html/rfc4122) 16-byte
+unique value. Both types are indexable
+and can be used as primary keys.
diff --git a/docs/guides/model-data/data-types/realmany.md b/docs/guides/model-data/data-types/realmany.md
new file mode 100644
index 0000000000..3dd1f28f10
--- /dev/null
+++ b/docs/guides/model-data/data-types/realmany.md
@@ -0,0 +1,293 @@
+# RealmAny - Java SDK
+> Version added: 10.6.0
+
+You can use the `RealmAny` data type to create
+Realm object fields that can contain any of several
+underlying types. You can store multiple `RealmAny` instances in
+`RealmList`, `RealmDictionary`, or `RealmSet` fields. To change
+the value of a `RealmAny` field, assign a new `RealmAny` instance
+with a different underlying value. `RealmAny` fields are indexable, but
+cannot be used as primary keys.
+
+> Note:
+> `RealmAny` objects can refer to any
+supported field type
+*except*:
+>
+> - `RealmAny`
+> - `RealmList`
+> - `RealmSet`
+> - `RealmDictionary`
+>
+
+## Usage
+To create a `RealmAny` instance, use the
+`RealmAny.valueOf()` method
+to assign an initial value or `RealmAny.nullValue()` to assign no
+value. `RealmAny` instances are immutable just like `String` or
+`Integer` instances; if you want to assign a new value to a
+`RealmAny` field, you must create a new `RealmAny` instance.
+
+> Warning:
+> `RealmAny` instances are always nullable. Additionally, instances can contain a value
+of type `RealmAny.Type.NULL`.
+>
+
+#### Java
+
+```java
+import com.mongodb.realm.examples.model.kotlin.Person;
+
+import io.realm.RealmAny;
+import io.realm.RealmObject;
+
+public class Frog extends RealmObject {
+ String name;
+ RealmAny bestFriend;
+ // realm-required empty constructor
+ public Frog() {}
+
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public RealmAny getBestFriend() { return bestFriend; }
+ public void setBestFriend(RealmAny bestFriend) { this.bestFriend = bestFriend; }
+ public String bestFriendToString() {
+ switch(bestFriend.getType()) {
+ case NULL: {
+ return "no best friend";
+ }
+ case STRING: {
+ return bestFriend.asString();
+ }
+ case OBJECT: {
+ if (bestFriend.getValueClass().equals(Person.class)) {
+ Person person = bestFriend.asRealmModel(Person.class);
+ return person.getName();
+ }
+ }
+ default: {
+ return "unknown type";
+ }
+ }
+ }
+}
+
+```
+
+```java
+ Frog frog = realm.createObject(Frog.class);
+ frog.setName("Jonathan Livingston Applesauce");
+
+ // set RealmAny field to a null value
+ frog.setBestFriend(RealmAny.nullValue());
+ Log.v("EXAMPLE", "Best friend: " + frog.bestFriendToString());
+
+ // possible types for RealmAny are defined in RealmAny.Type
+ Assert.assertTrue(frog.getBestFriend().getType() == RealmAny.Type.NULL);
+
+ // set RealmAny field to a string with RealmAny.valueOf a string value
+ frog.setBestFriend(RealmAny.valueOf("Greg"));
+ Log.v("EXAMPLE", "Best friend: " + frog.bestFriendToString());
+
+ // RealmAny instances change type as you reassign to different values
+ Assert.assertTrue(frog.getBestFriend().getType() == RealmAny.Type.STRING);
+
+ // set RealmAny field to a realm object, also with valueOf
+ Person person = new Person("Jason Funderburker");
+
+ frog.setBestFriend(RealmAny.valueOf(person));
+ Log.v("EXAMPLE", "Best friend: " + frog.bestFriendToString());
+
+ // You can also extract underlying Realm Objects from RealmAny with asRealmModel
+ Person bestFriendObject = frog.getBestFriend().asRealmModel(Person.class);
+ Log.v("EXAMPLE", "Best friend: " + bestFriendObject.getName());
+
+ // RealmAny fields referring to any Realm Object use the OBJECT type
+ Assert.assertTrue(frog.getBestFriend().getType() == RealmAny.Type.OBJECT);
+
+ // you can't put a RealmList in a RealmAny field directly,
+ // ...but you can set a RealmAny field to a RealmObject that contains a list
+ GroupOfPeople persons = new GroupOfPeople();
+ // GroupOfPeople contains a RealmList of people
+ persons.getPeople().add("Rand");
+ persons.getPeople().add("Perrin");
+ persons.getPeople().add("Mat");
+
+ frog.setBestFriend(RealmAny.valueOf(persons));
+ Log.v("EXAMPLE", "Best friend: " +
+ frog.getBestFriend().asRealmModel(GroupOfPeople.class).getPeople().toString());
+
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmAny
+import io.realm.RealmObject
+
+open class Frog(var bestFriend: RealmAny? = RealmAny.nullValue()) : RealmObject() {
+ var name: String? = null
+ open fun bestFriendToString(): String {
+ if (bestFriend == null) {
+ return "null"
+ }
+ return when (bestFriend!!.type) {
+ RealmAny.Type.NULL -> {
+ "no best friend"
+ }
+ RealmAny.Type.STRING -> {
+ bestFriend!!.asString()
+ }
+ RealmAny.Type.OBJECT -> {
+ if (bestFriend!!.valueClass == Person::class.java) {
+ val person = bestFriend!!.asRealmModel(Person::class.java)
+ person.name
+ }
+ "unknown type"
+ }
+ else -> {
+ "unknown type"
+ }
+ }
+ }
+}
+
+```
+
+```kotlin
+val frog = realm.createObject(Frog::class.java)
+frog.name = "George Washington"
+
+// set RealmAny field to a null value
+
+// set RealmAny field to a null value
+frog.bestFriend = RealmAny.nullValue()
+Log.v("EXAMPLE", "Best friend: " + frog.bestFriendToString())
+
+// possible types for RealmAny are defined in RealmAny.Type
+Assert.assertEquals(frog.bestFriend?.type, RealmAny.Type.NULL)
+
+// set RealmAny field to a string with RealmAny.valueOf a string value
+frog.bestFriend = RealmAny.valueOf("Greg")
+Log.v("EXAMPLE", "Best friend: " + frog.bestFriendToString())
+
+// RealmAny instances change type as you reassign to different values
+Assert.assertEquals(frog.bestFriend?.type, RealmAny.Type.STRING)
+
+// set RealmAny field to a realm object, also with valueOf
+val person = Person("Jason Funderburker")
+
+frog.bestFriend = RealmAny.valueOf(person)
+Log.v("EXAMPLE", "Best friend: " + frog.bestFriendToString())
+
+// You can also extract underlying Realm Objects from RealmAny with asRealmModel
+val bestFriendObject = frog.bestFriend?.asRealmModel(Person::class.java)
+Log.v("EXAMPLE", "Best friend: " + bestFriendObject?.name)
+
+// RealmAny fields referring to any Realm Object use the OBJECT type
+Assert.assertEquals(frog.bestFriend?.type, RealmAny.Type.OBJECT)
+
+// you can't put a RealmList in a RealmAny field directly,
+// ...but you can set a RealmAny field to a RealmObject that contains a list
+val persons = GroupOfPeople()
+// GroupOfPeople contains a RealmList of people
+persons.people.add("Rand")
+persons.people.add("Perrin")
+persons.people.add("Mat")
+
+frog.bestFriend = RealmAny.valueOf(persons)
+Log.v("EXAMPLE", "Best friend: " +
+ frog.bestFriend?.asRealmModel(GroupOfPeople::class.java)
+ ?.people.toString())
+
+```
+
+## Queries
+You can query a `RealmAny` field just like any other data type.
+Operators that only work with certain types, such as string
+operators and arithmetic operators, ignore
+values that do not contain that type. Negating such operators matches
+values that do not contain the type. Type queries match the underlying
+type, rather than `RealmAny`. Arithmetic operators convert numeric
+values implicitly to compare across types.
+
+## Notifications
+To subscribe to changes to a `RealmAny` field, use the
+`RealmObject.addChangeListener`
+method of the enclosing object. You can use the
+`ObjectChangeSet`
+parameter to determine if the `RealmAny` field changed.
+
+#### Java
+
+```java
+AtomicReference frog = new AtomicReference();
+realm.executeTransaction(r -> {
+ frog.set(realm.createObject(Frog.class));
+ frog.get().setName("Jonathan Livingston Applesauce");
+});
+
+RealmObjectChangeListener objectChangeListener =
+ new RealmObjectChangeListener() {
+ @Override
+ public void onChange(@NotNull Frog frog, @Nullable ObjectChangeSet changeSet) {
+ if (changeSet != null) {
+ Log.v("EXAMPLE", "Changes to fields: " +
+ Arrays.toString(changeSet.getChangedFields()));
+ if (changeSet.isFieldChanged("best_friend")) {
+ Log.v("EXAMPLE", "RealmAny best friend field changed to : " +
+ frog.bestFriendToString());
+ }
+ }
+ }
+};
+
+frog.get().addChangeListener(objectChangeListener);
+
+realm.executeTransaction(r -> {
+ // set RealmAny field to a null value
+ frog.get().setBestFriend(RealmAny.nullValue());
+ Log.v("EXAMPLE", "Best friend: " + frog.get().bestFriendToString());
+
+ // set RealmAny field to a string with RealmAny.valueOf a string value
+ frog.get().setBestFriend(RealmAny.valueOf("Greg"));
+
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+var frog: Frog? = null
+
+realm.executeTransaction { r: Realm? ->
+ frog = realm.createObject(Frog::class.java)
+ frog?.name = "Jonathan Livingston Applesauce"
+}
+
+val objectChangeListener
+ = RealmObjectChangeListener { frog, changeSet ->
+ if (changeSet != null) {
+ Log.v("EXAMPLE", "Changes to fields: " +
+ changeSet.changedFields)
+ if (changeSet.isFieldChanged("best_friend")) {
+ Log.v("EXAMPLE", "RealmAny best friend field changed to : " +
+ frog.bestFriendToString())
+ }
+ }
+}
+
+frog?.addChangeListener(objectChangeListener)
+
+realm.executeTransaction { r: Realm? ->
+ // set RealmAny field to a null value
+ frog?.bestFriend = RealmAny.nullValue()
+ Log.v("EXAMPLE", "Best friend: " + frog?.bestFriendToString())
+
+ // set RealmAny field to a string with RealmAny.valueOf a string value
+ frog?.bestFriend = RealmAny.valueOf("Greg")
+}
+
+```
+
diff --git a/docs/guides/model-data/data-types/realmdictionary.md b/docs/guides/model-data/data-types/realmdictionary.md
new file mode 100644
index 0000000000..c2dec0e899
--- /dev/null
+++ b/docs/guides/model-data/data-types/realmdictionary.md
@@ -0,0 +1,244 @@
+# RealmDictionary - Java SDK
+> Version added: 10.6.0
+
+You can use the `RealmDictionary` data type to manage a collection of
+unique `String` keys paired with values. `RealmDictionary`
+implements Java's `Map` interface, so it works just like the built-in
+`HashMap` class, except managed `RealmDictionary` instances persist
+their contents to a realm. `RealmDictionary` instances that
+contain Realm objects store references to those objects.
+When you delete a Realm object from a realm, any
+references to that object in a `RealmDictionary` become `null`
+values.
+
+## Usage
+To create a field of type `RealmDictionary`, define an object property
+of type `RealmDictionary`, where `T` defines the values you would
+like to store in your `RealmDictionary`. Currently, `RealmDictionary`
+instances can only use keys of type `String`.
+
+The following table shows which methods you can use to complete common
+collection tasks with `RealmDictionary`:
+
+|Task|Method|
+| --- | --- |
+|Add an object to a `RealmDictionary`|`put()` (or the `[]` operator in Kotlin)|
+|Add multiple objects to a `RealmDictionary`|`putAll()`|
+|Check if the dictionary contains an specific key|`containsKey()`|
+|Check if the dictionary contains a specific value|`containsValue()`|
+
+#### Java
+
+```java
+import io.realm.RealmDictionary;
+import io.realm.RealmObject;
+
+public class Frog extends RealmObject {
+ String name;
+ RealmDictionary nicknamesToFriends;
+ // realm-required empty constructor
+ public Frog() {}
+
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public RealmDictionary getNicknamesToFriends() { return nicknamesToFriends; }
+ public void setNicknamesToFriends(RealmDictionary nicknamesToFriends) { this.nicknamesToFriends = nicknamesToFriends; }
+}
+
+```
+
+```java
+Frog frog = realm.createObject(Frog.class);
+frog.setName("George Washington");
+
+// get the RealmDictionary field from the object we just created
+RealmDictionary dictionary = frog.getNicknamesToFriends();
+
+// add key/value to the dictionary
+Frog wirt = realm.createObject(Frog.class);
+wirt.setName("Wirt");
+dictionary.put("tall frog", wirt);
+
+// add multiple keys/values to the dictionary
+Frog greg = realm.createObject(Frog.class);
+greg.setName("Greg");
+Frog beatrice = realm.createObject(Frog.class);
+beatrice.setName("Beatrice");
+dictionary.putAll(Map.of("small frog", greg, "feathered frog", beatrice));
+
+// check for the presence of a key
+Assert.assertTrue(dictionary.containsKey("small frog"));
+
+// check for the presence of a value
+Assert.assertTrue(dictionary.containsValue(greg));
+
+// remove a key
+dictionary.remove("feathered frog");
+Assert.assertFalse(dictionary.containsKey("feathered frog"));
+
+// deleting a Realm object does NOT remove it from the dictionary
+int sizeOfDictionaryBeforeDelete = dictionary.size();
+greg.deleteFromRealm();
+// deleting greg object did not reduce the size of the dictionary
+Assert.assertEquals(sizeOfDictionaryBeforeDelete, dictionary.size());
+// but greg object IS now null:
+Assert.assertEquals(dictionary.get("small frog"), null);
+
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmDictionary
+import io.realm.RealmObject
+
+open class Frog
+ : RealmObject() {
+ var name: String? = null
+ var nicknamesToFriends: RealmDictionary = RealmDictionary()
+}
+
+```
+
+```kotlin
+val frog =
+ realm.createObject(Frog::class.java)
+frog.name = "George Washington"
+
+// get the RealmDictionary field from the object we just created
+val dictionary = frog.nicknamesToFriends
+
+// add key/value to the dictionary
+val wirt =
+ realm.createObject(Frog::class.java)
+wirt.name = "Wirt"
+dictionary["tall frog"] = wirt
+
+// add multiple keys/values to the dictionary
+val greg =
+ realm.createObject(Frog::class.java)
+greg.name = "Greg"
+val beatrice =
+ realm.createObject(Frog::class.java)
+beatrice.name = "Beatrice"
+dictionary.putAll(mapOf(
+ Pair("small frog", greg),
+ Pair("feathered frog", beatrice)))
+
+// check for the presence of a key
+Assert.assertTrue(dictionary.containsKey("small frog"))
+
+// check for the presence of a value
+Assert.assertTrue(dictionary.containsValue(greg))
+
+// remove a key
+dictionary.remove("feathered frog")
+Assert.assertFalse(dictionary.containsKey("feathered frog"))
+
+// deleting a Realm object does NOT remove it from the dictionary
+val sizeOfDictionaryBeforeDelete = dictionary.size
+greg.deleteFromRealm()
+// deleting greg object did not reduce the size of the dictionary
+Assert.assertEquals(
+ sizeOfDictionaryBeforeDelete.toLong(),
+ dictionary.size.toLong()
+)
+// but greg object IS now null:
+Assert.assertEquals(dictionary["small frog"], null)
+
+```
+
+## Notifications
+To subscribe to changes to a `RealmDictionary`, pass a
+`MapChangeListener`
+implementation to the `RealmSet.addChangeListener` method.
+Your `MapChangeListener` implementation must define an
+`onChange()` method, which accepts a reference to the changed `RealmDictionary`
+and a set of changes as parameters. You can access the keys
+added to the dictionary as well as the keys removed from the dictionary
+through the `MapChangeSet` parameter.
+
+#### Java
+
+```java
+AtomicReference frog = new AtomicReference();
+realm.executeTransaction(r -> {
+ frog.set(realm.createObject(Frog.class));
+ frog.get().setName("Jonathan Livingston Applesauce");
+});
+
+MapChangeListener mapChangeListener =
+ new MapChangeListener() {
+ @Override
+ public void onChange(RealmMap map,
+ MapChangeSet changes) {
+ for (String insertion : changes.getInsertions()) {
+ Log.v("EXAMPLE",
+ "Inserted key: " + insertion +
+ ", Inserted value: " + map.get(insertion).getName());
+ }
+ }
+ };
+
+frog.get().getNicknamesToFriends().addChangeListener(mapChangeListener);
+
+realm.executeTransaction(r -> {
+ // get the RealmDictionary field from the object we just created
+ RealmDictionary dictionary = frog.get().getNicknamesToFriends();
+
+ // add key/value to the dictionary
+ Frog wirt = realm.createObject(Frog.class);
+ wirt.setName("Wirt");
+ dictionary.put("tall frog", wirt);
+
+ // add multiple keys/values to the dictionary
+ Frog greg = realm.createObject(Frog.class);
+ greg.setName("Greg");
+ Frog beatrice = realm.createObject(Frog.class);
+ beatrice.setName("Beatrice");
+ dictionary.putAll(Map.of("small frog", greg, "feathered frog", beatrice));
+
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+var frog: Frog? = null
+realm.executeTransaction { r: Realm? ->
+ frog = realm.createObject(Frog::class.java)
+ frog?.name = "Jonathan Livingston Applesauce"
+}
+
+val mapChangeListener: MapChangeListener
+ = MapChangeListener { map, changes ->
+ for (insertion in changes.insertions) {
+ Log.v("EXAMPLE",
+ "Inserted key: $insertion, Inserted value: ${map[insertion]!!.name}")
+ }
+}
+
+frog?.nicknamesToFriends?.addChangeListener(mapChangeListener)
+
+realm.executeTransaction { r: Realm? ->
+ // get the RealmDictionary field from the object we just created
+ val dictionary = frog!!.nicknamesToFriends
+
+ // add key/value to the dictionary
+ val wirt = realm.createObject(Frog::class.java)
+ wirt.name = "Wirt"
+ dictionary["tall frog"] = wirt
+
+ // add multiple keys/values to the dictionary
+ val greg = realm.createObject(Frog::class.java)
+ greg.name = "Greg"
+ val beatrice = realm.createObject(Frog::class.java)
+ beatrice.name = "Beatrice"
+ dictionary.putAll(mapOf(
+ Pair("small frog", greg),
+ Pair("feathered frog", beatrice)))
+}
+
+```
+
diff --git a/docs/guides/model-data/data-types/realmset.md b/docs/guides/model-data/data-types/realmset.md
new file mode 100644
index 0000000000..618f5de4bc
--- /dev/null
+++ b/docs/guides/model-data/data-types/realmset.md
@@ -0,0 +1,265 @@
+# RealmSet - Java SDK
+> Version added: 10.6.0
+
+You can use the `RealmSet` data type
+to manage a collection of unique keys. `RealmSet` implements Java's
+`Set` interface, so it works just like the built-in `HashSet` class,
+except managed `RealmSet` instances persist their contents to a
+realm. `RealmSet` instances that contain Realm objects
+actually only store references to those objects, so deleting a
+Realm object from a realm also deletes that object from
+any `RealmSet` instances that contain the object.
+
+Because `RealmSet` implements `RealmCollection`, it has some useful
+mathematical methods, such as `sum`, `min`, and `max`. For a complete
+list of available `RealmSet` methods, see: [the RealmSet API
+reference](https://www.mongodb.com/docs/realm-sdks/java/latest/io/realm/RealmSet.html).
+
+## Method Limitations
+You cannot use the following `Realm` methods on objects that contain
+a field of type `RealmSet`:
+
+- `Realm.insert()`
+- `Realm.insertOrUpdate()`
+- `Realm.createAllFromJson()`
+- `Realm.createObjectFromJson()`
+- `Realm.createOrUpdateAllFromJson()`
+- `Realm.createOrUpdateObjectFromJson()`
+
+## Usage
+To create a field of type `RealmSet`, define an object property of
+type `RealmSet`, where `E` defines the keys you would like to
+store in your `RealmSet`.
+
+- Add an object to a `RealmSet` with
+`RealmSet.add()`
+- Add multiple objects with
+`RealmSet.addAll()`
+- Check if the set contains a specific object with
+`RealmSet.contains()`
+- Check if the set contains all of multiple objects with
+`RealmSet.containsAll()`
+
+#### Java
+
+```java
+import io.realm.RealmObject;
+import io.realm.RealmSet;
+
+public class Frog extends RealmObject {
+ String name;
+ RealmSet favoriteSnacks;
+ // realm-required empty constructor
+ public Frog() {}
+
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public RealmSet getFavoriteSnacks() { return favoriteSnacks; }
+ public void setFavoriteSnacks(RealmSet favoriteSnacks) { this.favoriteSnacks = favoriteSnacks; }
+}
+
+```
+
+```java
+import io.realm.RealmObject;
+
+public class Snack extends RealmObject {
+ private String name;
+ public Snack() {}
+
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+}
+
+```
+
+```java
+Frog frog = realm.createObject(Frog.class);
+frog.setName("George Washington");
+
+// get the RealmSet field from the object we just created
+RealmSet set = frog.getFavoriteSnacks();
+
+// add value to the RealmSet
+Snack flies = realm.createObject(Snack.class);
+flies.setName("flies");
+set.add(flies);
+
+// add multiple values to the RealmSet
+Snack water = realm.createObject(Snack.class);
+water.setName("water");
+Snack verySmallRocks = realm.createObject(Snack.class);
+verySmallRocks.setName("verySmallRocks");
+set.addAll(Arrays.asList(water, verySmallRocks));
+
+// check for the presence of a key with contains
+Assert.assertTrue(set.contains(flies));
+
+// check for the presence of multiple keys with containsAll
+Snack biscuits = realm.createObject(Snack.class);
+biscuits.setName("biscuits");
+Assert.assertTrue(set.containsAll(Arrays.asList(water, biscuits)) == false);
+
+// remove string from a set
+set.remove(verySmallRocks);
+
+// set no longer contains that string
+Assert.assertTrue(set.contains(verySmallRocks) == false);
+
+// deleting a Realm object also removes it from any RealmSets
+int sizeOfSetBeforeDelete = set.size();
+flies.deleteFromRealm();
+// deleting flies object reduced the size of the set by one
+Assert.assertTrue(sizeOfSetBeforeDelete == set.size() + 1);
+
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmObject
+import io.realm.RealmSet
+
+open class Frog
+ : RealmObject() {
+ var name: String = ""
+ var favoriteSnacks: RealmSet = RealmSet();
+}
+
+```
+
+```kotlin
+import io.realm.RealmObject
+
+open class Snack : RealmObject() {
+ var name: String? = null
+}
+
+```
+
+```kotlin
+val frog = realm.createObject(Frog::class.java)
+frog.name = "Jonathan Livingston Applesauce"
+
+// get the RealmSet field from the object we just created
+val set = frog.favoriteSnacks
+
+// add value to the RealmSet
+val flies = realm.createObject(Snack::class.java)
+flies.name = "flies"
+set.add(flies)
+
+// add multiple values to the RealmSet
+val water = realm.createObject(Snack::class.java)
+water.name = "water"
+val verySmallRocks = realm.createObject(Snack::class.java)
+verySmallRocks.name = "verySmallRocks"
+set.addAll(listOf(water, verySmallRocks))
+
+// check for the presence of a key with contains
+Assert.assertTrue(set.contains(flies))
+
+// check for the presence of multiple keys with containsAll
+val biscuits = realm.createObject(Snack::class.java)
+biscuits.name = "biscuits"
+Assert.assertTrue(set.containsAll(Arrays.asList(water, biscuits)) == false)
+
+// remove string from a set
+set.remove(verySmallRocks)
+
+// set no longer contains that string
+Assert.assertTrue(set.contains(verySmallRocks) == false)
+
+// deleting a Realm object also removes it from any RealmSets
+val sizeOfSetBeforeDelete = set.size
+flies.deleteFromRealm()
+// deleting flies object reduced the size of the set by one
+Assert.assertTrue(sizeOfSetBeforeDelete == set.size + 1)
+
+```
+
+## Notifications
+To subscribe to changes to a `RealmSet`, pass a
+`SetChangeListener`
+implementation to the `RealmSet.addChangeListener` method.
+Your `SetChangeListener` implementation must define an
+`onChange()` method, which accepts a reference to the changed `RealmSet`
+and a set of changes as parameters. You can access the number of items
+added to the set as well as the number of items removed from the set
+through the `SetChangeSet` parameter.
+
+#### Java
+
+```java
+AtomicReference frog = new AtomicReference();
+realm.executeTransaction(r -> {
+ frog.set(realm.createObject(Frog.class));
+ frog.get().setName("Jonathan Livingston Applesauce");
+});
+
+SetChangeListener setChangeListener = new SetChangeListener() {
+ @Override
+ public void onChange(@NotNull RealmSet set, SetChangeSet changes) {
+ Log.v("EXAMPLE", "Set changed: " +
+ changes.getNumberOfInsertions() + " new items, " +
+ changes.getNumberOfDeletions() + " items removed.");
+ }
+};
+frog.get().getFavoriteSnacks().addChangeListener(setChangeListener);
+
+realm.executeTransaction(r -> {
+ // get the RealmSet field from the object we just created
+ RealmSet set = frog.get().getFavoriteSnacks();
+
+ // add value to the RealmSet
+ Snack flies = realm.createObject(Snack.class);
+ flies.setName("flies");
+ set.add(flies);
+
+ // add multiple values to the RealmSet
+ Snack water = realm.createObject(Snack.class);
+ water.setName("water");
+ Snack verySmallRocks = realm.createObject(Snack.class);
+ verySmallRocks.setName("verySmallRocks");
+ set.addAll(Arrays.asList(water, verySmallRocks));
+
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+var frog :Frog? = null
+realm.executeTransaction { r: Realm? ->
+ frog = realm.createObject(Frog::class.java)
+ frog?.name = "Jonathan Livingston Applesauce"
+}
+
+val setChangeListener: SetChangeListener
+ = SetChangeListener { set, changes ->
+ Log.v("EXAMPLE", "Set changed: " +
+ changes.numberOfInsertions + " new items, " +
+ changes.numberOfDeletions + " items removed.")
+}
+frog?.favoriteSnacks?.addChangeListener(setChangeListener)
+
+realm.executeTransaction { r: Realm? ->
+ // get the RealmSet field from the object we just created
+ val set = frog!!.favoriteSnacks
+
+ // add value to the RealmSet
+ val flies = realm.createObject(Snack::class.java)
+ flies.name = "flies"
+ set.add(flies)
+
+ // add multiple values to the RealmSet
+ val water = realm.createObject(Snack::class.java)
+ water.name = "water"
+ val verySmallRocks = realm.createObject(Snack::class.java)
+ verySmallRocks.name = "verySmallRocks"
+ set.addAll(Arrays.asList(water, verySmallRocks))
+}
+
+```
+
diff --git a/docs/guides/model-data/define-a-realm-object-model.md b/docs/guides/model-data/define-a-realm-object-model.md
new file mode 100644
index 0000000000..dbf56218b0
--- /dev/null
+++ b/docs/guides/model-data/define-a-realm-object-model.md
@@ -0,0 +1,1095 @@
+# Define a Realm Object Model - Java SDK
+## Define a Realm Object
+To define a Realm object in your application,
+create a subclass of `RealmObject`
+or implement `RealmModel`.
+
+> Important:
+> - All Realm objects must provide an empty constructor.
+> - All Realm objects must use the `public` visibility modifier in Java
+or the `open` visibility modifier in Kotlin.
+>
+
+> Note:
+> Class names are limited to a maximum of 57 UTF-8 characters.
+>
+
+### Extend RealmObject
+The following code block shows a Realm object that
+describes a Frog. This Frog class can be stored in
+Realm because it `extends` the `RealmObject` class.
+
+#### Java
+
+```java
+import io.realm.RealmObject;
+
+// To add an object to your Realm Schema, extend RealmObject
+public class Frog extends RealmObject {
+ private String name;
+ private int age;
+ private String species;
+ private String owner;
+ public Frog(String name, int age, String species, String owner) {
+ this.name = name;
+ this.age = age;
+ this.species = species;
+ this.owner = owner;
+ }
+ public Frog(){} // RealmObject subclasses must provide an empty constructor
+
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public int getAge() { return age; }
+ public void setAge(int age) { this.age = age; }
+ public String getSpecies() { return species; }
+ public void setSpecies(String species) { this.species = species; }
+ public String getOwner() { return owner; }
+ public void setOwner(String owner) { this.owner = owner; }
+}
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmObject
+
+// providing default values for each constructor parameter
+// fulfills the need for an empty constructor
+open class Frog(
+ var name: String? = null,
+ var age: Int = 0,
+ var species: String? = null,
+ var owner: String? = null
+) : RealmObject() // To add an object to your Realm Schema, extend RealmObject
+```
+
+### Implement RealmModel
+The following code block shows a Realm object that
+describes a Frog. This Frog class can
+be stored in Realm because it `implements` the
+`RealmModel` class and uses the `@RealmClass` annotation:
+
+#### Java
+
+```java
+import io.realm.RealmModel;
+import io.realm.annotations.RealmClass;
+
+@RealmClass
+public class Frog implements RealmModel {
+ private String name;
+ private int age;
+ private String species;
+ private String owner;
+ public Frog(String name, int age, String species, String owner) {
+ this.name = name;
+ this.age = age;
+ this.species = species;
+ this.owner = owner;
+ }
+ public Frog() {} // RealmObject subclasses must provide an empty constructor
+
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public int getAge() { return age; }
+ public void setAge(int age) { this.age = age; }
+ public String getSpecies() { return species; }
+ public void setSpecies(String species) { this.species = species; }
+ public String getOwner() { return owner; }
+ public void setOwner(String owner) { this.owner = owner; }
+}
+```
+
+> Important:
+> All Realm objects must use the `public`
+visibility modifier.
+>
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmModel
+import io.realm.annotations.RealmClass
+
+@RealmClass
+open class Frog : RealmModel {
+ var name: String? = null
+ var age = 0
+ var species: String? = null
+ var owner: String? = null
+
+ constructor(name: String?, age: Int, species: String?, owner: String?) {
+ this.name = name
+ this.age = age
+ this.species = species
+ this.owner = owner
+ }
+
+ constructor() {} // RealmObject subclasses must provide an empty constructor
+}
+```
+
+> Important:
+> All Realm objects must use the `open`
+visibility modifier.
+>
+
+> Tip:
+> When you create a Realm object by extending the `RealmObject`
+class, you can access `RealmObject` class methods dynamically on
+instances of your Realm object. Realm objects
+created by implementing `RealmModel` can access those same methods
+statically through the `RealmObject` class:
+>
+> #### Java
+>
+> ```java
+> // With RealmObject
+> frogRealmObject.isValid();
+> frogRealmObject.addChangeListener(listener);
+>
+> // With RealmModel
+> RealmObject.isValid(frogRealmModel);
+> RealmObject.addChangeListener(frogRealmModel, listener);
+>
+> ```
+>
+>
+> #### Kotlin
+>
+> ```kotlin
+> // With RealmObject
+> frogRealmObject?.isValid
+> frogRealmObject?.addChangeListener(listener)
+>
+> // With RealmModel
+> RealmObject.isValid(frogRealmModel)
+> RealmObject.addChangeListener(frogRealmModel, listener)
+>
+> ```
+>
+>
+
+## Lists
+Realm objects can contain lists of non-Realm-object data
+types:
+
+#### Java
+
+Unlike lists of Realm objects, these lists can contain
+null values. If null values shouldn't be allowed, use the
+@Required annotation.
+
+```java
+import io.realm.RealmList;
+import io.realm.RealmObject;
+
+public class Frog extends RealmObject {
+ private String name;
+ private int age;
+ private String species;
+ private String owner;
+ private RealmList favoriteColors;
+ public Frog(String name, int age, String species, String owner, RealmList favoriteColors) {
+ this.name = name;
+ this.age = age;
+ this.species = species;
+ this.owner = owner;
+ this.favoriteColors = favoriteColors;
+ }
+ public Frog(){} // RealmObject subclasses must provide an empty constructor
+
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public int getAge() { return age; }
+ public void setAge(int age) { this.age = age; }
+ public String getSpecies() { return species; }
+ public void setSpecies(String species) { this.species = species; }
+ public String getOwner() { return owner; }
+ public void setOwner(String owner) { this.owner = owner; }
+ public RealmList getFavoriteColors() { return favoriteColors; }
+ public void setFavoriteColors(RealmList favoriteColors) { this.favoriteColors = favoriteColors; }
+}
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmList
+import io.realm.RealmObject
+
+open class Frog : RealmObject {
+ var name: String? = null
+ var age = 0
+ var species: String? = null
+ var owner: String? = null
+ var favoriteColors : RealmList? = null
+
+ constructor(
+ name: String?,
+ age: Int,
+ species: String?,
+ owner: String?,
+ favoriteColors: RealmList?
+ ) {
+ this.name = name
+ this.age = age
+ this.species = species
+ this.owner = owner
+ this.favoriteColors = favoriteColors
+ }
+
+ constructor() {} // RealmObject subclasses must provide an empty constructor
+}
+```
+
+> Seealso:
+> Data Types: Lists
+>
+
+## Define an Embedded Object Field
+Realm provides the ability to nest objects within other
+objects. This has several advantages:
+
+- When you delete an object that contains another object, the delete
+operation removes both objects from the realm, so unused objects
+don't accumulate in your realm file, taking up valuable space on
+user's mobile devices.
+
+To embed an object, set the `embedded` property of the
+`@RealmClass`
+annotation to `true` on the class that you'd like to nest within
+another class:
+
+#### Java
+
+```java
+import io.realm.RealmObject;
+import io.realm.annotations.RealmClass;
+
+@RealmClass(embedded=true)
+public class Fly extends RealmObject {
+ private String name;
+ public Fly(String name) {
+ this.name = name;
+ }
+ public Fly() {} // RealmObject subclasses must provide an empty constructor
+}
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmObject
+import io.realm.annotations.RealmClass
+
+@RealmClass(embedded = true)
+open class Fly : RealmObject {
+ private var name: String? = null
+
+ constructor(name: String?) {
+ this.name = name
+ }
+
+ constructor() {} // RealmObject subclasses must provide an empty constructor
+}
+```
+
+Then, any time you reference that class from another class,
+Realm will embed the referenced class within the enclosing
+class, as in the following example:
+
+#### Java
+
+```java
+import io.realm.RealmObject;
+
+public class Frog extends RealmObject {
+ private String name;
+ private int age;
+ private String species;
+ private String owner;
+ private Fly lastMeal;
+ public Frog(String name, int age, String species, String owner, Fly lastMeal) {
+ this.name = name;
+ this.age = age;
+ this.species = species;
+ this.owner = owner;
+ this.lastMeal = lastMeal;
+ }
+ public Frog(){} // RealmObject subclasses must provide an empty constructor
+
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public int getAge() { return age; }
+ public void setAge(int age) { this.age = age; }
+ public String getSpecies() { return species; }
+ public void setSpecies(String species) { this.species = species; }
+ public String getOwner() { return owner; }
+ public void setOwner(String owner) { this.owner = owner; }
+ public Fly getLastMeal() { return lastMeal; }
+ public void setLastMeal(Fly lastMeal) { this.lastMeal = lastMeal; }
+}
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmObject
+
+open class Frog : RealmObject {
+ var name: String? = null
+ var age = 0
+ var species: String? = null
+ var owner: String? = null
+ var lastMeal: Fly? = null
+
+ constructor(
+ name: String?,
+ age: Int,
+ species: String?,
+ owner: String?,
+ lastMeal: Fly?
+ ) {
+ this.name = name
+ this.age = age
+ this.species = species
+ this.owner = owner
+ this.lastMeal = lastMeal
+ }
+
+ constructor() {} // RealmObject subclasses must provide an empty constructor
+}
+```
+
+> Seealso:
+> Data Types: Embedded Objects
+>
+
+## Annotations
+Use annotations to customize your Realm object models.
+
+### Primary Key
+> Version added: 10.6.0
+> Realm automatically indexes
+primary key fields. Previously, Realm only indexed `String` primary
+keys automatically.
+>
+
+Realm treats fields marked with the
+`@PrimaryKey` annotation
+as primary keys for their corresponding object schema. Primary keys are
+subject to the following limitations:
+
+- You can define only one primary key per object schema.
+- Primary key values must be unique across all instances of an object
+in a realm. Attempting to insert a duplicate primary key value
+results in a `RealmPrimaryKeyConstraintException`.
+- Primary key values are immutable. To change the primary key value of
+an object, you must delete the original object and insert a new object
+with a different primary key value.
+- Embedded objects cannot define a
+primary key.
+
+You can create a primary key with any of the following types:
+
+- `String`
+- `UUID`
+- `ObjectId`
+- `Integer` or `int`
+- `Long` or `long`
+- `Short` or `short`
+- `Byte` or `byte[]`
+
+Non-primitive types can contain a value of `null` as a primary key
+value, but only for one object of a particular type, since each primary
+key value must be unique. Attempting to insert an object with an existing
+primary key into a realm will result in a
+`[RealmPrimaryKeyConstraintException`.
+
+Realm automatically indexes
+primary key fields, which allows you to efficiently read and modify
+objects based on their primary key.
+
+You cannot change the primary key field for an object type after adding
+any object of that type to a realm.
+
+Embedded objects cannot contain primary keys.
+
+You may optionally define a primary key for an object type as part of
+the object schema with the
+`@PrimaryKey` annotation:
+
+#### Java
+
+```java
+import io.realm.RealmObject;
+import io.realm.annotations.PrimaryKey;
+
+public class Frog extends RealmObject {
+ @PrimaryKey private String name;
+ private int age;
+ private String species;
+ private String owner;
+ public Frog(String name, int age, String species, String owner) {
+ this.name = name;
+ this.age = age;
+ this.species = species;
+ this.owner = owner;
+ }
+ public Frog(){} // RealmObject subclasses must provide an empty constructor
+
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public int getAge() { return age; }
+ public void setAge(int age) { this.age = age; }
+ public String getSpecies() { return species; }
+ public void setSpecies(String species) { this.species = species; }
+ public String getOwner() { return owner; }
+ public void setOwner(String owner) { this.owner = owner; }
+}
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+
+open class Frog : RealmObject {
+ @PrimaryKey var name : String? = null
+ var age = 0
+ var species: String? = null
+ var owner: String? = null
+
+ constructor(name: String?, age: Int, species: String?, owner: String?) {
+ this.name = name
+ this.age = age
+ this.species = species
+ this.owner = owner
+ }
+
+ constructor() {} // RealmObject subclasses must provide an empty constructor
+}
+```
+
+### Required Fields
+#### Java
+
+```java
+import io.realm.RealmObject;
+import io.realm.annotations.Required;
+
+public class Frog extends RealmObject {
+ @Required private String name;
+ private int age;
+ private String species;
+ private String owner;
+ public Frog(String name, int age, String species, String owner) {
+ this.name = name;
+ this.age = age;
+ this.species = species;
+ this.owner = owner;
+ }
+ public Frog(){} // RealmObject subclasses must provide an empty constructor
+
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public int getAge() { return age; }
+ public void setAge(int age) { this.age = age; }
+ public String getSpecies() { return species; }
+ public void setSpecies(String species) { this.species = species; }
+ public String getOwner() { return owner; }
+ public void setOwner(String owner) { this.owner = owner; }
+}
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmObject
+import io.realm.annotations.Required
+
+open class Frog : RealmObject {
+ @Required var name: String? = null
+ var age = 0
+ var species: String? = null
+ var owner: String? = null
+
+ constructor(name: String?, age: Int, species: String?, owner: String?) {
+ this.name = name
+ this.age = age
+ this.species = species
+ this.owner = owner
+ }
+
+ constructor() {} // RealmObject subclasses must provide an empty constructor
+}
+```
+
+### Optional Fields
+Fields marked with Java object types and Kotlin nullable types
+(ending with `?`) are nullable by default. All other types
+(primitives, non-nullable Kotlin object types) are required by default.
+You can mark a nullable field with the `@Required`
+annotation to prevent that field from holding a null value.
+`RealmLists` are never nullable, but
+you can use the `@Required` annotation to prevent objects in a list
+from holding a null value, even if the base type would otherwise allow it.
+You cannot mark a `RealmList` of `RealmObject` subtypes as required.
+
+You can make any of the following types required:
+
+- `String`
+- `UUID`
+- `ObjectId`
+- `Integer`
+- `Long`
+- `Short`
+- `Byte` or `byte[]`
+- `Boolean`
+- `Float`
+- `Double`
+- `Date`
+- `RealmList`
+
+Primitive types such as `int` and the `RealmList` type are
+implicitly required. Fields with the `RealmObject` type are always
+nullable, and cannot be made required.
+
+> Important:
+> In Kotlin, types are non-nullable by default unless you explicitly
+add a `?` suffix to the type. You can only annotate
+nullable types. Using the
+`@Required` annotation on non-nullable types will fail compilation.
+>
+
+#### Java
+
+Nullable fields are optional by default in Realm, unless
+otherwise specified with the @Required
+annotation. The following types are nullable:
+
+- `String`
+- `Date`
+- `UUID`
+- `ObjectId`
+- `Integer`
+- `Long`
+- `Short`
+- `Byte` or `byte[]`
+- `Boolean`
+- `Float`
+- `Double`
+
+Primitive types like `int` and `long` are non-nullable by
+default and cannot be made nullable, as they cannot be set to a
+null value.
+
+#### Kotlin
+
+In Kotlin, fields are considered nullable only if a field is
+marked nullable with the Kotlin [? operator](https://kotlinlang.org/docs/reference/null-safety.html) except
+for the following types:
+
+- `String`
+- `Date`
+- `UUID`
+- `ObjectId`
+- `Decimal128`
+- `RealmAny`
+
+You can require any type that ends with the Kotlin `?`
+operator, such as `Int?`.
+
+The `RealmList` type is non-nullable by default and cannot be
+made nullable.
+
+### Default Field Values
+To assign a default value to a field, use the built-in language features
+to assign default values.
+
+#### Java
+
+Use the class constructor(s) to assign default values:
+
+```java
+import io.realm.RealmObject;
+
+public class Frog extends RealmObject {
+ private String name = "Kitty";
+ private int age;
+ private String species;
+ private String owner;
+ public Frog(String name, int age, String species, String owner) {
+ this.name = name;
+ this.age = age;
+ this.species = species;
+ this.owner = owner;
+ }
+ public Frog(){} // RealmObject subclasses must provide an empty constructor
+
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public int getAge() { return age; }
+ public void setAge(int age) { this.age = age; }
+ public String getSpecies() { return species; }
+ public void setSpecies(String species) { this.species = species; }
+ public String getOwner() { return owner; }
+ public void setOwner(String owner) { this.owner = owner; }
+}
+```
+
+#### Kotlin
+
+Assign default values in the field declaration:
+
+```kotlin
+import io.realm.RealmObject
+
+open class Frog : RealmObject {
+ var name = "Kitty"
+ var age = 0
+ var species: String? = null
+ var owner: String? = null
+
+ constructor(name: String, age: Int, species: String?, owner: String?) {
+ this.name = name
+ this.age = age
+ this.species = species
+ this.owner = owner
+ }
+
+ constructor() {} // RealmObject subclasses must provide an empty constructor
+}
+```
+
+> Note:
+> While default values ensure that a newly created object cannot contain
+a value of `null` (unless you specify a default value of `null`),
+they do not impact the nullability of a field. To make a field
+non-nullable, see Required Fields.
+>
+
+### Index a Field
+**Indexes** support the efficient execution of queries in
+Realm. Without indexes, Realm must perform a
+*collection scan*, i.e. scan every document in a collection, to select
+those documents that match a query. If an appropriate index exists for a
+query, Realm can use the index to limit the number of
+documents that it must inspect.
+
+Indexes are special data structures that store a small portion of a
+realm's data in an easy to traverse form. The index stores the value
+of a specific field ordered by the value of the field. The ordering of
+the index entries supports efficient equality matches and range-based
+query operations.
+
+Adding an index can speed up some queries at the cost of slightly slower write
+times and additional storage and memory overhead. Indexes require space in your
+realm file, so adding an index to a property will increase disk space consumed
+by your realm file. Each index entry is a minimum of 12 bytes.
+
+You can index fields with the following types:
+
+- `String`
+- `UUID`
+- `ObjectId`
+- `Integer` or `int`
+- `Long` or `long`
+- `Short` or `short`
+- `Byte` or `byte[]`
+- `Boolean` or `bool`
+- `Date`
+- `RealmAny`
+
+Realm creates indexes for fields annotated with
+`@Index`.
+
+To index a field, use the `@Index`
+annotation:
+
+#### Java
+
+```java
+import io.realm.RealmObject;
+import io.realm.annotations.Index;
+
+public class Frog extends RealmObject {
+ private String name;
+ private int age;
+ @Index private String species;
+ private String owner;
+ public Frog(String name, int age, String species, String owner) {
+ this.name = name;
+ this.age = age;
+ this.species = species;
+ this.owner = owner;
+ }
+ public Frog(){} // RealmObject subclasses must provide an empty constructor
+
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public int getAge() { return age; }
+ public void setAge(int age) { this.age = age; }
+ public String getSpecies() { return species; }
+ public void setSpecies(String species) { this.species = species; }
+ public String getOwner() { return owner; }
+ public void setOwner(String owner) { this.owner = owner; }
+}
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmObject
+import io.realm.annotations.Index
+
+open class Frog : RealmObject {
+ var name: String? = null
+ var age = 0
+ @Index var species : String? = null
+ var owner: String? = null
+
+ constructor(name: String?, age: Int, species: String?, owner: String?) {
+ this.name = name
+ this.age = age
+ this.species = species
+ this.owner = owner
+ }
+
+ constructor() {} // RealmObject subclasses must provide an empty constructor
+}
+```
+
+### Ignore a Field
+If you don't want to save a field in your model to a realm, you can
+ignore a field.
+
+Ignore a field from a Realm object model with the
+`@Ignore` annotation:
+
+#### Java
+
+```java
+import io.realm.RealmObject;
+import io.realm.annotations.Ignore;
+
+public class Frog extends RealmObject {
+ private String name;
+ private int age;
+ private String species;
+ // can you ever really own a frog persistently?
+ @Ignore private String owner;
+ public Frog(String name, int age, String species, String owner) {
+ this.name = name;
+ this.age = age;
+ this.species = species;
+ this.owner = owner;
+ }
+ public Frog(){} // RealmObject subclasses must provide an empty constructor
+
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public int getAge() { return age; }
+ public void setAge(int age) { this.age = age; }
+ public String getSpecies() { return species; }
+ public void setSpecies(String species) { this.species = species; }
+ public String getOwner() { return owner; }
+ public void setOwner(String owner) { this.owner = owner; }
+}
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmObject
+import io.realm.annotations.Ignore
+
+open class Frog : RealmObject {
+ var name: String? = null
+ var age = 0
+ var species: String? = null
+ // can you ever really own a frog persistently?
+ @Ignore var owner : String? = null
+
+ constructor(name: String?, age: Int, species: String?, owner: String?) {
+ this.name = name
+ this.age = age
+ this.species = species
+ this.owner = owner
+ }
+
+ constructor() {} // RealmObject subclasses must provide an empty constructor
+}
+```
+
+> Note:
+> Fields marked `static` or `transient` are always ignored, and do
+not need the `@Ignore` annotation.
+>
+
+### Rename a Field
+By default, Realm uses the name defined in the model class
+to represent fields internally. In some cases you might want to change
+this behavior:
+
+- To make it easier to work across platforms, since naming conventions differ.
+- To change a field name in Kotlin without forcing a migration.
+
+Choosing an internal name that differs from the name used in model classes
+has the following implications:
+
+- Migrations must use the internal name when creating classes and fields.
+- Schema errors reported will use the internal name.
+
+Use the `@RealmField`
+annotation to rename a field:
+
+#### Java
+
+```java
+import io.realm.RealmObject;
+import io.realm.annotations.RealmField;
+
+public class Frog extends RealmObject {
+ private String name;
+ private int age;
+ @RealmField("latinName") private String species;
+ private String owner;
+ public Frog(String name, int age, String species, String owner) {
+ this.name = name;
+ this.age = age;
+ this.species = species;
+ this.owner = owner;
+ }
+ public Frog(){} // RealmObject subclasses must provide an empty constructor
+
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public int getAge() { return age; }
+ public void setAge(int age) { this.age = age; }
+ public String getSpecies() { return species; }
+ public void setSpecies(String species) { this.species = species; }
+ public String getOwner() { return owner; }
+ public void setOwner(String owner) { this.owner = owner; }
+}
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmObject
+import io.realm.annotations.RealmField
+
+open class Frog : RealmObject {
+ var name: String? = null
+ var age = 0
+ @RealmField("latinName") var species: String? = null
+ var owner: String? = null
+
+ constructor(name: String?, age: Int, species: String?, owner: String?) {
+ this.name = name
+ this.age = age
+ this.species = species
+ this.owner = owner
+ }
+
+ constructor() {} // RealmObject subclasses must provide an empty constructor
+}
+```
+
+Alternatively, you can also assign a naming policy at the module or
+class levels to change the way that Realm interprets field
+names.
+
+You can define a
+`naming policy`
+at the module level,
+which will affect all classes included in the module:
+
+#### Java
+
+```java
+import io.realm.annotations.RealmModule;
+import io.realm.annotations.RealmNamingPolicy;
+
+@RealmModule(
+ allClasses = true,
+ classNamingPolicy = RealmNamingPolicy.LOWER_CASE_WITH_UNDERSCORES,
+ fieldNamingPolicy = RealmNamingPolicy.LOWER_CASE_WITH_UNDERSCORES
+)
+public class MyModule {
+}
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.annotations.RealmModule
+import io.realm.annotations.RealmNamingPolicy
+
+@RealmModule(
+ allClasses = true,
+ classNamingPolicy = RealmNamingPolicy.LOWER_CASE_WITH_UNDERSCORES,
+ fieldNamingPolicy = RealmNamingPolicy.LOWER_CASE_WITH_UNDERSCORES
+)
+open class MyModule
+```
+
+You can also define a
+`naming policy`
+at the class level, which overrides module level settings:
+
+#### Java
+
+```java
+import io.realm.RealmObject;
+import io.realm.annotations.RealmClass;
+import io.realm.annotations.RealmNamingPolicy;
+
+@RealmClass(fieldNamingPolicy = RealmNamingPolicy.PASCAL_CASE)
+public class Frog extends RealmObject {
+ private String name;
+ private int age;
+ private String species;
+ private String owner;
+ public Frog(String name, int age, String species, String owner) {
+ this.name = name;
+ this.age = age;
+ this.species = species;
+ this.owner = owner;
+ }
+ public Frog(){} // RealmObject subclasses must provide an empty constructor
+
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public int getAge() { return age; }
+ public void setAge(int age) { this.age = age; }
+ public String getSpecies() { return species; }
+ public void setSpecies(String species) { this.species = species; }
+ public String getOwner() { return owner; }
+ public void setOwner(String owner) { this.owner = owner; }
+}
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmObject
+import io.realm.annotations.RealmClass
+import io.realm.annotations.RealmNamingPolicy
+
+@RealmClass(fieldNamingPolicy = RealmNamingPolicy.PASCAL_CASE)
+open class Frog : RealmObject {
+ var name: String? = null
+ var age = 0
+ var species: String? = null
+ var owner: String? = null
+
+ constructor(name: String?, age: Int, species: String?, owner: String?) {
+ this.name = name
+ this.age = age
+ this.species = species
+ this.owner = owner
+ }
+
+ constructor() {} // RealmObject subclasses must provide an empty constructor
+}
+```
+
+### Rename a Class
+By default, Realm uses the name defined in the model class
+to represent classes internally. In some cases you might want to change
+this behavior:
+
+- To support multiple model classes with the same simple name in different packages.
+- To make it easier to work across platforms, since naming conventions differ.
+- To use a class name that is longer than the 57 character limit enforced by Realm.
+- To change a class name in Kotlin without forcing a migration.
+
+Use the `@RealmClass`
+annotation to rename a class:
+
+#### Java
+
+```java
+import io.realm.RealmObject;
+import io.realm.annotations.RealmClass;
+
+@RealmClass(name = "ShortBodiedTaillessAmphibian")
+public class Frog extends RealmObject {
+ private String name;
+ private int age;
+ private String species;
+ private String owner;
+ public Frog(String name, int age, String species, String owner) {
+ this.name = name;
+ this.age = age;
+ this.species = species;
+ this.owner = owner;
+ }
+ public Frog(){} // RealmObject subclasses must provide an empty constructor
+
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public int getAge() { return age; }
+ public void setAge(int age) { this.age = age; }
+ public String getSpecies() { return species; }
+ public void setSpecies(String species) { this.species = species; }
+ public String getOwner() { return owner; }
+ public void setOwner(String owner) { this.owner = owner; }
+}
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmObject
+import io.realm.annotations.RealmClass
+
+@RealmClass(name = "Short_Bodied_Tailless_Amphibian")
+open class Frog : RealmObject {
+ var name: String? = null
+ var age = 0
+ var species: String? = null
+ var owner: String? = null
+
+ constructor(name: String?, age: Int, species: String?, owner: String?) {
+ this.name = name
+ this.age = age
+ this.species = species
+ this.owner = owner
+ }
+
+ constructor() {} // RealmObject subclasses must provide an empty constructor
+}
+```
+
+## Omit Classes from your Realm Schema
+By default, your application's Realm Schema includes all
+classes that extend `RealmObject`. If you only want to include a
+subset of classes that extend `RealmObject` in your Realm
+Schema, you can include that subset of classes in a module and open
+your realm using that module:
+
+#### Java
+
+```java
+import io.realm.annotations.RealmModule;
+
+@RealmModule(classes = { Frog.class, Fly.class })
+public class MyModule {
+}
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.annotations.RealmModule
+
+@RealmModule(classes = [Frog::class, Fly::class])
+open class MyModule
+```
+
diff --git a/docs/guides/model-data/modify-an-object-schema.md b/docs/guides/model-data/modify-an-object-schema.md
new file mode 100644
index 0000000000..35f35a6811
--- /dev/null
+++ b/docs/guides/model-data/modify-an-object-schema.md
@@ -0,0 +1,243 @@
+# Change an Object Model - Java SDK
+#### Local
+
+The following examples demonstrate how to add, delete, and modify
+properties in a schema. First, make the required schema change.
+Then, increment the schema version. Finally, if the change is
+breaking (destructive) create a corresponding migration function to move data from the original schema
+to the updated schema.
+
+> Note:
+> Assume that each schema change shown in the following example
+occurs after the application has used the existing schema. The
+new schema version numbers apply only after you open the
+realm and explicitly specify the new version number.
+In other words, you can't specify version 3 without previously
+specifying and using versions 0, 1, and 2.
+>
+
+A realm using schema version `0` has a `Person` object type:
+
+#### Java
+
+```java
+public class Person extends RealmObject { // Realm schema version 0
+ @Required
+ public String firstName;
+ @Required
+ public int age;
+}
+
+```
+
+#### Kotlin
+
+```kotlin
+class Person: RealmObject { // Realm schema version 0
+ var firstName: String = ""
+ var age: int = 0
+}
+
+```
+
+### A. Add a Property
+The following example adds a `lastName` property to the
+original Person schema:
+
+#### Java
+
+```java
+public class Person extends RealmObject { // Realm schema version 1
+ @Required
+ public String firstName;
+ @Required
+ public String lastName;
+ @Required
+ public int age;
+}
+```
+
+#### Kotlin
+
+```kotlin
+class Person: RealmObject { // Realm schema version 1
+ var firstName: String = ""
+ var lastName: String = ""
+ var age: int = 0
+}
+
+```
+
+### B. Delete a Property
+The following example uses a combined
+`fullName` property instead of the separate `firstName` and
+`lastName` property in the original Person schema:
+
+#### Java
+
+```java
+public class Person extends RealmObject { // Realm schema version 2
+ @Required
+ public String fullName;
+ @Required
+ public int age;
+}
+
+```
+
+#### Kotlin
+
+```kotlin
+class Person: RealmObject { // Realm schema version 2
+ var fullName: String = ""
+ var age: int = 0
+}
+
+```
+
+### C. Modify a Property Type or Rename a Property
+The following example modifies the `age` property in the
+original Person schema by
+renaming it to `birthday` and changing the type to `Date`:
+
+#### Java
+
+```java
+public class Person extends RealmObject { // Realm schema version 3
+ @Required
+ public String fullName;
+ @Required
+ public Date birthday = new Date();
+}
+
+```
+
+#### Kotlin
+
+```kotlin
+class Person: RealmObject { // Realm schema version 3
+ var fullName: String = ""
+ var birthday: Date = Date()
+}
+
+```
+
+### D. Migration Functions
+To migrate the realm to conform to the updated
+`Person` schema, set the realm's
+schema version to `3`
+and define a migration function to set the value of
+`fullName` based on the existing `firstName` and
+`lastName` properties and the value of `birthday` based on
+`age`:
+
+#### Java
+
+```java
+public class Migration implements RealmMigration {
+ @Override
+ public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
+ Long version = oldVersion;
+
+ // DynamicRealm exposes an editable schema
+ RealmSchema schema = realm.getSchema();
+
+ // Changes from version 0 to 1: Adding lastName.
+ // All properties will be initialized with the default value "".
+ if (version == 0L) {
+ schema.get("Person")
+ .addField("lastName", String.class, FieldAttribute.REQUIRED);
+ version++;
+ }
+
+ // Changes from version 1 to 2: combine firstName/lastName into fullName
+ if (version == 1L) {
+ schema.get("Person")
+ .addField("fullName", String.class, FieldAttribute.REQUIRED)
+ .transform( DynamicRealmObject obj -> {
+ String name = "${obj.getString("firstName")} ${obj.getString("lastName")}";
+ obj.setString("fullName", name);
+ })
+ .removeField("firstName")
+ .removeField("lastName");
+ version++;
+ }
+
+ // Changes from version 2 to 3: replace age with birthday
+ if (version == 2L) {
+ schema.get("Person")
+ .addField("birthday", Date::class.java, FieldAttribute.REQUIRED)
+ .transform(DynamicRealmObject obj -> {
+ Int birthYear = Date().year - obj.getInt("age");
+ obj.setDate("birthday", Date(birthYear, 1, 1));
+ })
+ .removeField("age");
+ version++;
+ }
+ }
+};
+
+@RealmModule(classes = { Person.class })
+public class Module {}
+
+RealmConfiguration config = new RealmConfiguration.Builder()
+ .modules(new Module())
+ .schemaVersion(3) // Must be bumped when the schema changes
+ .migration(new Migration()) // Migration to run instead of throwing an exception
+ .build();
+
+```
+
+#### Kotlin
+
+```kotlin
+val migration = object: RealmMigration {
+ override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
+ var version: Long = oldVersion
+
+ // DynamicRealm exposes an editable schema
+ val schema: RealmSchema = realm.schema
+
+ // Changes from version 0 to 1: Adding lastName.
+ // All properties will be initialized with the default value "".
+ if (version == 0L) {
+ schema.get("Person")!!
+ .addField("lastName", String::class.java, FieldAttribute.REQUIRED)
+ version++
+ }
+
+ // Changes from version 1 to 2: Combining firstName/lastName into fullName
+ if (version == 1L) {
+ schema.get("Person")!!
+ .addField("fullName", String::class.java, FieldAttribute.REQUIRED)
+ .transform { obj: DynamicRealmObject ->
+ val name = "${obj.getString("firstName")} ${obj.getString("lastName")}"
+ obj.setString("fullName", name)
+ }
+ .removeField("firstName")
+ .removeField("lastName")
+ version++
+ }
+
+ // Changes from version 2 to 3: Replace age with birthday
+ if (version == 2L) {
+ schema.get("Person")!!
+ .addField("birthday", Date::class.java, FieldAttribute.REQUIRED)
+ .transform { obj: DynamicRealmObject ->
+ var birthYear = Date().year - obj.getInt("age")
+ obj.setDate("birthday", Date(birthYear, 1, 1))
+ }
+ .removeField("age")
+ version++
+ }
+ }
+}
+
+@RealmModule(classes = { Person::class.java })
+class Module
+
+val config = RealmConfiguration.Builder()
+ .schemaVersion(3) // Must be bumped when the schema changes
+ .migration(migration) // Migration to run instead of throwing an exception
+ .build()
+```
diff --git a/docs/guides/model-data/relationships.md b/docs/guides/model-data/relationships.md
new file mode 100644
index 0000000000..4286cf962f
--- /dev/null
+++ b/docs/guides/model-data/relationships.md
@@ -0,0 +1,344 @@
+# Relationships - Java SDK
+## Relationships
+Realm allows you to define explicit relationships between the types of
+objects in an App. A relationship is an object property that references
+another Realm object rather than one of the primitive data types. You
+define relationships by setting the type of an object property to
+another object type in the property schema.
+
+Relationships are direct references to other objects in a realm, which
+means that you don't need bridge tables or explicit joins to define a
+relationship like you would in a relational database. Instead you can
+access related objects by reading and writing to the property that
+defines the relationship. Realm executes read operations
+lazily as they come in, so querying a relationship is just as performant
+as reading a regular property.
+
+There are three primary types of relationships between objects:
+
+- One-to-One Relationship
+- One-to-Many Relationship
+- Inverse Relationship
+
+You can define relationships, collections, and embedded objects in your
+object schema using the following types:
+
+- `RealmObject`
+- `RealmList extends RealmObject>`
+
+Use annotations to indicate whether a given field represents a foreign
+key relationship or an embedded object relationship. For more
+information, see Relationship Annotations.
+
+### To-One Relationship
+A **to-one** relationship means that an object is related in a specific
+way to no more than one other object. You define a to-one relationship
+for an object type in its object schema by
+specifying a property where the type is the related Realm object type.
+
+Setting a relationship field to null removes the connection between
+objects, but Realm does not delete the referenced object
+unless that object is embedded.
+
+### To-Many Relationship
+A **to-many** relationship means that an object is related in a specific
+way to multiple objects. You can create a relationship between one object
+and any number of objects using a field of type `RealmList`
+where `T` is a Realm object in your application:
+
+### Inverse Relationship
+An **inverse relationship** links an object back to any other objects that refer
+to it in a defined to-one or to-many relationship. Relationship definitions are
+unidirectional, so you must explicitly define a property in the object's model
+as an inverse relationship.
+
+For example, the to-many relationship "User has many Tasks" does not
+automatically create the inverse relationship "Task belongs to User". If you
+don't specify the inverse relationship in the object model, you would need to
+run a separate query to look up the user that is assigned to a given task.
+
+To define an inverse relationship, define a `LinkingObjects` property in your
+object model. The `LinkingObjects` definition specifies the object type and
+property name of the relationship that it inverts.
+
+Realm automatically updates implicit relationships whenever an
+object is added or removed in the specified relationship. You cannot manually
+set the value of an inverse relationship property.
+
+Fields annotated with `@LinkingObjects` must be:
+
+- marked `final`
+- of type `RealmResults` where `T` is the type at the opposite
+end of the relationship
+
+Since relationships are many-to-one or many-to-many, following inverse
+relationships can result in zero, one, or many objects.
+
+Like any other `RealmResults` set, you can
+query an inverse relationship.
+
+## Define a Relationship Field
+
+> Warning:
+> Realm objects use getters and setters to persist updated
+field values to your realms. Always use getters and setters for
+updates.
+>
+
+### Many-to-One
+To set up a many-to-one or one-to-one relationship, create a field
+whose type is a Realm object in your application:
+
+#### Java
+
+```java
+import io.realm.RealmObject;
+
+public class Frog extends RealmObject {
+ private String name;
+ private int age;
+ private String species;
+ private String owner;
+ private Frog bestFriend;
+ public Frog(String name, int age, String species, String owner, Frog bestFriend) {
+ this.name = name;
+ this.age = age;
+ this.species = species;
+ this.owner = owner;
+ this.bestFriend = bestFriend;
+ }
+ public Frog(){} // RealmObject subclasses must provide an empty constructor
+
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public int getAge() { return age; }
+ public void setAge(int age) { this.age = age; }
+ public String getSpecies() { return species; }
+ public void setSpecies(String species) { this.species = species; }
+ public String getOwner() { return owner; }
+ public void setOwner(String owner) { this.owner = owner; }
+ public Frog getBestFriend() { return bestFriend; }
+ public void setBestFriend(Frog bestFriend) { this.bestFriend = bestFriend; }
+}
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmObject
+
+open class Frog : RealmObject {
+ var name: String? = null
+ var age = 0
+ var species: String? = null
+ var owner: String? = null
+ var bestFriend: Frog? = null
+
+ constructor(
+ name: String?,
+ age: Int,
+ species: String?,
+ owner: String?,
+ bestFriend: Frog?
+ ) {
+ this.name = name
+ this.age = age
+ this.species = species
+ this.owner = owner
+ this.bestFriend = bestFriend
+ }
+
+ constructor() {} // RealmObject subclasses must provide an empty constructor
+}
+```
+
+> Important:
+> When you declare a to-one relationship in your object model, it must
+be an optional property. If you try to make a to-one relationship
+required, Realm throws an exception at runtime.
+>
+
+Each `Frog` references either zero `Frog` instances or one other `Frog` instance. Nothing
+prevents multiple `Frog` instances from referencing the same `Frog`
+as a best friend; the distinction between a many-to-one and a one-to-one
+relationship is up to your application.
+
+### Many-to-Many
+#### Java
+
+```java
+import io.realm.RealmList;
+import io.realm.RealmObject;
+
+public class Frog extends RealmObject {
+ private String name;
+ private int age;
+ private String species;
+ private String owner;
+ private RealmList bestFriends;
+ public Frog(String name, int age, String species, String owner, RealmList bestFriends) {
+ this.name = name;
+ this.age = age;
+ this.species = species;
+ this.owner = owner;
+ this.bestFriends = bestFriends;
+ }
+ public Frog(){} // RealmObject subclasses must provide an empty constructor
+
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public int getAge() { return age; }
+ public void setAge(int age) { this.age = age; }
+ public String getSpecies() { return species; }
+ public void setSpecies(String species) { this.species = species; }
+ public String getOwner() { return owner; }
+ public void setOwner(String owner) { this.owner = owner; }
+ public RealmList getBestFriends() { return bestFriends; }
+ public void setBestFriends(RealmList bestFriends) { this.bestFriends = bestFriends; }
+}
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmList
+import io.realm.RealmObject
+
+open class Frog : RealmObject {
+ var name: String? = null
+ var age = 0
+ var species: String? = null
+ var owner: String? = null
+ var bestFriends: RealmList? = null
+
+ constructor(
+ name: String?,
+ age: Int,
+ species: String?,
+ owner: String?,
+ bestFriends: RealmList?
+ ) {
+ this.name = name
+ this.age = age
+ this.species = species
+ this.owner = owner
+ this.bestFriends = bestFriends
+ }
+
+ constructor() {} // RealmObject subclasses must provide an empty constructor
+}
+```
+
+`RealmList` s are containers of `RealmObject` s, but otherwise behave
+like a regular collection. You can use the same object in multiple
+`RealmList` s.
+
+### Inverse Relationships
+By default, Realm relationships are unidirectional. You
+can follow a link from one class to a referenced class, but not in the
+opposite direction. Consider the following class defining a `Toad` with
+a list of `frogFriends`:
+
+#### Java
+
+```java
+import io.realm.RealmList;
+import io.realm.RealmObject;
+
+public class Toad extends RealmObject {
+ private RealmList frogFriends;
+ public Toad(RealmList frogFriends) {
+ this.frogFriends = frogFriends;
+ }
+ public Toad() {}
+
+ public RealmList getFrogFriends() { return frogFriends; }
+ public void setFrogFriends(RealmList frogFriends) { this.frogFriends = frogFriends; }
+}
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmList
+import io.realm.RealmObject
+
+open class Toad : RealmObject {
+ var frogFriends: RealmList? = null
+
+ constructor(frogFriends: RealmList?) {
+ this.frogFriends = frogFriends
+ }
+
+ constructor() {}
+}
+```
+
+You can provide a link in the opposite direction, from `Frog` to `Toad`,
+with the `@LinkingObjects`
+annotation on a `final` (in Java) or `val` (in Kotlin) field of type
+`RealmResults`:
+
+#### Java
+
+```java
+import io.realm.RealmObject;
+import io.realm.RealmResults;
+import io.realm.annotations.LinkingObjects;
+
+public class Frog extends RealmObject {
+ private String name;
+ private int age;
+ private String species;
+ private String owner;
+ @LinkingObjects("frogFriends")
+ private final RealmResults toadFriends = null;
+
+ public Frog(String name, int age, String species, String owner) {
+ this.name = name;
+ this.age = age;
+ this.species = species;
+ this.owner = owner;
+ }
+ public Frog(){} // RealmObject subclasses must provide an empty constructor
+
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public int getAge() { return age; }
+ public void setAge(int age) { this.age = age; }
+ public String getSpecies() { return species; }
+ public void setSpecies(String species) { this.species = species; }
+ public String getOwner() { return owner; }
+ public void setOwner(String owner) { this.owner = owner; }
+}
+```
+
+#### Kotlin
+
+```kotlin
+import io.realm.RealmObject
+import io.realm.RealmResults
+import io.realm.annotations.LinkingObjects
+
+open class Frog : RealmObject {
+ var name: String? = null
+ var age = 0
+ var species: String? = null
+ var owner: String? = null
+ @LinkingObjects("frogFriends")
+ private val toadFriends: RealmResults? = null
+
+ constructor(name: String?, age: Int, species: String?, owner: String?) {
+ this.name = name
+ this.age = age
+ this.species = species
+ this.owner = owner
+ }
+
+ constructor() {} // RealmObject subclasses must provide an empty constructor
+}
+```
+
+> Important:
+> Inverse relationship fields must be marked `final`.
+>
diff --git a/docs/guides/quick-start-local.md b/docs/guides/quick-start-local.md
new file mode 100644
index 0000000000..b79bc54636
--- /dev/null
+++ b/docs/guides/quick-start-local.md
@@ -0,0 +1,675 @@
+# Quick Start - Java SDK
+
+This page contains information to quickly get Realm
+integrated into your app. Before you begin, ensure you have:
+
+- Installed the Java SDK
+
+## Initialize Realm
+Before you can use Realm in your app, you must
+initialize the Realm library. Your application should
+initialize Realm just once each time the application runs.
+
+To initialize the Realm library, provide an Android
+`context` to the `Realm.init()` static function. You can provide
+an Activity, Fragment, or Application `context` for initialization with no
+difference in behavior. You can initialize the Realm library
+in the `onCreate()` method of an [application subclass](https://developer.android.com/reference/android/app/Application) to
+ensure that you only initialize Realm once each time the
+application runs.
+
+#### Java
+
+```java
+Realm.init(this); // context, usually an Activity or Application
+
+```
+
+#### Kotlin
+
+```kotlin
+Realm.init(this) // context, usually an Activity or Application
+
+```
+
+> Tip:
+> If you create your own `Application` subclass, you must add it to your
+application's `AndroidManifest.xml` to execute your custom
+application logic. Set the `android.name` property of your manifest's
+application definition to ensure that Android instantiates your `Application`
+subclass before any other class when a user launches your application.
+>
+> ```xml
+>
+> package="com.mongodb.example">
+>
+> android:name=".MyApplicationSubclass"
+> ...
+> />
+>
+> ```
+>
+
+## Define Your Object Model
+Your application's **data model** defines the structure of data
+stored within Realm.
+You can define your application's data model via Kotlin or
+Java classes in your application code with
+Realm Object Models.
+
+To define your application's data model, add the following class
+definitions to your application code:
+
+#### Java
+
+```java
+import io.realm.RealmObject;
+import io.realm.annotations.PrimaryKey;
+import io.realm.annotations.Required;
+
+public class Task extends RealmObject {
+ @PrimaryKey private String name;
+ @Required private String status = TaskStatus.Open.name();
+
+ public void setStatus(TaskStatus status) { this.status = status.name(); }
+ public String getStatus() { return this.status; }
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public Task(String _name) { this.name = _name; }
+ public Task() {}
+}
+
+```
+
+```java
+
+public enum TaskStatus {
+ Open("Open"),
+ InProgress("In Progress"),
+ Complete("Complete");
+
+ String displayName;
+ TaskStatus(String displayName) {
+ this.displayName = displayName;
+ }
+}
+
+```
+
+#### Kotlin
+
+```kotlin
+
+enum class TaskStatus(val displayName: String) {
+ Open("Open"),
+ InProgress("In Progress"),
+ Complete("Complete"),
+}
+
+open class Task() : RealmObject() {
+ @PrimaryKey
+ var name: String = "task"
+
+ @Required
+ var status: String = TaskStatus.Open.name
+ var statusEnum: TaskStatus
+ get() {
+ // because status is actually a String and another client could assign an invalid value,
+ // default the status to "Open" if the status is unreadable
+ return try {
+ TaskStatus.valueOf(status)
+ } catch (e: IllegalArgumentException) {
+ TaskStatus.Open
+ }
+ }
+ set(value) { status = value.name }
+}
+
+```
+
+## Open a Realm
+Use `RealmConfiguration` to control the specifics of the realm you
+would like to open, including the name or location of the realm,
+whether to allow synchronous reads or writes to a realm on the UI
+thread, and more.
+
+#### Java
+
+```java
+String realmName = "My Project";
+RealmConfiguration config = new RealmConfiguration.Builder().name(realmName).build();
+
+Realm backgroundThreadRealm = Realm.getInstance(config);
+
+```
+
+#### Kotlin
+
+```kotlin
+val realmName: String = "My Project"
+val config = RealmConfiguration.Builder().name(realmName).build()
+
+val backgroundThreadRealm : Realm = Realm.getInstance(config)
+
+```
+
+## Create, Read, Update, and Delete Objects
+Once you have opened a realm, you can modify the
+objects within that realm in a
+write transaction block.
+
+> Important:
+> By default, you can only read or write to a realm in your
+application's UI thread using
+asynchronous transactions. That is,
+you can only use `Realm` methods whose name ends with the word
+`Async` in the main thread of your Android application unless you
+explicitly allow the use of synchronous methods.
+>
+> This restriction exists for the benefit of your application users:
+performing read and write operations on the UI thread can lead to
+unresponsive or slow UI interactions, so it's usually best to handle
+these operations either asynchronously or in a background thread.
+
+To create a new `Task`, instantiate an instance of the
+`Task` class and add it to the realm in a write block:
+
+#### Java
+
+```java
+Task Task = new Task("New Task");
+backgroundThreadRealm.executeTransaction (transactionRealm -> {
+ transactionRealm.insert(Task);
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+val task : Task = Task()
+task.name = "New Task"
+backgroundThreadRealm.executeTransaction { transactionRealm ->
+ transactionRealm.insert(task)
+}
+
+```
+
+You can retrieve a live collection
+of all items in the realm:
+
+#### Java
+
+```java
+// all Tasks in the realm
+RealmResults Tasks = backgroundThreadRealm.where(Task.class).findAll();
+
+```
+
+#### Kotlin
+
+```kotlin
+// all tasks in the realm
+val tasks : RealmResults = backgroundThreadRealm.where().findAll()
+
+```
+
+You can also filter that collection using a filter:
+
+#### Java
+
+```java
+// you can also filter a collection
+RealmResults TasksThatBeginWithN = Tasks.where().beginsWith("name", "N").findAll();
+RealmResults openTasks = Tasks.where().equalTo("status", TaskStatus.Open.name()).findAll();
+
+```
+
+#### Kotlin
+
+```kotlin
+// you can also filter a collection
+val tasksThatBeginWithN : List = tasks.where().beginsWith("name", "N").findAll()
+val openTasks : List = tasks.where().equalTo("status", TaskStatus.Open.name).findAll()
+
+```
+
+To modify a task, update its properties in a write transaction block:
+
+#### Java
+
+```java
+Task otherTask = Tasks.get(0);
+
+// all modifications to a realm must happen inside of a write block
+backgroundThreadRealm.executeTransaction( transactionRealm -> {
+ Task innerOtherTask = transactionRealm.where(Task.class).equalTo("_id", otherTask.getName()).findFirst();
+ innerOtherTask.setStatus(TaskStatus.Complete);
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+val otherTask: Task = tasks[0]!!
+
+// all modifications to a realm must happen inside of a write block
+backgroundThreadRealm.executeTransaction { transactionRealm ->
+ val innerOtherTask : Task = transactionRealm.where().equalTo("name", otherTask.name).findFirst()!!
+ innerOtherTask.status = TaskStatus.Complete.name
+}
+
+```
+
+Finally, you can delete a task by calling the `deleteFromRealm()`
+method in a write transaction block:
+
+#### Java
+
+```java
+Task yetAnotherTask = Tasks.get(0);
+String yetAnotherTaskName = yetAnotherTask.getName();
+// all modifications to a realm must happen inside of a write block
+backgroundThreadRealm.executeTransaction( transactionRealm -> {
+ Task innerYetAnotherTask = transactionRealm.where(Task.class).equalTo("_id", yetAnotherTaskName).findFirst();
+ innerYetAnotherTask.deleteFromRealm();
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+val yetAnotherTask: Task = tasks.get(0)!!
+val yetAnotherTaskName: String = yetAnotherTask.name
+// all modifications to a realm must happen inside of a write block
+backgroundThreadRealm.executeTransaction { transactionRealm ->
+ val innerYetAnotherTask : Task = transactionRealm.where().equalTo("name", yetAnotherTaskName).findFirst()!!
+ innerYetAnotherTask.deleteFromRealm()
+}
+
+```
+
+## Watch for Changes
+You can watch a realm, collection, or object for changes by attaching a custom
+`OrderedRealmCollectionChangeListener` with the `addChangeListener()`
+method:
+
+#### Java
+
+```java
+// all Tasks in the realm
+RealmResults Tasks = uiThreadRealm.where(Task.class).findAllAsync();
+
+Tasks.addChangeListener(new OrderedRealmCollectionChangeListener>() {
+ @Override
+ public void onChange(RealmResults collection, OrderedCollectionChangeSet changeSet) {
+ // process deletions in reverse order if maintaining parallel data structures so indices don't change as you iterate
+ OrderedCollectionChangeSet.Range[] deletions = changeSet.getDeletionRanges();
+ for (OrderedCollectionChangeSet.Range range : deletions) {
+ Log.v("QUICKSTART", "Deleted range: " + range.startIndex + " to " + (range.startIndex + range.length - 1));
+ }
+
+ OrderedCollectionChangeSet.Range[] insertions = changeSet.getInsertionRanges();
+ for (OrderedCollectionChangeSet.Range range : insertions) {
+ Log.v("QUICKSTART", "Inserted range: " + range.startIndex + " to " + (range.startIndex + range.length - 1)); }
+
+ OrderedCollectionChangeSet.Range[] modifications = changeSet.getChangeRanges();
+ for (OrderedCollectionChangeSet.Range range : modifications) {
+ Log.v("QUICKSTART", "Updated range: " + range.startIndex + " to " + (range.startIndex + range.length - 1)); }
+ }
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+// all tasks in the realm
+val tasks : RealmResults = realm.where().findAllAsync()
+
+tasks.addChangeListener(OrderedRealmCollectionChangeListener> { collection, changeSet ->
+ // process deletions in reverse order if maintaining parallel data structures so indices don't change as you iterate
+ val deletions = changeSet.deletionRanges
+ for (i in deletions.indices.reversed()) {
+ val range = deletions[i]
+ Log.v("QUICKSTART", "Deleted range: ${range.startIndex} to ${range.startIndex + range.length - 1}")
+ }
+
+ val insertions = changeSet.insertionRanges
+ for (range in insertions) {
+ Log.v("QUICKSTART", "Inserted range: ${range.startIndex} to ${range.startIndex + range.length - 1}")
+ }
+
+ val modifications = changeSet.changeRanges
+ for (range in modifications) {
+ Log.v("QUICKSTART", "Updated range: ${range.startIndex} to ${range.startIndex + range.length - 1}")
+ }
+})
+
+```
+
+## Complete Example
+If you're running this project in a fresh Android Studio project, you can
+copy and paste this file into your application's `MainActivity` -- just
+remember to:
+
+- use a package declaration at the top of the file for your own project
+- update the `import` statements for `Task` and `TaskStatus` if
+you're using java
+
+#### Java
+
+```java
+import io.realm.RealmObject;
+import io.realm.annotations.PrimaryKey;
+import io.realm.annotations.Required;
+
+public class Task extends RealmObject {
+ @PrimaryKey private String name;
+ @Required private String status = TaskStatus.Open.name();
+
+ public void setStatus(TaskStatus status) { this.status = status.name(); }
+ public String getStatus() { return this.status; }
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+ public Task(String _name) { this.name = _name; }
+ public Task() {}
+}
+
+```
+
+```java
+
+public enum TaskStatus {
+ Open("Open"),
+ InProgress("In Progress"),
+ Complete("Complete");
+
+ String displayName;
+ TaskStatus(String displayName) {
+ this.displayName = displayName;
+ }
+}
+
+```
+
+```java
+import io.realm.OrderedCollectionChangeSet;
+
+import android.os.Bundle;
+
+import androidx.appcompat.app.AppCompatActivity;
+import android.util.Log;
+
+import io.realm.OrderedRealmCollectionChangeListener;
+
+import io.realm.Realm;
+import io.realm.RealmConfiguration;
+import io.realm.RealmResults;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+
+import com.mongodb.realm.examples.model.java.Task;
+import com.mongodb.realm.examples.model.java.TaskStatus;
+
+public class MainActivity extends AppCompatActivity {
+ Realm uiThreadRealm;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Realm.init(this); // context, usually an Activity or Application
+
+ String realmName = "My Project";
+ RealmConfiguration config = new RealmConfiguration.Builder().name(realmName).build();
+
+ uiThreadRealm = Realm.getInstance(config);
+
+ addChangeListenerToRealm(uiThreadRealm);
+
+ FutureTask Task = new FutureTask(new BackgroundQuickStart(), "test");
+ ExecutorService executorService = Executors.newFixedThreadPool(2);
+ executorService.execute(Task);
+
+ }
+
+ private void addChangeListenerToRealm(Realm realm) {
+ // all Tasks in the realm
+ RealmResults Tasks = uiThreadRealm.where(Task.class).findAllAsync();
+
+ Tasks.addChangeListener(new OrderedRealmCollectionChangeListener>() {
+ @Override
+ public void onChange(RealmResults collection, OrderedCollectionChangeSet changeSet) {
+ // process deletions in reverse order if maintaining parallel data structures so indices don't change as you iterate
+ OrderedCollectionChangeSet.Range[] deletions = changeSet.getDeletionRanges();
+ for (OrderedCollectionChangeSet.Range range : deletions) {
+ Log.v("QUICKSTART", "Deleted range: " + range.startIndex + " to " + (range.startIndex + range.length - 1));
+ }
+
+ OrderedCollectionChangeSet.Range[] insertions = changeSet.getInsertionRanges();
+ for (OrderedCollectionChangeSet.Range range : insertions) {
+ Log.v("QUICKSTART", "Inserted range: " + range.startIndex + " to " + (range.startIndex + range.length - 1)); }
+
+ OrderedCollectionChangeSet.Range[] modifications = changeSet.getChangeRanges();
+ for (OrderedCollectionChangeSet.Range range : modifications) {
+ Log.v("QUICKSTART", "Updated range: " + range.startIndex + " to " + (range.startIndex + range.length - 1)); }
+ }
+ });
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ // the ui thread realm uses asynchronous transactions, so we can only safely close the realm
+ // when the activity ends and we can safely assume that those transactions have completed
+ uiThreadRealm.close();
+ }
+
+ public class BackgroundQuickStart implements Runnable {
+
+ @Override
+ public void run() {
+ String realmName = "My Project";
+ RealmConfiguration config = new RealmConfiguration.Builder().name(realmName).build();
+
+ Realm backgroundThreadRealm = Realm.getInstance(config);
+
+ Task Task = new Task("New Task");
+ backgroundThreadRealm.executeTransaction (transactionRealm -> {
+ transactionRealm.insert(Task);
+ });
+
+ // all Tasks in the realm
+ RealmResults Tasks = backgroundThreadRealm.where(Task.class).findAll();
+
+ // you can also filter a collection
+ RealmResults TasksThatBeginWithN = Tasks.where().beginsWith("name", "N").findAll();
+ RealmResults openTasks = Tasks.where().equalTo("status", TaskStatus.Open.name()).findAll();
+
+ Task otherTask = Tasks.get(0);
+
+ // all modifications to a realm must happen inside of a write block
+ backgroundThreadRealm.executeTransaction( transactionRealm -> {
+ Task innerOtherTask = transactionRealm.where(Task.class).equalTo("_id", otherTask.getName()).findFirst();
+ innerOtherTask.setStatus(TaskStatus.Complete);
+ });
+
+ Task yetAnotherTask = Tasks.get(0);
+ String yetAnotherTaskName = yetAnotherTask.getName();
+ // all modifications to a realm must happen inside of a write block
+ backgroundThreadRealm.executeTransaction( transactionRealm -> {
+ Task innerYetAnotherTask = transactionRealm.where(Task.class).equalTo("_id", yetAnotherTaskName).findFirst();
+ innerYetAnotherTask.deleteFromRealm();
+ });
+
+ // because this background thread uses synchronous realm transactions, at this point all
+ // transactions have completed and we can safely close the realm
+ backgroundThreadRealm.close();
+ }
+ }
+}
+
+```
+
+#### Kotlin
+
+```kotlin
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import android.util.Log
+import io.realm.*
+import io.realm.annotations.PrimaryKey
+
+import io.realm.annotations.Required
+import io.realm.kotlin.where
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
+import java.util.concurrent.FutureTask
+
+class MainActivity : AppCompatActivity() {
+ lateinit var uiThreadRealm: Realm
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ Realm.init(this) // context, usually an Activity or Application
+
+ val realmName: String = "My Project"
+ val config = RealmConfiguration.Builder()
+ .name(realmName)
+ .build()
+
+ uiThreadRealm = Realm.getInstance(config)
+
+ addChangeListenerToRealm(uiThreadRealm)
+
+ val task : FutureTask = FutureTask(BackgroundQuickStart(), "test")
+ val executorService: ExecutorService = Executors.newFixedThreadPool(2)
+ executorService.execute(task)
+
+ }
+
+ fun addChangeListenerToRealm(realm : Realm) {
+ // all tasks in the realm
+ val tasks : RealmResults = realm.where().findAllAsync()
+
+ tasks.addChangeListener(OrderedRealmCollectionChangeListener> { collection, changeSet ->
+ // process deletions in reverse order if maintaining parallel data structures so indices don't change as you iterate
+ val deletions = changeSet.deletionRanges
+ for (i in deletions.indices.reversed()) {
+ val range = deletions[i]
+ Log.v("QUICKSTART", "Deleted range: ${range.startIndex} to ${range.startIndex + range.length - 1}")
+ }
+
+ val insertions = changeSet.insertionRanges
+ for (range in insertions) {
+ Log.v("QUICKSTART", "Inserted range: ${range.startIndex} to ${range.startIndex + range.length - 1}")
+ }
+
+ val modifications = changeSet.changeRanges
+ for (range in modifications) {
+ Log.v("QUICKSTART", "Updated range: ${range.startIndex} to ${range.startIndex + range.length - 1}")
+ }
+ })
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ // the ui thread realm uses asynchronous transactions, so we can only safely close the realm
+ // when the activity ends and we can safely assume that those transactions have completed
+ uiThreadRealm.close()
+ }
+
+ class BackgroundQuickStart : Runnable {
+
+ override fun run() {
+ val realmName: String = "My Project"
+ val config = RealmConfiguration.Builder().name(realmName).build()
+
+ val backgroundThreadRealm : Realm = Realm.getInstance(config)
+
+ val task : Task = Task()
+ task.name = "New Task"
+ backgroundThreadRealm.executeTransaction { transactionRealm ->
+ transactionRealm.insert(task)
+ }
+
+ // all tasks in the realm
+ val tasks : RealmResults = backgroundThreadRealm.where().findAll()
+
+ // you can also filter a collection
+ val tasksThatBeginWithN : List = tasks.where().beginsWith("name", "N").findAll()
+ val openTasks : List = tasks.where().equalTo("status", TaskStatus.Open.name).findAll()
+
+ val otherTask: Task = tasks[0]!!
+
+ // all modifications to a realm must happen inside of a write block
+ backgroundThreadRealm.executeTransaction { transactionRealm ->
+ val innerOtherTask : Task = transactionRealm.where().equalTo("name", otherTask.name).findFirst()!!
+ innerOtherTask.status = TaskStatus.Complete.name
+ }
+
+ val yetAnotherTask: Task = tasks.get(0)!!
+ val yetAnotherTaskName: String = yetAnotherTask.name
+ // all modifications to a realm must happen inside of a write block
+ backgroundThreadRealm.executeTransaction { transactionRealm ->
+ val innerYetAnotherTask : Task = transactionRealm.where().equalTo("name", yetAnotherTaskName).findFirst()!!
+ innerYetAnotherTask.deleteFromRealm()
+ }
+
+ // because this background thread uses synchronous realm transactions, at this point all
+ // transactions have completed and we can safely close the realm
+ backgroundThreadRealm.close()
+ }
+
+ }
+}
+
+enum class TaskStatus(val displayName: String) {
+ Open("Open"),
+ InProgress("In Progress"),
+ Complete("Complete"),
+}
+
+open class Task() : RealmObject() {
+ @PrimaryKey
+ var name: String = "task"
+
+ @Required
+ var status: String = TaskStatus.Open.name
+ var statusEnum: TaskStatus
+ get() {
+ // because status is actually a String and another client could assign an invalid value,
+ // default the status to "Open" if the status is unreadable
+ return try {
+ TaskStatus.valueOf(status)
+ } catch (e: IllegalArgumentException) {
+ TaskStatus.Open
+ }
+ }
+ set(value) { status = value.name }
+}
+
+```
+
+## Output
+Running the above code should produce output resembling the following:
+
+```shell
+Successfully authenticated anonymously.
+
+Updated range: 0 to 1
+
+Deleted range: 0 to 1
+
+Successfully logged out.
+```
diff --git a/docs/guides/react-to-changes.md b/docs/guides/react-to-changes.md
new file mode 100644
index 0000000000..63857233f9
--- /dev/null
+++ b/docs/guides/react-to-changes.md
@@ -0,0 +1,382 @@
+# React to Changes - Java SDK
+Objects in Realm clients are **live objects** that
+update automatically to reflect data changes and emit
+notification events that you
+can subscribe to whenever their underlying data changes.
+
+Any modern app should be able to react when data changes,
+regardless of where that change originated. When a user adds
+a new item to a list, you may want to update the UI, show a
+notification, or log a message. When someone updates that
+item, you may want to change its visual state or fire off a
+network request. Finally, when someone deletes the item, you
+probably want to remove it from the UI. Realm's notification
+system allows you to watch for and react to changes in your
+data, independent of the writes that caused the changes.
+
+Realm emits three kinds of notifications:
+
+- Realm notifications whenever a specific realm commits a write transaction.
+- Collection notifications whenever any Realm object in a collection changes, including inserts, updates, and deletes.
+- Object notifications whenever a specific Realm object changes, including updates and deletes.
+
+## Auto-Refresh
+Realm objects accessed on a thread associated with a
+[Looper](https://developer.android.com/reference/android/os/Looper.html) automatically
+update periodically to reflect changes to underlying data.
+
+The Android UI thread always contains a `Looper` instance. If you need
+to keep Realm objects around for long periods of time on
+any other thread, you should configure a `Looper` for that thread.
+
+> Warning:
+> Realms on a thread without a [Looper](https://developer.android.com/reference/android/os/Looper)
+do not automatically advance their version. This can increase the size of the
+realm in memory and on disk. Avoid using realm instances on
+non-Looper threads when possible. If you *do* open a realm on a non-Looper
+thread, close the realm when you're done using it.
+>
+
+## Register a Realm Change Listener
+You can register a notification handler on an entire realm.
+Realm calls the notification handler whenever any write
+transaction involving that realm is committed. The
+handler receives no information about the change.
+
+This is useful when you want to know that there has been a
+change but do not care to know specifically what changed.
+For example, proof of concept apps often use this
+notification type and simply refresh the entire UI when
+anything changes. As the app becomes more sophisticated and
+performance-sensitive, the app developers shift to more
+granular notifications.
+
+> Example:
+> Suppose you are writing a real-time collaborative app. To
+give the sense that your app is buzzing with collaborative
+activity, you want to have an indicator that lights up when
+any change is made. In that case, a realm notification
+handler would be a great way to drive the code that controls
+the indicator. The following code shows how to observe a realm
+for changes with with `addChangeListener()`:
+>
+> #### Java
+>
+> ```java
+> public class MyActivity extends Activity {
+> private Realm realm;
+> private RealmChangeListener realmListener;
+>
+> @Override
+> protected void onCreate(Bundle savedInstanceState) {
+> super.onCreate(savedInstanceState);
+> realm = Realm.getDefaultInstance();
+> realmListener = new RealmChangeListener() {
+> @Override
+> public void onChange(Realm realm) {
+> // ... do something with the updates (UI, etc.) ...
+> }
+> };
+> // Observe realm notifications.
+> realm.addChangeListener(realmListener);
+> }
+>
+> @Override
+> protected void onDestroy() {
+> super.onDestroy();
+> // Remove the listener.
+> realm.removeChangeListener(realmListener);
+> // Close the Realm instance.
+> realm.close();
+> }
+> }
+>
+> ```
+>
+>
+> #### Kotlin
+>
+> ```kotlin
+> class MyActivity : Activity() {
+> private lateinit var realm: Realm
+> private lateinit var realmListener: RealmChangeListener
+>
+> override fun onCreate(savedInstanceState: Bundle?) {
+> super.onCreate(savedInstanceState)
+> realm = Realm.getDefaultInstance()
+> realmListener = RealmChangeListener {
+> // ... do something with the updates (UI, etc.) ...
+> }
+> // Observe realm notifications.
+> realm.addChangeListener(realmListener)
+> }
+>
+> override fun onDestroy() {
+> super.onDestroy()
+> // Remove the listener.
+> realm.removeChangeListener(realmListener)
+> // Close the Realm instance.
+> realm.close()
+> }
+> }
+>
+> ```
+>
+>
+
+> Important:
+> All threads that contain a `Looper` automatically refresh
+`RealmObject` and `RealmResult` instances when new changes are
+written to the realm. As a result, it isn't necessary to fetch
+those objects again when reacting to a `RealmChangeListener`, since
+those objects are already updated and ready to be redrawn to the
+screen.
+>
+
+## Register a Collection Change Listener
+You can register a notification handler on a specific
+collection within a realm. The handler receives a
+description of changes since the last notification.
+Specifically, this description consists of three lists of
+indices:
+
+- The indices of the objects that were deleted.
+- The indices of the objects that were inserted.
+- The indices of the objects that were modified.
+
+Stop notification delivery by calling the `removeChangeListener()` or
+`removeAllChangeListeners()` methods. Notifications also stop if:
+
+- the object on which the listener is registered gets garbage collected.
+- the realm instance closes.
+
+Keep a strong reference to the object you're listening to
+for as long as you need the notifications.
+
+> Important:
+> In collection notification handlers, always apply changes
+in the following order: deletions, insertions, then
+modifications. Handling insertions before deletions may
+result in unexpected behavior.
+>
+
+Realm emits an initial notification after retrieving the
+collection. After that, Realm delivers collection
+notifications asynchronously whenever a write transaction
+adds, changes, or removes objects in the collection.
+
+Unlike realm notifications, collection notifications contain
+detailed information about the change. This enables
+sophisticated and selective reactions to changes. Collection
+notifications provide all the information needed to manage a
+list or other view that represents the collection in the UI.
+
+The following code shows how to observe a collection for
+changes with `addChangeListener()`:
+
+#### Java
+
+```java
+RealmResults dogs = realm.where(Dog.class).findAll();
+// Set up the collection notification handler.
+OrderedRealmCollectionChangeListener> changeListener = (collection, changeSet) -> {
+ // For deletions, notify the UI in reverse order if removing elements the UI
+ OrderedCollectionChangeSet.Range[] deletions = changeSet.getDeletionRanges();
+ for (int i = deletions.length - 1; i >= 0; i--) {
+ OrderedCollectionChangeSet.Range range = deletions[i];
+ Log.v("EXAMPLE", range.length + " dogs deleted at " + range.startIndex);
+ }
+ OrderedCollectionChangeSet.Range[] insertions = changeSet.getInsertionRanges();
+ for (OrderedCollectionChangeSet.Range range : insertions) {
+ Log.v("EXAMPLE", range.length + " dogs inserted at " + range.startIndex);
+ }
+ OrderedCollectionChangeSet.Range[] modifications = changeSet.getChangeRanges();
+ for (OrderedCollectionChangeSet.Range range : modifications) {
+ Log.v("EXAMPLE", range.length + " dogs modified at " + range.startIndex);
+ }
+};
+// Observe collection notifications.
+dogs.addChangeListener(changeListener);
+
+```
+
+#### Kotlin
+
+```kotlin
+val dogs = realm.where(Dog::class.java).findAll()
+// Set up the collection notification handler.
+val changeListener =
+ OrderedRealmCollectionChangeListener { collection: RealmResults?, changeSet: OrderedCollectionChangeSet ->
+ // For deletions, notify the UI in reverse order if removing elements the UI
+ val deletions = changeSet.deletionRanges
+ for (i in deletions.indices.reversed()) {
+ val range = deletions[i]
+ Log.v("EXAMPLE", "${range.length} dogs deleted at ${range.startIndex}")
+ }
+ val insertions = changeSet.insertionRanges
+ for (range in insertions) {
+ Log.v("EXAMPLE", "${range.length} dogs inserted at ${range.startIndex}")
+ }
+ val modifications = changeSet.changeRanges
+ for (range in modifications) {
+ Log.v("EXAMPLE", "${range.length} dogs modified at ${range.startIndex}")
+ }
+ }
+// Observe collection notifications.
+dogs.addChangeListener(changeListener)
+
+```
+
+## Register an Object Change Listener
+You can register a notification handler on a specific object
+within a realm. Realm notifies your handler:
+
+- When the object is deleted.
+- When any of the object's properties change.
+
+The handler receives information about what fields changed
+and whether the object was deleted.
+
+Stop notification delivery by calling the `removeChangeListener()` or
+`removeAllChangeListeners()` methods. Notifications also stop if:
+
+- the object on which the listener is registered gets garbage collected.
+- the realm instance closes.
+
+Keep a strong reference of the object you're listening to
+for as long as you need the notifications.
+
+The following code shows how create a new instance of a class
+in a realm and observe that instance for changes with
+`addChangeListener()`:
+
+#### Java
+
+```java
+// Create a dog in the realm.
+AtomicReference dog = new AtomicReference();
+realm.executeTransaction(transactionRealm -> {
+ dog.set(transactionRealm.createObject(Dog.class, new ObjectId()));
+ dog.get().setName("Max");
+});
+
+// Set up the listener.
+RealmObjectChangeListener listener = (changedDog, changeSet) -> {
+ if (changeSet.isDeleted()) {
+ Log.i("EXAMPLE", "The dog was deleted");
+ return;
+ }
+ for (String fieldName : changeSet.getChangedFields()) {
+ Log.i("EXAMPLE", "Field '" + fieldName + "' changed.");
+ }
+};
+
+// Observe object notifications.
+dog.get().addChangeListener(listener);
+
+// Update the dog to see the effect.
+realm.executeTransaction(r -> {
+ dog.get().setName("Wolfie"); // -> "Field 'name' was changed."
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+// Create a dog in the realm.
+var dog = Dog()
+realm.executeTransaction { transactionRealm ->
+ dog = transactionRealm.createObject(Dog::class.java, ObjectId())
+ dog.name = "Max"
+}
+
+// Set up the listener.
+val listener = RealmObjectChangeListener { changedDog: Dog?, changeSet: ObjectChangeSet? ->
+ if (changeSet!!.isDeleted) {
+ Log.i("EXAMPLE", "The dog was deleted")
+ } else {
+ for (fieldName in changeSet.changedFields) {
+ Log.i(
+ "EXAMPLE",
+ "Field '$fieldName' changed."
+ )
+ }
+ }
+}
+
+// Observe object notifications.
+dog.addChangeListener(listener)
+
+// Update the dog to see the effect.
+realm.executeTransaction { r: Realm? ->
+ dog.name = "Wolfie" // -> "Field 'name' was changed."
+}
+
+```
+
+## Unregister a Change Listener
+You can unregister a change listener by passing your change listener to
+`Realm.removeChangeListener()`.
+You can unregister all change listeners currently subscribed to changes
+in a realm or any of its linked objects or collections with
+`Realm.removeAllChangeListeners()`.
+
+## Use Realm in System Apps on Custom ROMs
+Realm uses named pipes in order to support notifications and
+access to the realm file from multiple processes. While this is
+allowed by default for normal user apps, it is disallowed for system
+apps.
+
+You can define a system apps by setting
+`android:sharedUserId="android.uid.system"` in the Android manifest.
+When working with a system app, you may see a security violation in
+Logcat that looks something like this:
+
+```
+05-24 14:08:08.984 6921 6921 W .realmsystemapp: type=1400 audit(0.0:99): avc: denied { write } for name="realm.testapp.com.realmsystemapp-Bfqpnjj4mUvxWtfMcOXBCA==" dev="vdc" ino=14660 scontext=u:r:system_app:s0 tcontext=u:object_r:apk_data_file:s0 tclass=dir permissive=0
+05-24 14:08:08.984 6921 6921 W .realmsystemapp: type=1400 audit(0.0:100): avc: denied { write } for name="realm.testapp.com.realmsystemapp-Bfqpnjj4mUvxWtfMcOXBCA==" dev="vdc" ino=14660 scontext=u:r:system_app:s0 tcontext=u:object_r:apk_data_file:s0 tclass=dir permissive=0
+```
+
+In order to fix this you need to adjust the SELinux security rules in
+the ROM. This can be done by using the tool `audit2allow`, which ships
+as part of AOSP:
+
+1. Pull the current policy from the device: `adb pull /sys/fs/selinux/policy`
+2. Copy the SELinux error inside a text file called input.txt.
+3. Run the `audit2allow` tool: `audit2allow -p policy -i input.txt`
+4. The tool should output a rule you can add to your existing policy
+to enable the use of Realm.
+
+An example of such a policy is provided below:
+
+```
+# Allow system_app to create named pipes required by Realm
+# Credit: https://github.com/mikalackis/platform_vendor_ariel/blob/master_oreo/sepolicy/system_app.te
+allow system_app fuse:fifo_file create;
+allow system_app system_app_data_file:fifo_file create;
+allow system_app system_app_data_file:fifo_file { read write };
+allow system_app system_app_data_file:fifo_file open;
+```
+
+> Seealso:
+> `audit2allow` is produced when compiling AOSP/ROM and only runs on
+Linux. You can read more about it [here](https://source.android.com/security/selinux/validate#using_audit2allow).
+>
+
+> Note:
+> Since Android Oreo, Google changed the way it configures SELinux.
+The default security policies are now much more modularized.
+Read more about that
+[here](https://source.android.com/security/selinux/images/SELinux_Treble.pdf).
+>
+
+## Change Notification Limits
+Changes in nested documents deeper than four levels down do not trigger
+change notifications.
+
+If you have a data structure where you need to listen for changes five
+levels down or deeper, workarounds include:
+
+- Refactor the schema to reduce nesting.
+- Add something like "push-to-refresh" to enable users to manually refresh data.
diff --git a/docs/guides/realm-files.md b/docs/guides/realm-files.md
new file mode 100644
index 0000000000..6b7fb377fb
--- /dev/null
+++ b/docs/guides/realm-files.md
@@ -0,0 +1,239 @@
+# Work with Realm Files - Java SDK
+A **realm** is a set of related objects that conform to a pre-defined
+schema. Realms may contain more than one type of data as long as a
+schema exists for each type.
+
+Every realm stores data in a separate realm file that
+contains a binary encoding of each object in the realm. You can
+automatically synchronize realm across multiple
+devices and set up reactive
+event handlers that call a
+function any time an object in a realm is created,
+modified, or deleted.
+
+## The Realm Lifecycle
+Every realm instance consumes a significant amount of resources.
+Opening and closing a realm are both expensive operations, but
+keeping a realm open also incurs significant resource overhead. To
+maximize the performance of your application, you should minimize the
+number of open realms at any given time and limit the number of
+open and close operations used.
+
+However, opening a realm is not always consistently expensive.
+If the realm is already open within the same process or thread,
+opening an additional instance requires fewer resources:
+
+- If the realm is not open within the same process, opening the
+realm is expensive.
+- If the realm is already open on a different thread within the
+same process, opening the realm is less expensive, but still
+nontrivial.
+- If the realm is already open on the same thread within the same
+process, opening the realm requires minimal additional resources.
+
+When you open a realm for the first time, Realm
+performs the memory-mapping and schema validation required to read and
+write data to the realm. Additional instances of that
+realm on the same thread use the same underlying resources.
+Instances of that realm on separate threads use some of the same
+underlying resources.
+
+When all connections to a realm are closed in
+a thread, Realm frees the thread resources used to
+connect to that realm. When all connections to a realm are
+closed in a process, Realm frees all resources used to
+connect to that realm.
+
+As a best practice, we recommend tying the realm instance
+lifecycle to the lifecycles of the views that observe the realm. For
+instance, consider a `RecyclerView` that displays `RealmResults`
+data via a `Fragment`. You could:
+
+- Open a single realm that contains the data for that view
+in the `Fragment.onCreateView()` lifecycle method.
+- Close that same realm in the `Fragment.onDestroyView()`
+lifecycle method.
+
+> Note:
+> If your realm is especially large, fetching a realm instance
+in `Fragment.onCreateView()` may briefly block rendering. If
+opening your realm in `onCreateView()` causes performance
+issues, consider managing the realm from `Fragment.onStart()`
+and `Fragment.onStop()` instead.
+>
+
+If multiple `Fragment` instances require access to the same dataset,
+you could manage a single realm in the enclosing `Activity`:
+
+- Open the realm in the `Activity.onCreate()` lifecycle method.
+- Close the realm in the `Activity.onDestroy()` lifecycle method.
+
+## Multi-process
+You cannot access encrypted or
+[delete me]s
+simultaneously from different processes. However, local realms
+function normally across processes, so you can read, write, and
+receive notifications from multiple APKs.
+
+## Realm Schema
+A **Realm Schema** is a list of valid object schemas that each define an object type that an App
+may persist. All objects in a realm must conform to the Realm Schema.
+
+By default, the SDK automatically adds all classes in your project
+that derive from `RealmObject` to the
+realm schema.
+
+Client applications provide a Realm Schema when they open a
+realm. If a realm already contains data, then Realm
+validates each existing object to ensure that an object schema was
+provided for its type and that it meets all of the constraints specified
+in the schema.
+
+> Example:
+> A realm that contains basic data about books in libraries might use a
+schema like the following:
+>
+> ```json
+> [
+> {
+> "type": "Library",
+> "properties": {
+> "address": "string",
+> "books": "Book[]"
+> }
+> },
+> {
+> "type": "Book",
+> "primaryKey": "isbn",
+> "properties": {
+> "isbn": "string",
+> "title": "string",
+> "author": "string",
+> "numberOwned": { "type": "int?", "default": 0 },
+> "numberLoaned": { "type": "int?", "default": 0 }
+> }
+> }
+> ]
+> ```
+>
+
+## Find Your Realm File
+Realm stores a binary encoded version of every object
+and type in a realm in a single `.realm` file.
+
+The filesystem used by Android emulators is not directly accessible
+from the machine running Realm Studio. You must download the file
+from the emulator before you can access it.
+
+First, find the path of the file on the emulator:
+
+```java
+// Run this on the device to find the path on the emulator
+Realm realm = Realm.getDefaultInstance();
+Log.i("Realm", realm.getPath());
+```
+
+Then, download the file using ADB. You can do this while the app
+is running.
+
+```java
+> adb pull
+```
+
+You can also upload the modified file again using ADB, but only
+when the app isn't running. Uploading a modified file while the
+app is running can corrupt the file.
+
+```java
+> adb push
+```
+
+> Seealso:
+> Realm creates additional files for each realm.
+To learn more about these files, see Realm Internals.
+>
+
+## Realm File Size
+Realm usually takes up less space on disk than an
+equivalent SQLite database. However, in order to give you a consistent
+view of your data, Realm operates on multiple versions of a
+realm. If many versions of a realm are opened simultaneously,
+the realm file can require additional space on disk.
+
+These versions take up an amount of space dependent on the amount of
+changes in each transaction. Many small transactions have the same
+overhead as a small number of large transactions.
+
+Unexpected file size growth usually happens for one of three reasons:
+
+1. *You open a realm on a background thread and forget to close it
+again.* As a result, Realm retains a reference to the
+older version of data on the background thread. Because
+Realm automatically updates realms to the most
+recent version on threads with loopers, the UI thread and other
+Looper threads do not have this problem.
+2. *You hold references to too many versions of frozen objects.*
+Frozen objects preserve the version of a realm that existed when
+the object was first frozen. If you need to freeze a large number of
+objects, consider using `Realm.copyFromRealm()` instead to only preserve the
+data you need.
+3. *You read some data from a realm. Then, you block the thread with
+a long-running operation. Meanwhile, you write many times to the
+realm on other threads.* This causes Realm to
+create many intermediate versions. You can avoid this by: batching the
+writes, avoiding leaving the realm open while otherwise blocking the
+background thread.
+
+### Limit the Maximum Number of Active Versions
+You can set `maxNumberOfActiveVersions()`
+when building your `RealmConfiguration` to throw an
+`IllegalStateException` if your application opens more versions of
+a realm than the permitted number. Versions are created when
+executing a write transaction.
+
+Realm automatically removes older versions of data once
+they are no longer used by your application. However,
+Realm does not free the space used by older versions of
+data; instead, that space is used for new writes to the realm.
+
+### Compact a Realm
+You can remove unused space by **compacting** the realm file:
+
+- Manually: call `compactRealm()`
+- Automatically: specify the `compactOnLaunch()`
+builder option when opening the first connection to a realm in your
+Android application
+
+> Important:
+> Every production application should implement compacting to
+periodically reduce realm file size.
+>
+
+## Backup and Restore Realms
+Realm persists realms to disk using files on your
+Android device. To back up a realm, find your realm file and copy it to a safe location. You should close
+all instances of the realm before copying it.
+
+Alternatively, you can also use `realm.writeCopyTo()` to write a compacted
+version of a realm to a destination file.
+
+> Seealso:
+> If you want to back up a realm to an external location like
+Google Drive, see the following article series: ([Part 1](https://medium.com/glucosio-project/example-class-to-export-import-a-realm-database-on-java-c429ade2b4ed#.80ibsc7wm),
+[Part 2](https://medium.com/glucosio-project/backup-restore-a-realm-database-on-google-drive-with-drive-api-c238515a5975#.qbuugb322),
+[Part 3](https://medium.com/glucosio-project/build-a-nice-ux-to-backup-and-sync-your-app-data-on-google-drive-3-3-a3b598cab68b#.5mjk4w4se)).
+>
+
+## Modules
+Realm Modules describe the set of Realm objects
+that can be stored in a realm. By default, Realm
+automatically creates a Realm Module that contains all
+Realm objects defined in your application.
+You can define a `RealmModule`
+to restrict a realm to a subset of classes defined in an application.
+If you produce a library that uses Realm, you can use a
+Realm Module to explicitly include only the Realm
+objects defined in your library in your realm. This allows
+applications that include your library to also use Realm
+without managing object name conflicts and migrations with your library's
+defined Realm objects.
diff --git a/docs/guides/realm-files/bundle-a-realm.md b/docs/guides/realm-files/bundle-a-realm.md
new file mode 100644
index 0000000000..fec4242784
--- /dev/null
+++ b/docs/guides/realm-files/bundle-a-realm.md
@@ -0,0 +1,53 @@
+# Bundle a Realm File - Java SDK
+Realm supports **bundling** realm files. When you bundle
+a realm file, you include a database and all of its data in your
+application download.
+
+This allows users to start applications for the first time with a set of
+initial data.
+
+## Overview
+To create and bundle a realm file with your application:
+
+1. Create a realm file that
+contains the data you'd like to bundle.
+2. Bundle the realm file in the
+//src/main/assets folder of your production
+application.
+3. In your production application,
+open the realm from the bundled asset file.
+
+## Create a Realm File for Bundling
+1. Build a temporary realm app that shares the data model of your
+application.
+2. Open a realm and add the data you wish to bundle.
+3. Use the `writeCopyTo()`
+method to copy the realm to a new file.
+
+`writeCopyTo()` automatically compacts your realm to the smallest
+possible size before copying.
+
+## Bundle a Realm File in Your Production Application
+Now that you have a copy of the realm that contains the initial data,
+bundle it with your production application.
+
+1. Search your application logs to find the location of the realm file
+copy you just created.
+2. Using the "Device File Explorer" widget in the bottom right of your
+Android Studio window, navigate to the file.
+3. Right click on the file and select "Save As". Navigate to the
+//src/main/assets folder of your production application.
+Save a copy of the realm file there.
+
+> Tip:
+> If your application does not already contain an asset folder, you can
+create one by right clicking on your top-level application
+folder () in Android Studio and selecting
+New > Folder > Assets Folder in the menu.
+>
+
+## Open a Realm from a Bundled Realm File
+Now that you have a copy of the realm included with your production
+application, you need to add code to use it. Use the `assetFile()`
+method when configuring your realm to open the realm
+from the bundled file.
diff --git a/docs/guides/realm-files/encryption.md b/docs/guides/realm-files/encryption.md
new file mode 100644
index 0000000000..2b6b67e19e
--- /dev/null
+++ b/docs/guides/realm-files/encryption.md
@@ -0,0 +1,400 @@
+# Encrypt a Realm - Java SDK
+## Overview
+You can encrypt your realms to ensure that the data stored to disk can't be
+read outside of your application. You encrypt the realm file on disk with AES-256 +
+SHA-2 by supplying a 64-byte encryption key when opening a
+realm.
+
+Realm transparently encrypts and decrypts data with standard
+[AES-256 encryption](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard) using the
+first 256 bits of the given 512-bit encryption key. Realm
+uses the other 256 bits of the 512-bit encryption key to validate
+integrity using a [hash-based message authentication code
+(HMAC)](https://en.wikipedia.org/wiki/HMAC).
+
+> Warning:
+> Do not use cryptographically-weak hashes for realm encryption keys.
+For optimal security, we recommend generating random rather than derived
+encryption keys.
+>
+
+## Considerations
+The following are key impacts to consider when encrypting a realm.
+
+### Storing & Reusing Keys
+You **must** pass the same encryption key to `RealmConfiguration.Builder.encryptionKey()` each
+time you open the realm.
+If you don't provide a key or specify the wrong key for an encrypted
+realm, the Realm SDK throws an error.
+
+Apps should store the encryption key in the
+[Android KeyStore](https://developer.android.com/training/articles/keystore.html) so
+that other apps cannot read the key.
+
+### Performance Impact
+Reads and writes on encrypted realms can be up to 10% slower than unencrypted realms.
+
+### Accessing an Encrypted Realm from Multiple Processes
+> Version changed: 10.14.0
+
+Starting with Realm Java SDK version 10.14.0, Realm supports opening
+the same encrypted realm in multiple processes.
+
+If your app uses Realm Java SDK version 10.14.0 or earlier, attempting to
+open an encrypted realm from multiple processes throws this error:
+`Encrypted interprocess sharing is currently unsupported.`
+
+## Example
+The following steps describe the recommended way to use the
+[Android KeyStore](https://developer.android.com/training/articles/keystore.html) for encryption with
+Realm:
+
+1. Generate an asymmetric RSA key that Android can securely store and
+retrieve using the Android KeyStore. Versions M and above require user PIN or fingerprint to unlock
+the KeyStore.
+2. Generate a symmetric key (AES) you can use to encrypt the realm.
+3. Encrypt the symmetric AES key using your private RSA key.
+4. Store the encrypted AES key on filesystem (in a
+`SharedPreferences`, for example).
+
+When you need to use your encrypted realm:
+
+1. Retrieve your encrypted AES key.
+2. Decrypt your encrypted AES key using the public RSA key.
+3. Use the decrypted AES key in the `RealmConfiguration` to open the
+encrypted realm.
+
+> Seealso:
+> For an end-to-end example of storing and reusing encryption keys, see
+the [store_password](https://github.com/realm/realm-java/tree/feature/example/store_password/examples/StoreEncryptionPassword) example project, which demonstrates the
+fingerprint API.
+>
+
+### Generate and Store an Encryption Key
+The following code demonstrates how to securely generate and store an
+encryption key for a realm:
+
+#### Java
+
+```java
+// Create a key to encrypt a realm and save it securely in the keystore
+public byte[] getNewKey() {
+ // open a connection to the android keystore
+ KeyStore keyStore;
+ try {
+ keyStore = KeyStore.getInstance("AndroidKeyStore");
+ keyStore.load(null);
+ } catch (KeyStoreException | NoSuchAlgorithmException
+ | CertificateException | IOException e) {
+ Log.v("EXAMPLE", "Failed to open the keystore.");
+ throw new RuntimeException(e);
+ }
+
+ // create a securely generated random asymmetric RSA key
+ byte[] realmKey = new byte[Realm.ENCRYPTION_KEY_LENGTH];
+ new SecureRandom().nextBytes(realmKey);
+
+ // create a cipher that uses AES encryption -- we'll use this to encrypt our key
+ Cipher cipher;
+ try {
+ cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES
+ + "/" + KeyProperties.BLOCK_MODE_CBC
+ + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+ Log.e("EXAMPLE", "Failed to create a cipher.");
+ throw new RuntimeException(e);
+ }
+
+ // generate secret key
+ KeyGenerator keyGenerator;
+ try {
+ keyGenerator = KeyGenerator.getInstance(
+ KeyProperties.KEY_ALGORITHM_AES,
+ "AndroidKeyStore");
+ } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
+ Log.e("EXAMPLE", "Failed to access the key generator.");
+ throw new RuntimeException(e);
+ }
+ KeyGenParameterSpec keySpec = new KeyGenParameterSpec.Builder(
+ "realm_key",
+ KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
+ .setUserAuthenticationRequired(true)
+ .setUserAuthenticationValidityDurationSeconds(
+ AUTH_VALID_DURATION_IN_SECOND)
+ .build();
+ try {
+ keyGenerator.init(keySpec);
+ } catch (InvalidAlgorithmParameterException e) {
+ Log.e("EXAMPLE", "Failed to generate a secret key.");
+ throw new RuntimeException(e);
+ }
+ keyGenerator.generateKey();
+
+ // access the generated key in the android keystore, then
+ // use the cipher to create an encrypted version of the key
+ byte[] initializationVector;
+ byte[] encryptedKeyForRealm;
+ try {
+ SecretKey secretKey =
+ (SecretKey) keyStore.getKey("realm_key", null);
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+ encryptedKeyForRealm = cipher.doFinal(realmKey);
+ initializationVector = cipher.getIV();
+ } catch (InvalidKeyException | UnrecoverableKeyException
+ | NoSuchAlgorithmException | KeyStoreException
+ | BadPaddingException | IllegalBlockSizeException e) {
+ Log.e("EXAMPLE", "Failed encrypting the key with the secret key.");
+ throw new RuntimeException(e);
+ }
+
+ // keep the encrypted key in shared preferences
+ // to persist it across application runs
+ byte[] initializationVectorAndEncryptedKey =
+ new byte[Integer.BYTES +
+ initializationVector.length +
+ encryptedKeyForRealm.length];
+ ByteBuffer buffer = ByteBuffer.wrap(initializationVectorAndEncryptedKey);
+ buffer.order(ByteOrder.BIG_ENDIAN);
+ buffer.putInt(initializationVector.length);
+ buffer.put(initializationVector);
+ buffer.put(encryptedKeyForRealm);
+ activity.getSharedPreferences("realm_key", Context.MODE_PRIVATE).edit()
+ .putString("iv_and_encrypted_key",
+ Base64.encodeToString(initializationVectorAndEncryptedKey, Base64.NO_WRAP))
+ .apply();
+
+ return realmKey; // pass to a realm configuration via encryptionKey()
+}
+
+```
+
+#### Kotlin
+
+```kotlin
+// Create a key to encrypt a realm and save it securely in the keystore
+fun getNewKey(): ByteArray {
+ // open a connection to the android keystore
+ val keyStore: KeyStore
+ try {
+ keyStore = KeyStore.getInstance("AndroidKeyStore")
+ keyStore.load(null)
+ } catch (e: Exception) {
+ Log.v("EXAMPLE", "Failed to open the keystore.")
+ throw RuntimeException(e)
+ }
+
+ // create a securely generated random asymmetric RSA key
+ val realmKey = ByteArray(Realm.ENCRYPTION_KEY_LENGTH)
+ SecureRandom().nextBytes(realmKey)
+
+ // create a cipher that uses AES encryption -- we'll use this to encrypt our key
+ val cipher: Cipher
+ cipher = try {
+ Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES
+ + "/" + KeyProperties.BLOCK_MODE_CBC
+ + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7)
+ } catch (e: Exception) {
+ Log.e("EXAMPLE", "Failed to create a cipher.")
+ throw RuntimeException(e)
+ }
+
+ // generate secret key
+ val keyGenerator: KeyGenerator
+ keyGenerator = try {
+ KeyGenerator.getInstance(
+ KeyProperties.KEY_ALGORITHM_AES,
+ "AndroidKeyStore")
+ } catch (e: NoSuchAlgorithmException) {
+ Log.e("EXAMPLE", "Failed to access the key generator.")
+ throw RuntimeException(e)
+ }
+ val keySpec = KeyGenParameterSpec.Builder(
+ "realm_key",
+ KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
+ .setUserAuthenticationRequired(true)
+ .setUserAuthenticationValidityDurationSeconds(
+ AUTH_VALID_DURATION_IN_SECOND)
+ .build()
+ try {
+ keyGenerator.init(keySpec)
+ } catch (e: InvalidAlgorithmParameterException) {
+ Log.e("EXAMPLE", "Failed to generate a secret key.")
+ throw RuntimeException(e)
+ }
+ keyGenerator.generateKey()
+
+ // access the generated key in the android keystore, then
+ // use the cipher to create an encrypted version of the key
+ val initializationVector: ByteArray
+ val encryptedKeyForRealm: ByteArray
+ try {
+ val secretKey = keyStore.getKey("realm_key", null) as SecretKey
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey)
+ encryptedKeyForRealm = cipher.doFinal(realmKey)
+ initializationVector = cipher.iv
+ } catch (e: Exception) {
+ Log.e("EXAMPLE", "Failed encrypting the key with the secret key.")
+ throw RuntimeException(e)
+ }
+
+ // keep the encrypted key in shared preferences
+ // to persist it across application runs
+ val initializationVectorAndEncryptedKey = ByteArray(Integer.BYTES +
+ initializationVector.size +
+ encryptedKeyForRealm.size)
+ val buffer = ByteBuffer.wrap(initializationVectorAndEncryptedKey)
+ buffer.order(ByteOrder.BIG_ENDIAN)
+ buffer.putInt(initializationVector.size)
+ buffer.put(initializationVector)
+ buffer.put(encryptedKeyForRealm)
+ activity!!.getSharedPreferences("realm_key", Context.MODE_PRIVATE).edit()
+ .putString("iv_and_encrypted_key",
+ Base64.encodeToString(initializationVectorAndEncryptedKey, Base64.NO_WRAP))
+ .apply()
+ return realmKey // pass to a realm configuration via encryptionKey()
+}
+
+```
+
+### Access an Existing Encryption Key
+The following code demonstrates how to access and decrypt a securely
+stored encryption key for a realm:
+
+#### Java
+
+```java
+// Access the encrypted key in the keystore, decrypt it with the secret,
+// and use it to open and read from the realm again
+public byte[] getExistingKey() {
+ // open a connection to the android keystore
+ KeyStore keyStore;
+ try {
+ keyStore = KeyStore.getInstance("AndroidKeyStore");
+ keyStore.load(null);
+ } catch (KeyStoreException | NoSuchAlgorithmException
+ | CertificateException | IOException e) {
+ Log.e("EXAMPLE", "Failed to open the keystore.");
+ throw new RuntimeException(e);
+ }
+
+ // access the encrypted key that's stored in shared preferences
+ byte[] initializationVectorAndEncryptedKey = Base64.decode(activity
+ .getSharedPreferences("realm_key", Context.MODE_PRIVATE)
+ .getString("iv_and_encrypted_key", null), Base64.DEFAULT);
+ ByteBuffer buffer = ByteBuffer.wrap(initializationVectorAndEncryptedKey);
+ buffer.order(ByteOrder.BIG_ENDIAN);
+
+ // extract the length of the initialization vector from the buffer
+ int initializationVectorLength = buffer.getInt();
+ // extract the initialization vector based on that length
+ byte[] initializationVector = new byte[initializationVectorLength];
+ buffer.get(initializationVector);
+ // extract the encrypted key
+ byte[] encryptedKey = new byte[initializationVectorAndEncryptedKey.length
+ - Integer.BYTES
+ - initializationVectorLength];
+ buffer.get(encryptedKey);
+
+ // create a cipher that uses AES encryption to decrypt our key
+ Cipher cipher;
+ try {
+ cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES
+ + "/" + KeyProperties.BLOCK_MODE_CBC
+ + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+ Log.e("EXAMPLE", "Failed to create cipher.");
+ throw new RuntimeException(e);
+ }
+
+ // decrypt the encrypted key with the secret key stored in the keystore
+ byte[] decryptedKey;
+ try {
+ final SecretKey secretKey =
+ (SecretKey) keyStore.getKey("realm_key", null);
+ final IvParameterSpec initializationVectorSpec =
+ new IvParameterSpec(initializationVector);
+ cipher.init(Cipher.DECRYPT_MODE, secretKey, initializationVectorSpec);
+ decryptedKey = cipher.doFinal(encryptedKey);
+ } catch (InvalidKeyException e) {
+ Log.e("EXAMPLE", "Failed to decrypt. Invalid key.");
+ throw new RuntimeException(e);
+ } catch (UnrecoverableKeyException | NoSuchAlgorithmException
+ | BadPaddingException | KeyStoreException
+ | IllegalBlockSizeException | InvalidAlgorithmParameterException e) {
+ Log.e("EXAMPLE",
+ "Failed to decrypt the encrypted realm key with the secret key.");
+ throw new RuntimeException(e);
+ }
+ return decryptedKey; // pass to a realm configuration via encryptionKey()
+}
+
+```
+
+#### Kotlin
+
+```kotlin
+// Access the encrypted key in the keystore, decrypt it with the secret,
+// and use it to open and read from the realm again
+fun getExistingKey(): ByteArray {
+ // open a connection to the android keystore
+ val keyStore: KeyStore
+ try {
+ keyStore = KeyStore.getInstance("AndroidKeyStore")
+ keyStore.load(null)
+ } catch (e: Exception) {
+ Log.e("EXAMPLE", "Failed to open the keystore.")
+ throw RuntimeException(e)
+ }
+
+ // access the encrypted key that's stored in shared preferences
+ val initializationVectorAndEncryptedKey = Base64.decode(activity
+ ?.getSharedPreferences("realm_key", Context.MODE_PRIVATE)
+ ?.getString("iv_and_encrypted_key", null), Base64.DEFAULT)
+ val buffer = ByteBuffer.wrap(initializationVectorAndEncryptedKey)
+ buffer.order(ByteOrder.BIG_ENDIAN)
+
+ // extract the length of the initialization vector from the buffer
+ val initializationVectorLength = buffer.int
+ // extract the initialization vector based on that length
+ val initializationVector = ByteArray(initializationVectorLength)
+ buffer[initializationVector]
+ // extract the encrypted key
+ val encryptedKey = ByteArray(initializationVectorAndEncryptedKey.size
+ - Integer.BYTES
+ - initializationVectorLength)
+ buffer[encryptedKey]
+
+ // create a cipher that uses AES encryption to decrypt our key
+ val cipher: Cipher
+ cipher = try {
+ Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES
+ + "/" + KeyProperties.BLOCK_MODE_CBC
+ + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7)
+ } catch (e: Exception) {
+ Log.e("EXAMPLE", "Failed to create cipher.")
+ throw RuntimeException(e)
+ }
+
+ // decrypt the encrypted key with the secret key stored in the keystore
+ val decryptedKey: ByteArray
+ decryptedKey = try {
+ val secretKey = keyStore.getKey("realm_key", null) as SecretKey
+ val initializationVectorSpec = IvParameterSpec(initializationVector)
+ cipher.init(Cipher.DECRYPT_MODE, secretKey, initializationVectorSpec)
+ cipher.doFinal(encryptedKey)
+ } catch (e: InvalidKeyException) {
+ Log.e("EXAMPLE", "Failed to decrypt. Invalid key.")
+ throw RuntimeException(e)
+ } catch (e: Exception ) {
+ Log.e("EXAMPLE",
+ "Failed to decrypt the encrypted realm key with the secret key.")
+ throw RuntimeException(e)
+ }
+ return decryptedKey // pass to a realm configuration via encryptionKey()
+}
+
+```
diff --git a/docs/guides/realm-files/open-and-close-a-realm.md b/docs/guides/realm-files/open-and-close-a-realm.md
new file mode 100644
index 0000000000..3ea9a9d5ec
--- /dev/null
+++ b/docs/guides/realm-files/open-and-close-a-realm.md
@@ -0,0 +1,364 @@
+# Open & Close a Realm - Java SDK
+Interacting with realms in an Android
+application uses the following high-level series of steps:
+
+1. Create a configuration for the realm you want to open.
+2. Open the realm using the config.
+3. Close the realm to free up resources when you're finished.
+
+## The Default Realm
+You can save any `RealmConfiguration`
+as the default for your application using the
+`setDefaultConfiguration()`
+method:
+
+#### Java
+
+```java
+RealmConfiguration config = new RealmConfiguration.Builder()
+ .name("default-realm")
+ .allowQueriesOnUiThread(true)
+ .allowWritesOnUiThread(true)
+ .compactOnLaunch()
+ .inMemory()
+ .build();
+// set this config as the default realm
+Realm.setDefaultConfiguration(config);
+```
+
+#### Kotlin
+
+```kotlin
+val config = RealmConfiguration.Builder()
+ .name("default-realm")
+ .allowQueriesOnUiThread(true)
+ .allowWritesOnUiThread(true)
+ .compactOnLaunch()
+ .inMemory()
+ .build()
+// set this config as the default realm
+Realm.setDefaultConfiguration(config)
+```
+
+You can then use
+`getDefaultConfiguration()`
+to access that configuration, or
+`getDefaultInstance()`
+to open a realm with that configuration:
+
+#### Java
+
+```java
+Realm realm = Realm.getDefaultInstance();
+Log.v("EXAMPLE","Successfully opened the default realm at: " + realm.getPath());
+```
+
+#### Kotlin
+
+```kotlin
+val realm = Realm.getDefaultInstance()
+Log.v("EXAMPLE","Successfully opened the default realm at: ${realm.path}")
+```
+
+## Local Realms
+Local realms store data only on the client device. You can customize
+the settings for a local realm with `RealmConfiguration`.
+
+### Local Realm Configuration
+To configure settings for a realm, create a
+`RealmConfiguration` with a
+`RealmConfiguration.Builder`.
+The following example configures a local realm with:
+
+- the file name "alternate-realm"
+- synchronous reads explicitly allowed on the UI thread
+- synchronous writes explicitly allowed on the UI thread
+- automatic compaction when launching the realm to save file space
+
+#### Java
+
+```java
+RealmConfiguration config = new RealmConfiguration.Builder()
+ .name("alternate-realm")
+ .allowQueriesOnUiThread(true)
+ .allowWritesOnUiThread(true)
+ .compactOnLaunch()
+ .build();
+
+Realm realm = Realm.getInstance(config);
+Log.v("EXAMPLE", "Successfully opened a realm at: " + realm.getPath());
+
+```
+
+#### Kotlin
+
+```kotlin
+val config = RealmConfiguration.Builder()
+ .name("alternate-realm")
+ .allowQueriesOnUiThread(true)
+ .allowWritesOnUiThread(true)
+ .compactOnLaunch()
+ .build()
+val realm = Realm.getInstance(config)
+Log.v("EXAMPLE", "Successfully opened a realm at: ${realm.path}")
+
+```
+
+> Important:
+> By default, you can only read or write to a realm in your
+application's UI thread using
+asynchronous transactions. That is,
+you can only use `Realm` methods whose name ends with the word
+`Async` in the main thread of your Android application unless you
+explicitly allow the use of synchronous methods.
+>
+> This restriction exists for the benefit of your application users:
+performing read and write operations on the UI thread can lead to
+unresponsive or slow UI interactions, so it's usually best to handle
+these operations either asynchronously or in a background thread.
+
+### Open a Local Realm
+To open a realm, create a
+`RealmConfiguration` with
+`RealmConfiguration.Builder` and
+pass the resulting `RealmConfiguration` to
+`getInstance()`
+or `getInstanceAsync()`:
+
+#### Java
+
+```java
+RealmConfiguration config = new RealmConfiguration.Builder()
+ .allowQueriesOnUiThread(true)
+ .allowWritesOnUiThread(true)
+ .build();
+
+Realm realm;
+try {
+ realm = Realm.getInstance(config);
+ Log.v("EXAMPLE", "Successfully opened a realm at: " + realm.getPath());
+} catch (RealmFileException ex) {
+ Log.v("EXAMPLE", "Error opening the realm.");
+ Log.v("EXAMPLE", ex.toString());
+}
+
+```
+
+#### Kotlin
+
+```kotlin
+val config = RealmConfiguration.Builder()
+ .allowQueriesOnUiThread(true)
+ .allowWritesOnUiThread(true)
+ .build()
+
+var realm: Realm
+try {
+ realm = Realm.getInstance(config)
+ Log.v("EXAMPLE", "Successfully opened a realm at: ${realm.path}")
+} catch(ex: RealmFileException) {
+ Log.v("EXAMPLE", "Error opening the realm.")
+ Log.v("EXAMPLE", ex.toString())
+}
+
+```
+
+### Read-Only Realms
+It's sometimes useful to ship a prepared realm file with your app
+that contains shared data that does not frequently change. You can use
+the `readOnly()`
+method when configuring your realm to make it read-only. This can
+prevent accidental writes to the realm and causes the realm to
+throw an `IllegalStateException` if a write occurs.
+
+> Warning:
+> Read-only realms are only enforced as read-only in process.
+The realm file itself is still writeable.
+>
+
+#### Java
+
+```java
+RealmConfiguration config = new RealmConfiguration.Builder()
+ .assetFile("bundled.realm")
+ .readOnly()
+ .modules(new BundledRealmModule())
+ .build();
+```
+
+#### Kotlin
+
+```kotlin
+val config = RealmConfiguration.Builder()
+ .assetFile("readonly.realm")
+ .readOnly()
+ .modules(BundledRealmModule())
+ .build()
+```
+
+### In-Memory Realms
+You can create a realm that runs entirely in memory without being written
+to a file. When memory runs low on an Android device, in-memory realms
+may [swap](https://en.wikipedia.org/wiki/Memory_paging#Terminology) temporarily from main
+memory to disk space. The SDK deletes all files created by an in-memory
+realm when:
+
+- the realm closes
+- all references to that realm fall out of scope
+
+To create an in-memory realm, use `inMemory()`
+when configuring your realm:
+
+#### Java
+
+```java
+RealmConfiguration config = new RealmConfiguration.Builder()
+ .inMemory()
+ .name("java.transient.realm")
+ .build();
+Realm realm = Realm.getInstance(config);
+```
+
+#### Kotlin
+
+```kotlin
+val config = RealmConfiguration.Builder()
+ .inMemory()
+ .name("kt.transient.realm")
+ .build()
+val realm = Realm.getInstance(config)
+```
+
+### Dynamic Realms
+Conventional realms define a schema using `RealmObject` subclasses
+or the `RealmModel` interface. A
+`DynamicRealm` uses strings to
+define a schema at runtime. Opening a dynamic realm uses the same
+configuration as a conventional realm, but dynamic realms ignore
+all configured schema, migration, and schema versions.
+
+Dynamic realms offer flexibility at the expense of type safety and
+performance. As a result, only use dynamic realms when that
+flexibility is required, such as during migrations, manual client
+resets, and when working with string-based data like CSV files or JSON.
+
+To open a Dynamic Realm with a mutable schema, use
+`DynamicRealm`:
+
+#### Java
+
+```java
+RealmConfiguration config = new RealmConfiguration.Builder()
+ .allowWritesOnUiThread(true)
+ .allowQueriesOnUiThread(true)
+ .name("java.dynamic.realm")
+ .build();
+DynamicRealm dynamicRealm = DynamicRealm.getInstance(config);
+
+// all objects in a DynamicRealm are DynamicRealmObjects
+AtomicReference frog = new AtomicReference<>();
+dynamicRealm.executeTransaction(transactionDynamicRealm -> {
+ // add type Frog to the schema with name and age fields
+ dynamicRealm.getSchema()
+ .create("Frog")
+ .addField("name", String.class)
+ .addField("age", int.class);
+ frog.set(transactionDynamicRealm.createObject("Frog"));
+ frog.get().set("name", "Wirt Jr.");
+ frog.get().set("age", 42);
+});
+
+// access all fields in a DynamicRealm using strings
+String name = frog.get().getString("name");
+int age = frog.get().getInt("age");
+
+// because an underlying schema still exists,
+// accessing a field that does not exist throws an exception
+try {
+ frog.get().getString("doesn't exist");
+} catch (IllegalArgumentException e) {
+ Log.e("EXAMPLE", "That field doesn't exist.");
+}
+
+// Queries still work normally
+RealmResults frogs = dynamicRealm.where("Frog")
+ .equalTo("name", "Wirt Jr.")
+ .findAll();
+```
+
+#### Kotlin
+
+```kotlin
+val config = RealmConfiguration.Builder()
+ .allowWritesOnUiThread(true)
+ .allowQueriesOnUiThread(true)
+ .name("kt.dynamic.realm")
+ .build()
+val dynamicRealm = DynamicRealm.getInstance(config)
+
+// all objects in a DynamicRealm are DynamicRealmObjects
+var frog: DynamicRealmObject? = null
+dynamicRealm.executeTransaction { transactionDynamicRealm: DynamicRealm ->
+ // add type Frog to the schema with name and age fields
+ dynamicRealm.schema
+ .create("Frog")
+ .addField("name", String::class.java)
+ .addField("age", Integer::class.java)
+ frog = transactionDynamicRealm.createObject("Frog")
+ frog?.set("name", "Wirt Jr.")
+ frog?.set("age", 42)
+}
+
+// access all fields in a DynamicRealm using strings
+val name = frog?.getString("name")
+val age = frog?.getInt("age")
+
+// because an underlying schema still exists,
+// accessing a field that does not exist throws an exception
+try {
+ frog?.getString("doesn't exist")
+} catch (e: IllegalArgumentException) {
+ Log.e("EXAMPLE", "That field doesn't exist.")
+}
+
+// Queries still work normally
+val frogs = dynamicRealm.where("Frog")
+ .equalTo("name", "Wirt Jr.")
+ .findAll()
+```
+
+## Close a Realm
+It is important to remember to call the `close()` method when done with a
+realm instance to free resources. Neglecting to close realms can lead to an
+`OutOfMemoryError`.
+
+#### Java
+
+```java
+realm.close();
+
+```
+
+#### Kotlin
+
+```kotlin
+realm.close()
+
+```
+
+## Configure Which Classes to Include in Your Realm Schema
+Realm modules are collections of Realm object
+models. Specify a module or modules when opening a realm to control
+which classes Realm should include in your schema. If you
+do not specify a module, Realm uses the default module,
+which includes all Realm objects defined in your
+application.
+
+> Note:
+> Libraries that include Realm must expose and use their
+schema through a module. Doing so prevents the library from
+generating the default `RealmModule`, which would conflict with
+the default `RealmModule` used by any app that includes the library.
+Apps using the library access library classes through the module.
+>
+
diff --git a/docs/guides/realm-query-language.md b/docs/guides/realm-query-language.md
new file mode 100644
index 0000000000..f86ae0ae54
--- /dev/null
+++ b/docs/guides/realm-query-language.md
@@ -0,0 +1,735 @@
+# Realm Query Language
+Realm Query Language (RQL) is a string-based query language to constrain
+searches when retrieving objects from a realm. SDK-specific methods pass queries
+to the Realm query engine, which retrieves matching objects from the realm.
+Realm Query Language syntax is based on [NSPredicate](https://developer.apple.com/documentation/foundation/nspredicate).
+
+Queries evaluate a predicate for every object in the collection being queried.
+If the predicate resolves to `true`, the results collection includes the object.
+
+You can use Realm Query Language in most Realm SDKs with your SDK's filter
+or query methods. The Swift SDK is the exception, as it uses the
+NSPredicate query API.
+Some SDKs also support idiomatic APIs for querying realms in their language.
+
+You can also use Realm Query Language to browse for data in
+Realm Studio. Realm Studio is a visual tool
+to view, edit, and design Realm files.
+
+## Examples on This Page
+Many of the examples in this page use a simple data set for a to-do list app.
+The two Realm object types are `Project` and `Item`.
+
+- An `Item` has a name, assignee's name, and completed flag.
+There is also an arbitrary number for priority (higher is more important)
+and a count of minutes spent working on it.
+- A `Project` has zero or more `Items` and an optional quota
+for minimum number of to-do items expected to be completed.
+
+See the schema for these two classes, `Project` and `Item`, below:
+
+### Java
+
+```java
+public class Item extends RealmObject {
+ ObjectId id = new ObjectId();
+ String name;
+ Boolean isComplete = false;
+ String assignee;
+ Integer priority = 0;
+ Integer progressMinutes = 0;
+ @LinkingObjects("items")
+ final RealmResults projects = null;
+}
+public class Project extends RealmObject {
+ ObjectId id = new ObjectId();
+ String name;
+ RealmList items;
+ Integer quota = null;
+}
+```
+
+### Kotlin
+
+```kotlin
+open class Item(): RealmObject() {
+ var id: ObjectId = new ObjectId()
+ @FullText
+ lateinit var name: String
+ var isComplete: Boolean = false
+ var assignee: String? = null
+ var priority: Int = 0
+ var progressMinutes: Int = 0
+}
+
+open class Project(): RealmObject() {
+ var id: ObjectId = new ObjectId()
+ lateinit var name: String
+ lateinit var items: RealmList
+ var quota: Int? = null
+}
+```
+
+
+
+## Expressions
+Filters consist of **expressions** in a predicate. An expression consists of
+one of the following:
+
+- The name of a property of the object currently being evaluated.
+- An operator and up to two argument expression(s). For example, in the
+expression `A + B`, the entirety of `A + B` is an expression, but `A`
+and `B` are also argument expressions to the operator `+`.
+- A value, such as a string (`'hello'`) or a number (`5`).
+
+```javascript
+"progressMinutes > 1 AND assignee == $0", "Ali"
+
+```
+
+## Parameterized Queries
+Create parameterized queries to interpolate variables into prepared
+Realm Query Language statements. The syntax for interpolated variables is
+`$`, starting at `0`. Pass the positional arguments as
+additional arguments to Realm SDK methods that use Realm Query Language.
+
+Include just one parameter with `$0`.
+
+```js
+"progressMinutes > 1 AND assignee == $0", "Ali"
+
+```
+
+Include multiple parameters with ascending integers starting at `$0`.
+
+```js
+"progressMinutes > $0 AND assignee == $1", 1, "Alex"
+
+```
+
+### Query Formats
+The following table shows how a query should be formatted when serialized and
+parameterized for the following data types:
+
+|Type|Parameterized Example|Serialized Example|Note|
+| --- | --- | --- | --- |
+|Boolean|"setting == $0", false|"setting == false"|`true` or `false` values.|
+|String|"name == $0", "George"|"name == 'George'"|Applies to `string` and `char` data type.|
+|Number|"age > $0", 5.50|"age > 5.50"|Applies to `int`, `short`, `long`, `double`, `Decimal128`, and `float` data types.|
+|Date|"date < $0", dateObject|"date < 2021-02-20@17:30:15:0"|For parameterized date queries, you must pass in a date object. For serialized date queries, you can represented the date in the following formats: As an explicit date and time- YYYY-MM-DD@HH:mm:ss:nn (year-month-day@hours:minutes:seconds:nanoseconds) As a `datetime` relative to the [Unix epoch](https://en.wikipedia.org/wiki/Unix_time)- Ts:n (T, designates the start of the time; `s`, seconds; `n`, nanoseconds) Parameterized `Date` object|
+|ObjectID|"_id == $0", oidValue|"_id == oid(507f1f77bcf86cd799439011)"|For parameterized ObjectId queries, you must pass in an ObjectId. For serialized ObjectId queries, the string representation is `oid()`.|
+|UUID|"id == $0", uuidValue|"id == uuid(d1b186e1-e9e0-4768-a1a7-c492519d47ee)"|For parameterized UUID queries, you must pass in a UUID. For serialized UUID queries, the string representation is `uuid()`.|
+|Binary|"value == $0", "binary"|"value == 'binary'"|For ASCII characters, RQL serializes the binary value like a string, with quotes. For non-printable characters, RQL serializes the binary to a base 64 value.|
+|List|"ANY items.name == {$0, $1}", "milk", "bread"|"ANY items.name == {'milk', 'bread'}"|Applies for list, collections, and sets. A parameterized value should be used for each member of the list.|
+|RealmObject|"ANY items == $0", obj("Item", oid(6489f036f7bd0546377303ab))|"ANY items == obj('Item', oid(6489f036f7bd0546377303ab))"|To pass in a RealmObject, you need the class and primary key of the object.|
+
+## Dot Notation
+When referring to an object property, you can use **dot notation** to refer
+to child properties of that object. You can even refer to the properties of
+embedded objects and relationships with dot notation.
+
+For example, consider a query on an object with a `workplace` property that
+refers to a Workplace object. The Workplace object has an embedded object
+property, `address`. You can chain dot notations to refer to the zipcode
+property of that address:
+
+```js
+"workplace.address.zipcode == 10019"
+
+```
+
+## Nil Type
+Realm Query Language include the `nil` type to represent a null pointer.
+You can either reference `nil` directly in your queries or with a parameterized query.
+If you're using a parameterized query, each SDK maps its respective null pointer
+to `nil`.
+
+```js
+"assignee == nil"
+
+```
+
+```js
+// comparison to language null pointer
+"assignee == $0", null
+
+```
+
+## Comparison Operators
+The most straightforward operation in a search is to compare
+values.
+
+> Important:
+> The type on both sides of the operator must be equivalent. For
+example, comparing an ObjectId with string will result in a precondition
+failure with a message like:
+>
+> ```
+> "Expected object of type object id for property 'id' on object of type
+> 'User', but received: 11223344556677889900aabb (Invalid value)"
+> ```
+>
+> You can compare any numeric type with any other numeric type,
+including decimal, float, and Decimal128.
+>
+
+|Operator|Description|
+| --- | --- |
+|`BETWEEN {number1, number2}`|Evaluates to `true` if the left-hand numerical or date expression is between or equal to the right-hand range. For dates, this evaluates to `true` if the left-hand date is within the right-hand date range.|
+|== , =|Evaluates to `true` if the left-hand expression is equal to the right-hand expression.|
+|>|Evaluates to `true` if the left-hand numerical or date expression is greater than the right-hand numerical or date expression. For dates, this evaluates to `true` if the left-hand date is later than the right-hand date.|
+|>=|Evaluates to `true` if the left-hand numerical or date expression is greater than or equal to the right-hand numerical or date expression. For dates, this evaluates to `true` if the left-hand date is later than or the same as the right-hand date.|
+|IN|Evaluates to `true` if the left-hand expression is in the right-hand list. This is equivalent to and used as a shorthand for `== ANY`.|
+|<|Evaluates to `true` if the left-hand numerical or date expression is less than the right-hand numerical or date expression. For dates, this evaluates to `true` if the left-hand date is earlier than the right-hand date.|
+|<=|Evaluates to `true` if the left-hand numeric expression is less than or equal to the right-hand numeric expression. For dates, this evaluates to `true` if the left-hand date is earlier than or the same as the right-hand date.|
+|!= , <>|Evaluates to `true` if the left-hand expression is not equal to the right-hand expression.|
+
+> Example:
+> The following example uses Realm Query Language's comparison operators to:
+>
+> - Find high priority to-do items by comparing the value of the `priority`
+property value with a threshold number, above which priority can be considered high.
+> - Find long-running to-do items by seeing if the `progressMinutes` property
+is at or above a certain value.
+> - Find unassigned to-do items by finding items where the `assignee` property
+is equal to `null`.
+> - Find to-do items within a certain time range by finding items where the
+`progressMinutes` property is between two numbers.
+> - Find to-do items with a certain amount of `progressMinutes` from the
+given list.
+>
+> ```javascript
+> // Find high priority to-do items by comparing the value of the ``priority``
+> // property value with a threshold number, above which priority can be considered high.
+> "priority > $0", 5
+>
+> // Find long-running to-do items by seeing if the progressMinutes property is at or above a certain value.
+> "progressMinutes > $0", 120
+>
+> // Find unassigned to-do items by finding items where the assignee property is equal to null.
+> "assignee == $0", null
+>
+> // Find to-do items within a certain time range by finding items
+> // where the progressMinutes property is between two numbers.
+> "progressMinutes BETWEEN { $0 , $1 }", 30, 60
+>
+> // Find to-do items with a certain amount of progressMinutes from the given list.
+> "progressMinutes IN { $0, $1, $2, $3, $4, $5 }", 10, 20, 30, 40, 50, 60
+>
+> ```
+>
+
+## Logical Operators
+Make compound predicates using logical operators.
+
+|Operator|Description|
+| --- | --- |
+|AND &&|Evaluates to `true` if both left-hand and right-hand expressions are `true`.|
+|NOT !|Negates the result of the given expression.|
+|OR \\|\\||Evaluates to `true` if either expression returns `true`.|
+
+> Example:
+> We can use the query language's logical operators to find
+all of Ali's completed to-do items. That is, we find all items
+where the `assignee` property value is equal to 'Ali' AND
+the `isComplete` property value is `true`:
+>
+> ```javascript
+> "assignee == $0 AND isComplete == $1", "Ali", true
+>
+> ```
+>
+
+## String Operators
+Compare string values using these string operators.
+Regex-like wildcards allow more flexibility in search.
+
+> Note:
+> You can use the following modifiers with the string operators:
+>
+> - `[c]` for case insensitivity. `"name CONTAINS[c] $0", 'a'`
+>
+
+|Operator|Description|
+| --- | --- |
+|BEGINSWITH|Evaluates to `true` if the left-hand string expression begins with the right-hand string expression. This is similar to `contains`, but only matches if the right-hand string expression is found at the beginning of the left-hand string expression.|
+|CONTAINS|Evaluates to `true` if the right-hand string expression is found anywhere in the left-hand string expression.|
+|ENDSWITH|Evaluates to `true` if the left-hand string expression ends with the right-hand string expression. This is similar to `contains`, but only matches if the left-hand string expression is found at the very end of the right-hand string expression.|
+|LIKE|Evaluates to `true` if the left-hand string expression matches the right-hand string wildcard string expression. A wildcard string expression is a string that uses normal characters with two special wildcard characters: The `*` wildcard matches zero or more of any character The `?` wildcard matches any character. For example, the wildcard string "d?g" matches "dog", "dig", and "dug", but not "ding", "dg", or "a dog".|
+|== , =|Evaluates to `true` if the left-hand string is lexicographically equal to the right-hand string.|
+|!= , <>|Evaluates to `true` if the left-hand string is not lexicographically equal to the right-hand string.|
+
+> Example:
+> We use the query engine's string operators to find:
+>
+> - Projects with a name starting with the letter 'e'
+> - Projects with names that contain 'ie'
+>
+> ```javascript
+> "name BEGINSWITH[c] $0", 'e'
+>
+> "name CONTAINS $0", 'ie'
+>
+> ```
+>
+
+## ObjectId and UUID Operators
+Query [BSON ObjectIds](https://www.mongodb.com/docs/manual/reference/method/ObjectId/) and
+[UUIDs](https://www.mongodb.com/docs/manual/reference/method/UUID/).
+These data types are often used as primary keys.
+
+To query with ObjectIds, use a parameterized query. Pass the ObjectId or UUID
+you're querying against as the argument.
+
+```js
+"_id == $0", oidValue
+
+```
+
+You can also put a string representation of the ObjectId you're evaluating
+in `oid()`.
+
+```js
+"_id == oid(6001c033600510df3bbfd864)"
+
+```
+
+To query with UUIDs, put a string representation of the UUID you're evaluating
+in `uuid()`.
+
+```js
+"id == uuid(d1b186e1-e9e0-4768-a1a7-c492519d47ee)"
+
+```
+
+|Operator|Description|
+| --- | --- |
+|== , =|Evaluates to `true` if the left-hand value is equal to the right-hand value.|
+|!= , <>|Evaluates to `true` if the left-hand value is not equal to the right-hand value.|
+
+## Arithmetic Operators
+Perform basic arithmetic in one side of a RQL expression when evaluating
+numeric data types.
+
+```js
+ "2 * priority > 6"
+ // Is equivalent to
+ "priority >= 2 * (2 - 1) + 2"
+
+```
+
+You can also use multiple object properties together in a mathematic operation.
+
+```js
+"progressMinutes * priority == 90"
+
+```
+
+|Operator|Description|
+| --- | --- |
+|*|Multiplication.|
+|/|Division.|
+|+|Addition.|
+|-|Subtraction.|
+|()|Group expressions together.|
+
+## Type Operator
+Check the type of a property using the `@type` operator.
+You can only use the type operator with mixed types and dictionaries.
+
+Evaluate the property against a string representation of the data type name.
+Refer to SDK documentation on the mapping from the SDK language's data types
+to Realm data types.
+
+|Operator|Description|
+| --- | --- |
+|`@type`|Check if type of a property is the property name as a string. Use `==` and `!=` to compare equality.|
+
+```js
+ "mixedType.@type == 'string'"
+
+ "mixedType.@type == 'bool'"
+
+```
+
+## Dictionary Operators
+Compare dictionary values using these dictionary operators.
+
+|Operator|Description|
+| --- | --- |
+|`@values`|Returns objects that have the value specified in the right-hand expression.|
+|`@keys`|Returns objects that have the key specified in the right-hand expression.|
+|`@size`, `@count`|The number of elements in a dictionary.|
+|`Dictionary['key']`|Access the value at a key of a dictionary.|
+|`ALL \| ANY \| NONE .@type`|Checks if the dictionary contains properties of certain type.|
+
+You can also use dictionary operators in combination with
+comparison operators to filter objects
+based on dictionary keys and values. The following examples show some ways
+to use dictionary operators with comparison operators. All examples query
+a collection of Realm objects with a dictionary property named `dict`.
+
+> Example:
+> The following examples use various dictionary operators.
+>
+> ```js
+> // Evaluates if there is a dictionary key with the name 'foo'
+> "ANY dict.@keys == $0", 'foo'
+>
+> // Evaluates if there is a dictionary key with key 'foo' and value 'bar
+> "dict['foo'] == $0", 'bar'
+>
+> // Evaluates if there is greater than one key-value pair in the dictionary
+> "dict.@count > $0", 1
+>
+> // Evaluates if dictionary has property of type 'string'
+> "ANY dict.@type == 'string'"
+>
+> // Evaluates if all the dictionary's values are integers
+> "ALL dict.@type == 'bool'"
+>
+> // Evaluates if dictionary does not have any values of type int
+> "NONE dict.@type == 'double'"
+>
+> // ANY is implied.
+> "dict.@type == 'string'"
+>
+> ```
+>
+
+## Date Operators
+Query date types in a realm.
+
+Generally, you should use a parameterized query to pass a date data type
+from the SDK language you are using to a query.
+
+```js
+"timeCompleted < $0", someDate
+
+```
+
+You can also specify dates in the following two ways:
+
+- As a specific date (in UTC)- `YYYY-MM-DD@HH:mm:ss:nnnnnnnnnn` (year-month-day@hours:minutes:seconds:nanoseconds), UTC.
+You can also use `T` instead of `@` to separate the date from the time.
+- As a time in seconds since the [Unix epoch](https://en.wikipedia.org/wiki/Unix_time)- `Ts:n`, where `T` designates the start of the time,
+`s` is the number of seconds, and `n` is the number of nanoseconds.
+
+Date supports comparison operators.
+
+> Example:
+> The following example shows how to use a parameterized query with
+a date object:
+>
+> ```js
+> var date = new Date("2021-02-20@17:30:15:0");
+>
+> "timeCompleted > $0", date
+> ```
+>
+
+## Aggregate Operators
+Apply an aggregate operator to a collection property of a Realm
+object. Aggregate operators traverse a collection and reduce it to a
+single value.
+
+|Operator|Description|
+| --- | --- |
+|@avg|Evaluates to the average value of a given numerical property across a collection. If any values are `null`, they are not counted in the result.|
+|@count|Evaluates to the number of objects in the given collection.|
+|@max|Evaluates to the highest value of a given numerical property across a collection. `null` values are ignored.|
+|@min|Evaluates to the lowest value of a given numerical property across a collection. `null` values are ignored.|
+|@sum|Evaluates to the sum of a given numerical property across a collection, excluding `null` values.|
+
+> Example:
+> These examples all query for projects containing to-do items that meet
+this criteria:
+>
+> - Projects with average item priority above 5.
+> - Projects with an item whose priority is less than 5.
+> - Projects with an item whose priority is greater than 5.
+> - Projects with more than 5 items.
+> - Projects with long-running items.
+>
+> ```javascript
+> var priorityNum = 5;
+>
+> "items.@avg.priority > $0", priorityNum
+>
+> "items.@max.priority < $0", priorityNum
+>
+> "items.@min.priority > $0", priorityNum
+>
+> "items.@count > $0", 5
+>
+> "items.@sum.progressMinutes > $0", 100
+>
+> ```
+>
+
+## Collection Operators
+A **collection operator** lets you query list properties within a collection of objects.
+Collection operators filter a collection by applying a predicate
+to every element of a given list property of the object.
+If the predicate returns true, the object is included in the output collection.
+
+|Operator|Description|
+| --- | --- |
+|`ALL`|Returns objects where the predicate evaluates to `true` for all objects in the collection.|
+|`ANY`, `SOME`|Returns objects where the predicate evaluates to `true` for any objects in the collection.|
+|`NONE`|Returns objects where the predicate evaluates to false for all objects in the collection.|
+
+> Example:
+> This example uses collection operators to find projects that contain to-do items
+matching certain criteria:
+>
+> ```js
+> // Projects with no complete items.
+> "NONE items.isComplete == $0", true
+>
+> // Projects that contain a item with priority 10
+> "ANY items.priority == $0", 10
+>
+> // Projects that only contain completed items
+> "ALL items.isComplete == $0", true
+>
+> // Projects with at least one item assigned to either Alex or Ali
+> "ANY items.assignee IN { $0 , $1 }", "Alex", "Ali"
+>
+> // Projects with no items assigned to either Alex or Ali
+> "NONE items.assignee IN { $0 , $1 }", "Alex", "Ali"
+>
+> ```
+>
+
+## List Comparisons
+You can use comparison operators and
+collection operators to filter based
+on lists of data.
+
+You can compare any type of valid list. This includes:
+
+- collections of Realm objects, which let you filter against other data
+in the realm. `"oid(631a072f75120729dc9223d9) IN items.id"
+`
+- lists defined directly in the query, which let you filter against
+static data. You define static lists as a comma-separated list of
+literal values enclosed in opening (`{`) and closing (`}`) braces. `"priority IN {0, 1, 2}"
+`
+- native list objects passed in a parameterized expression, which let you pass application data
+directly to your queries. `const ids = [
+ new BSON.ObjectId("631a072f75120729dc9223d9"),
+ new BSON.ObjectId("631a0737c98f89f5b81cd24d"),
+ new BSON.ObjectId("631a073c833a34ade21db2b2"),
+];
+const parameterizedQuery = realm.objects("Item").filtered("id IN $0", ids);
+`
+
+If you do not define a collection operator, a list expression defaults
+to the `ANY` operator.
+
+> Example:
+> These two list queries are equivalent:
+>
+> - `age == ANY {18, 21}`
+> - `age == {18, 21}`
+>
+> Both of these queries return objects with an age property equal to
+either 18 or 21. You could also do the opposite by returning objects
+only if the age is not equal to either 18 or 21:
+>
+> - `age == NONE {18, 21}`
+>
+
+The following table includes examples that illustrate how collection
+operators interact with lists and comparison operators:
+
+|Expression|Match?|Reason|
+| --- | --- | --- |
+|`ANY {1, 2, 3} > ALL {1, 2}`|true|A value on the left (3) is greater than some value on the right (both 1 and 2)|
+|`ANY {1, 2, 3} == NONE {1, 2}`|true|3 does not match either of 1 or 2|
+|`ANY {4, 8} == ANY {5, 9, 11}`|false|Neither 4 nor 8 matches any value on the right (5, 9 or 11)|
+|`ANY {1, 2, 7} <= NONE {1, 2}`|true|A value on the left (7) is not less than or equal to both 1 and 2|
+|`ALL {1, 2} IN ANY {1, 2, 3}`|true|Every value on the left (1 and 2) is equal to 1, 2 or 3|
+|`ALL {3, 1, 4, 3} == NONE {1, 2}`|false|1 matches a value in the NONE list (1 or 2)|
+|`ALL {} in ALL {1, 2}`|true|An empty list matches all lists|
+|`NONE {1, 2, 3, 12} > ALL {5, 9, 11}`|false|12 is bigger than all values on the right (5, 9, and 11)|
+|`NONE {4, 8} > ALL {5, 9, 11}`|true|4 and 8 are both less than some value on the right (5, 9, or 11)|
+|`NONE {0, 1} < NONE {1, 2}`|true|0 and 1 are both less than none of 1 and 2|
+
+## Full Text Search
+You can use RQL to query on properties that have a full-text search (FTS)
+annotation. FTS supports boolean match word searches, rather than searches for relevance.
+For information on enabling FTS on a property, see the FTS documentation for
+your SDK:
+
+- Flutter SDK
+- Kotlin SDK
+- .NET SDK
+- Node.js SDK
+- React Native SDK
+- Swift SDK does not yet support Full-Text Search.
+
+To query these properties, use the `TEXT` predicate in your query.
+
+You can search for entire words or phrases, or limit your results with the following characters:
+
+- Exclude results for a word by placing the `-` character in front of the word.
+- Specify prefixes by placing the `*` character at the end of a prefix. Suffix
+searching is not currently supported.
+
+In the following example, we query the `Item.name` property:
+
+```js
+ // Filter for items with 'write' in the name
+ "name TEXT $0", "write"
+
+ // Find items with 'write' but not 'tests' using '-'
+ "name TEXT $0", "write -tests"
+
+ // Find items starting with 'wri-' using '*'
+ "name TEXT $0", "wri*"
+
+```
+
+### Full-Text Search Tokenizer Details
+Full-Text Search (FTS) indexes support:
+
+- Tokens are diacritics- and case-insensitive.
+- Tokens can only consist of characters from ASCII and the Latin-1 supplement (western languages).
+All other characters are considered whitespace.
+- Words split by a hyphen (-) are split into two tokens. For example, `full-text`
+splits into `full` and `text`.
+
+## Backlink Queries
+A backlink is an inverse relationship link that lets you look up objects
+that reference another object. Backlinks use the to-one and to-many
+relationships defined in your object schemas but reverse the direction.
+Every relationship that you define in your schema implicitly has a
+corresponding backlink.
+
+You can access backlinks in queries using the
+`@links..` syntax, where ``
+and `` refer to a specific property on an object type
+that references the queried object type.
+
+```js
+// Find items that belong to a project with a quota greater than 10 (@links)
+"@links.Project.items.quota > 10"
+
+```
+
+You can also define a `linkingObjects` property to explicitly include
+the backlink in your data model. This lets you reference the backlink
+through an assigned property name using standard dot notation.
+
+```js
+// Find items that belong to a project with a quota greater than 10 (LinkingObjects)
+"projects.quota > 10"
+
+```
+
+The result of a backlink is treated like a collection and supports
+collection operators.
+
+```js
+ // Find items where any project that references the item has a quota greater than 0
+ "ANY @links.Project.items.quota > 0"
+ // Find items where all projects that reference the item have a quota greater than 0
+ "ALL @links.Project.items.quota > 0"
+
+```
+
+You can use aggregate operators on the backlink collection.
+
+```js
+ // Find items that are referenced by multiple projects
+ "projects.@count > 1"
+ // Find items that are not referenced by any project
+ "@links.Project.items.@count == 0"
+ // Find items that belong to a project where the average item has
+ // been worked on for at least 5 minutes
+ "@links.Project.items.items.@avg.progressMinutes > 10"
+
+```
+
+You can query the count of all relationships that point to an object by
+using the `@count` operator directly on `@links`.
+
+```js
+// Find items that are not referenced by another object of any type
+"@links.@count == 0"
+
+```
+
+## Subqueries
+Iterate through list properties with another query using the
+`SUBQUERY()` predicate function.
+
+Subqueries are useful for the following scenarios:
+
+- Matching each object in a list property on multiple conditions
+- Counting the number of objects that match a subquery
+
+`SUBQUERY()` has the following structure:
+
+```js
+SUBQUERY(, , )
+```
+
+- `collection`: The name of the property to iterate through
+- `variableName`: A variable name of the element to use in the subquery
+- `predicate`: The subquery predicate.
+Use the variable specified by `variableName` to refer to the
+currently-iterated element.
+
+A subquery iterates through the given collection and checks the given predicate
+against each object in the collection. The predicate can refer to the current
+iterated object with the variable name passed to `SUBQUERY()`.
+
+A subquery expression resolves to a list of objects.
+Realm only supports the `@count` aggregate operator on the result
+of a subquery. This allows you to count how many objects in the subquery
+input collection matched the predicate.
+
+You can use the count of the subquery result as you would any other number
+in a valid expression. In particular, you can compare the count with the
+number `0` to return all matching objects.
+
+> Example:
+> The following example shows two subquery filters on a collection of projects.
+>
+> ```js
+> // Returns projects with items that have not been completed
+> // by a user named Alex.
+> "SUBQUERY(items, $item, $item.isComplete == false AND $item.assignee == 'Alex').@count > 0"
+>
+> // Returns the projects where the number of completed items is
+> // greater than or equal to the value of a project's `quota` property.
+> "SUBQUERY(items, $item, $item.isComplete == true).@count >= quota"
+>
+> ```
+>
+
+## Sort, Distinct & Limit
+Sort and limit the results collection of your query using additional operators.
+
+|Operator|Description|
+| --- | --- |
+|`SORT`|Specify the name of the property to compare, and whether to sort by ascending (`ASC`) or descending (`DESC`) order. If you specify multiple SORT fields, you must specify sort order for each field. With multiple sort fields, the query sorts by the first field, and then the second. For example, if you `SORT (priority DESC, name DESC)`, the query returns sorted by priority, and then by name when priority value is the same.|
+|`DISTINCT`|Specify a name of the property to compare. Remove duplicates for that property in the results collection. If you specify multiple DISTINCT fields, the query removes duplicates by the first field, and then the second. For example, if you `DISTINCT (name, assignee)`, the query only removes duplicates where the values of both properties are the same.|
+|`LIMIT`|Limit the results collection to the specified number.|
+
+> Example:
+> Use the query engine's sort, distinct, and limit operators to find to-do items
+where the assignee is Ali:
+>
+> - Sorted by priority in descending order
+> - Enforcing uniqueness by name
+> - Limiting the results to 5 items
+>
+> ```javascript
+> "assignee == 'Ali' SORT(priority DESC) DISTINCT(name) LIMIT(5)"
+>
+> ```
+>
diff --git a/docs/guides/test-and-debug/debugging.md b/docs/guides/test-and-debug/debugging.md
new file mode 100644
index 0000000000..d96af446ac
--- /dev/null
+++ b/docs/guides/test-and-debug/debugging.md
@@ -0,0 +1,53 @@
+# Debugging - Java SDK
+## Android Studio Debugging
+> Important:
+> The Android Studio debugger can provide misleading values for
+Realm object fields. For correct values, you can watch
+accessor values instead, or use the Realm object
+`toString()` method to see the latest field values.
+>
+
+This section details information you should keep in mind when debugging
+Realm applications with Android Studio to avoid incorrect
+value reporting. When you watch a Realm object,
+you'll see values displayed in the object's fields. These values
+are incorrect because the field values themselves are not used. This is
+because Realm creates a proxy object behind the scenes, overriding
+the getters and setters to access the persisted data in the
+realm. To see the correct values, add a watch on the accessors.
+
+In the image above, the debugger has stopped on line `113`. There are
+three watch values:
+
+- The `person` variable
+- The `person.getName()` accessor
+- The `person.getAge()` accessor
+
+The code from lines `107` to `111` alters the `person` instance by
+changing the name and age in a write transaction. On line `113`, the `person` watch instance reports
+incorrect values for the *field* watch values. The watch values that use
+the *accessors* report values that are correct.
+
+## NDK Debugging
+The Realm Java SDK library contains native code.
+Debugging NDK crashes can be cumbersome, as the default stack trace
+provides minimal information.
+
+We recommend you use a crash reporting tool such as
+[Crashlytics](http://www.crashlytics.com/). This gives you the
+ability to track native errors and gather other valuable information. We
+can help with your issues faster if you have this information.
+
+To enable NDK crash reporting in Crashlytics for
+your project, add the following to the root of your application
+`build.gradle` file:
+
+```groovy
+crashlytics {
+ enableNdk true
+}
+```
+
+> Note:
+> The values `androidNdkOut` and `androidNdkLibsOut` are not needed.
+>
diff --git a/docs/guides/test-and-debug/log-realm-events.md b/docs/guides/test-and-debug/log-realm-events.md
new file mode 100644
index 0000000000..ac005eb06d
--- /dev/null
+++ b/docs/guides/test-and-debug/log-realm-events.md
@@ -0,0 +1,29 @@
+# Log Realm Events - Java SDK
+The SDK logs events to the Android system log automatically. You can
+view these events using [Logcat](https://developer.android.com//studio/debug/am-logcat).
+
+## Set the Client Log Level
+Realm uses the log levels defined by [Log4J](https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Level.html).
+To configure the log level for Realm logs in your application, pass a
+`LogLevel` to
+`RealmLog.setLevel()`:
+
+#### Java
+
+```java
+RealmLog.setLevel(LogLevel.ALL);
+
+```
+
+#### Kotlin
+
+```kotlin
+RealmLog.setLevel(LogLevel.ALL)
+
+```
+
+> Tip:
+> To diagnose and troubleshoot errors while developing your application, set the
+log level to `debug` or `trace`. For production deployments, decrease the
+log level for improved performance.
+>
diff --git a/docs/guides/test-and-debug/testing.md b/docs/guides/test-and-debug/testing.md
new file mode 100644
index 0000000000..6343477a70
--- /dev/null
+++ b/docs/guides/test-and-debug/testing.md
@@ -0,0 +1,1052 @@
+# Testing - Java SDK
+You can test your application using unit tests or integration tests.
+**Unit tests** only assess the logic written in your application's code.
+**Integration tests** assess your application logic, database queries and
+writes, and calls to your application's backend, if you have one. Unit tests
+run on your development machine using the JVM, while integration tests
+run on a physical or emulated Android device. You can run integration
+tests by communicating with actual instances of Realm
+or an App backend using Android's built-in instrumented tests.
+
+Android uses specific file paths and folder names in Android projects
+for unit tests and instrumented tests:
+
+|Test Type|Path|
+| --- | --- |
+|Unit Tests|/app/src/test|
+|Instrumented Tests|/app/src/androidTest|
+
+Because the SDK uses C++ code via Android Native for data
+storage, unit testing requires you to entirely mock interactions with
+Realm. Prefer integration tests for logic that requires
+extensive interaction with the database.
+
+## Integration Tests
+This section shows how to integration test an application that uses
+the Realm SDK. It covers the following concepts in the test
+environment:
+
+- acquiring an application context
+- executing logic on a `Looper` thread
+- how to delay test execution while asynchronous method calls complete
+
+### Application Context
+To initialize the SDK, you'll need to provide an application or activity
+[context](https://developer.android.com/reference/android/content/Context).
+This isn't available by default in Android integration tests. However,
+you can use Android's built-in testing [ActivityScenario](https://developer.android.com/reference/androidx/test/core/app/ActivityScenario)
+class to start an activity in your tests. You can use any activity from
+your application, or you can create an empty activity just for testing.
+Call `ActivityScenario.launch()` with your activity class as a
+parameter to start the simulated activity.
+
+Next, use the `ActivityScenario.onActivity()` method to run a lambda
+on the simulated activity's main thread. In this lambda, you should call
+the `Realm.init()` function to initialize the SDK with your activity
+as a parameter. Additionally, you should save the parameter passed to
+your lambda (the newly created instance of your activity) for future
+use.
+
+Because the `onActivity()` method runs on a different thread, you
+should block your test from executing further until this initial setup completes.
+
+The following example uses an `ActivityScenario`, an empty testing
+activity, and a `CountDownLatch` to demonstrate how to set up an
+environment where you can test your Realm application:
+
+#### Java
+
+```java
+AtomicReference testActivity = new AtomicReference();
+ActivityScenario scenario = ActivityScenario.launch(BasicActivity.class);
+
+// create a latch to force blocking for an async call to initialize realm
+CountDownLatch setupLatch = new CountDownLatch(1);
+
+scenario.onActivity(activity -> {
+ Realm.init(activity);
+ testActivity.set(activity);
+ setupLatch.countDown(); // unblock the latch await
+});
+
+// block until we have an activity to run tests on
+try {
+ Assert.assertTrue(setupLatch.await(1, TimeUnit.SECONDS));
+} catch (InterruptedException e) {
+ Log.e("EXAMPLE", e.getMessage());
+}
+
+```
+
+#### Kotlin
+
+```kotlin
+var testActivity: Activity? = null
+val scenario: ActivityScenario? =
+ ActivityScenario.launch(BasicActivity::class.java)
+
+// create a latch to force blocking for an async call to initialize realm
+val setupLatch = CountDownLatch(1)
+
+scenario?.onActivity{ activity: BasicActivity ->
+ Realm.init(activity)
+ testActivity = activity
+ setupLatch.countDown() // unblock the latch await
+}
+
+```
+
+### Looper Thread
+Realm functionality such as
+Live objects and change notifications only
+work on [Looper](https://developer.android.com/reference/android/os/Looper) threads.
+Threads configured with a `Looper` object pass events over a message
+loop coordinated by the `Looper`. Test functions normally don't have
+a `Looper` object, and configuring one to work in your tests can be
+very error-prone.
+
+Instead, you can use the [Activity.runOnUiThread()](https://developer.android.com/reference/android/app/Activity#runOnUiThread(java.lang.Runnable))
+method of your test activity to execute logic on a thread that already
+has a `Looper` configured. Combine `Activity.runOnUiThread()` with
+a `CountDownLatch` as described in the delay section to prevent your test from completing
+and exiting before your logic has executed. Within the `runOnUiThread()`
+call, you can interact with the SDK just like you normally would in your
+application code:
+
+#### Java
+
+```java
+testActivity.get().runOnUiThread(() -> {
+ // instantiate an app connection
+ String appID = YOUR_APP_ID; // replace this with your test application App ID
+ App app = new App(new AppConfiguration.Builder(appID).build());
+
+ // authenticate a user
+ Credentials credentials = Credentials.anonymous();
+ app.loginAsync(credentials, it -> {
+ if (it.isSuccess()) {
+ Log.v("EXAMPLE", "Successfully authenticated.");
+
+ Realm.getInstanceAsync(config, new Realm.Callback() {
+ @Override
+ public void onSuccess(@NonNull Realm realm) {
+ Log.v("EXAMPLE", "Successfully opened a realm.");
+ // read and write to realm here via transactions
+ testLatch.countDown();
+ realm.executeTransaction(new Realm.Transaction() {
+ @Override
+ public void execute(@NonNull Realm realm) {
+ realm.createObjectFromJson(Frog.class,
+ "{ name: \"Doctor Cucumber\", age: 1, species: \"bullfrog\", owner: \"Wirt\", _id: 0 }");
+ }
+ });
+ realm.close();
+ }
+ @Override
+ public void onError(@NonNull Throwable exception) {
+ Log.e("EXAMPLE", "Failed to open the realm: " + exception.getLocalizedMessage());
+ }
+ });
+ } else {
+ Log.e("EXAMPLE", "Failed login: " + it.getError().getErrorMessage());
+ }
+ });
+});
+
+```
+
+#### Kotlin
+
+```kotlin
+testActivity?.runOnUiThread {
+ // instantiate an app connection
+ val appID: String = YOUR_APP_ID // replace this with your App ID
+ val app = App(AppConfiguration.Builder(appID).build())
+
+ // authenticate a user
+ val credentials = Credentials.anonymous()
+ app.loginAsync(credentials) {
+ if (it.isSuccess) {
+ Log.v("EXAMPLE", "Successfully authenticated.")
+
+ Realm.getInstanceAsync(config, object : Realm.Callback() {
+ override fun onSuccess(realm: Realm) {
+ Log.v("EXAMPLE", "Successfully opened a realm.")
+ // read and write to realm here via transactions
+ realm.executeTransaction {
+ realm.createObjectFromJson(
+ Frog::class.java,
+ "{ name: \"Doctor Cucumber\", age: 1, species: \"bullfrog\", owner: \"Wirt\", _id:0 }"
+ )
+ }
+ testLatch.countDown()
+ realm.close()
+ }
+ override fun onError(exception: Throwable) {
+ Log.e("EXAMPLE",
+ "Failed to open the realm: " + exception.localizedMessage)
+ }
+ })
+ } else {
+ Log.e("EXAMPLE", "Failed login: " + it.error.errorMessage)
+ }
+ }
+}
+
+```
+
+### Delay Test Execution While Async Calls Complete
+Because the SDK uses asynchronous calls for common operations, tests need a way
+to wait for those async calls to complete. Otherwise, your tests will
+exit before your asynchronous (or multi-threaded) calls run. This example
+uses Java's built-in [CountDownLatch](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/CountDownLatch.html). Follow these steps to use a `CountDownLatch` in your own tests:
+
+1. Instantiate a `CountDownLatch` with a count of 1.
+2. After running the async logic your test needs to wait for, call that
+`CountDownLatch` instance's `countDown()` method.
+3. When you need to wait for async logic, add a `try`/`catch` block
+that handles an `InterruptedException`. In that block,
+call that `CountDownLatch` instance's `await()` method.
+4. Pass a timeout interval and unit to `await()`, and wrap
+the call in a `Assert.assertTrue()` assertion. If the logic takes
+too long, the `await()` call times out, returning false and failing
+the test.
+
+### Testing Backend
+Applications that use an App backend should not connect to the
+production backend for testing purposes for the following reasons:
+
+- you should always keep test users and production users separate
+for security and privacy reasons
+- tests often require a clean initial state, so there's a good chance
+your tests will include a setup or teardown method that deletes all
+users or large chunks of data
+
+You can use environments to manage separate
+apps for testing and production.
+
+## Unit Tests
+To unit test Realm applications that use Realm,
+you must [mock](https://en.wikipedia.org/wiki/Mock_object) Realm (and your
+application backend, if you use one). Use the following libraries to
+mock SDK functionality:
+
+- [Robolectric](http://robolectric.org/)
+- [PowerMock](https://powermock.github.io/)
+- [Mockito](https://site.mockito.org/)
+
+To make these libraries available for unit testing in your Android project,
+add the following to the `dependencies` block of your application
+`build.gradle` file:
+
+```
+ testImplementation "org.robolectric:robolectric:4.1"
+ testImplementation "org.mockito:mockito-core:3.3.3"
+ testImplementation "org.powermock:powermock-module-junit4:2.0.9"
+ testImplementation "org.powermock:powermock-module-junit4-rule:2.0.9"
+ testImplementation "org.powermock:powermock-api-mockito2:2.0.9"
+ testImplementation "org.powermock:powermock-classloading-xstream:2.0.9"
+```
+
+> Note:
+> Mocking the SDK in unit tests requires Robolectric, Mockito, and
+Powermock because the SDK uses Android Native C++ method calls to
+interact with Realm. Because the frameworks required to
+override these method calls can be delicate, you should use the
+versions listed above to ensure that your mocking is successful. Some
+recent version updates (particularly Robolectric version 4.2+) can
+break compiliation of unit tests using the SDK.
+>
+
+To configure your unit tests to use Robolectric, PowerMock, and Mockito
+with the SDK, add the following annotations to each unit test class that
+mocks the SDK:
+
+#### Java
+
+```java
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = 28)
+@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "jdk.internal.reflect.*", "androidx.*"})
+@SuppressStaticInitializationFor("io.realm.internal.Util")
+@PrepareForTest({Realm.class, RealmConfiguration.class, RealmQuery.class, RealmResults.class, RealmCore.class, RealmLog.class})
+
+```
+
+#### Kotlin
+
+```kotlin
+@RunWith(RobolectricTestRunner::class)
+@Config(sdk = [28])
+@PowerMockIgnore(
+ "org.mockito.*",
+ "org.robolectric.*",
+ "android.*",
+ "jdk.internal.reflect.*",
+ "androidx.*"
+)
+@SuppressStaticInitializationFor("io.realm.internal.Util")
+@PrepareForTest(
+ Realm::class,
+ RealmConfiguration::class,
+ RealmQuery::class,
+ RealmResults::class,
+ RealmCore::class,
+ RealmLog::class
+)
+
+```
+
+Then, bootstrap Powermock globally in the test class:
+
+#### Java
+
+```java
+// bootstrap powermock
+@Rule
+public PowerMockRule rule = new PowerMockRule();
+
+```
+
+#### Kotlin
+
+```kotlin
+// bootstrap powermock
+@Rule
+var rule = PowerMockRule()
+
+```
+
+Next, mock the components of the SDK that might query native C++ code
+so we don't hit the limitations of the test environment:
+
+#### Java
+
+```java
+// set up realm SDK components to be mocked. The order of these matters
+mockStatic(RealmCore.class);
+mockStatic(RealmLog.class);
+mockStatic(Realm.class);
+mockStatic(RealmConfiguration.class);
+Realm.init(RuntimeEnvironment.application);
+// boilerplate to mock realm components -- this prevents us from hitting any
+// native code
+doNothing().when(RealmCore.class);
+RealmCore.loadLibrary(any(Context.class));
+
+```
+
+#### Kotlin
+
+```kotlin
+// set up realm SDK components to be mocked. The order of these matters
+PowerMockito.mockStatic(RealmCore::class.java)
+PowerMockito.mockStatic(RealmLog::class.java)
+PowerMockito.mockStatic(Realm::class.java)
+PowerMockito.mockStatic(RealmConfiguration::class.java)
+Realm.init(RuntimeEnvironment.application)
+PowerMockito.doNothing().`when`(RealmCore::class.java)
+RealmCore.loadLibrary(ArgumentMatchers.any(Context::class.java))
+
+```
+
+Once you've completed the setup required for mocking, you can start
+mocking components and wiring up behavior for your tests. You can also
+configure PowerMockito to return specific objects when new objects of
+a type are instantiated, so even code that references the default
+realm in your application won't break your tests:
+
+#### Java
+
+```java
+// create the mocked realm
+final Realm mockRealm = mock(Realm.class);
+final RealmConfiguration mockRealmConfig = mock(RealmConfiguration.class);
+// use this mock realm config for all new realm configurations
+whenNew(RealmConfiguration.class).withAnyArguments().thenReturn(mockRealmConfig);
+// use this mock realm for all new default realms
+when(Realm.getDefaultInstance()).thenReturn(mockRealm);
+
+```
+
+#### Kotlin
+
+```kotlin
+// create the mocked realm
+val mockRealm = PowerMockito.mock(Realm::class.java)
+val mockRealmConfig = PowerMockito.mock(
+ RealmConfiguration::class.java
+)
+// use this mock realm config for all new realm configurations
+PowerMockito.whenNew(RealmConfiguration::class.java).withAnyArguments()
+ .thenReturn(mockRealmConfig)
+// use this mock realm for all new default realms
+PowerMockito.`when`(Realm.getDefaultInstance()).thenReturn(mockRealm)
+
+```
+
+After mocking a realm, you'll have to configure data for your test
+cases. See the full example below for some examples of how you can
+provide testing data in unit tests.
+
+### Full Example
+The following example shows a full JUnit `test`
+example mocking Realm in unit tests. This example tests
+an activity that performs some basic Realm operations.
+The tests use mocking to simulate those operations when that activity is
+started during a unit test:
+
+#### Java
+
+```java
+package com.mongodb.realm.examples.java;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.os.Bundle;
+
+import android.os.AsyncTask;
+import android.util.Log;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.mongodb.realm.examples.R;
+import com.mongodb.realm.examples.model.java.Cat;
+
+import io.realm.Realm;
+import io.realm.RealmResults;
+
+public class UnitTestActivity extends AppCompatActivity {
+
+ public static final String TAG = UnitTestActivity.class.getName();
+ private LinearLayout rootLayout = null;
+
+ private Realm realm;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Realm.init(getApplicationContext());
+ setContentView(R.layout.activity_unit_test);
+ rootLayout = findViewById(R.id.container);
+ rootLayout.removeAllViews();
+
+ // open the default Realm for the UI thread.
+ realm = Realm.getDefaultInstance();
+
+ // clean up from previous run
+ cleanUp();
+
+ // small operation that is ok to run on the main thread
+ basicCRUD(realm);
+
+ // more complex operations can be executed on another thread.
+ AsyncTask foo = new AsyncTask() {
+ @Override
+ protected String doInBackground(Void... voids) {
+ String info = "";
+ info += complexQuery();
+ return info;
+ }
+
+ @Override
+ protected void onPostExecute(String result) {
+ showStatus(result);
+ }
+ };
+
+ foo.execute();
+
+ findViewById(R.id.clean_up).setOnClickListener(view -> {
+ view.setEnabled(false);
+ Log.d("TAG", "clean up");
+ cleanUp();
+ view.setEnabled(true);
+ });
+ }
+
+ private void cleanUp() {
+ // delete all cats
+ realm.executeTransaction(r -> r.delete(Cat.class));
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ realm.close(); // remember to close realm when done.
+ }
+
+ private void showStatus(String txt) {
+ Log.i(TAG, txt);
+ TextView tv = new TextView(this);
+ tv.setText(txt);
+ rootLayout.addView(tv);
+ }
+
+ private void basicCRUD(Realm realm) {
+ showStatus("Perform basic Create/Read/Update/Delete (CRUD) operations...");
+
+ // all writes must be wrapped in a transaction to facilitate safe multi threading
+ realm.executeTransaction(r -> {
+ // add a cat
+ Cat cat = r.createObject(Cat.class);
+ cat.setName("John Young");
+ });
+
+ // find the first cat (no query conditions) and read a field
+ final Cat cat = realm.where(Cat.class).findFirst();
+ showStatus(cat.getName());
+
+ // update cat in a transaction
+ realm.executeTransaction(r -> {
+ cat.setName("John Senior");
+ });
+
+ showStatus(cat.getName());
+
+ // add two more cats
+ realm.executeTransaction(r -> {
+ Cat jane = r.createObject(Cat.class);
+ jane.setName("Jane");
+
+ Cat doug = r.createObject(Cat.class);
+ doug.setName("Robert");
+ });
+
+ RealmResults cats = realm.where(Cat.class).findAll();
+ showStatus(String.format("Found %s cats", cats.size()));
+ for (Cat p : cats) {
+ showStatus("Found " + p.getName());
+ }
+ }
+
+ private String complexQuery() {
+ String status = "\n\nPerforming complex Query operation...";
+
+ Realm realm = Realm.getDefaultInstance();
+ status += "\nNumber of cats in the DB: " + realm.where(Cat.class).count();
+
+ // find all cats where name begins with "J".
+ RealmResults results = realm.where(Cat.class)
+ .beginsWith("name", "J")
+ .findAll();
+ status += "\nNumber of cats whose name begins with 'J': " + results.size();
+
+ realm.close();
+ return status;
+ }
+}
+
+```
+
+```java
+import android.content.Context;
+
+import com.mongodb.realm.examples.java.UnitTestActivity;
+import com.mongodb.realm.examples.model.java.Cat;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor;
+import org.powermock.modules.junit4.rule.PowerMockRule;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.Arrays;
+import java.util.List;
+
+import io.realm.Realm;
+import io.realm.RealmConfiguration;
+import io.realm.RealmObject;
+import io.realm.RealmQuery;
+import io.realm.RealmResults;
+import io.realm.internal.RealmCore;
+import io.realm.log.RealmLog;
+
+import com.mongodb.realm.examples.R;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.powermock.api.mockito.PowerMockito.doNothing;
+import static org.powermock.api.mockito.PowerMockito.mock;
+import static org.powermock.api.mockito.PowerMockito.mockStatic;
+import static org.powermock.api.mockito.PowerMockito.when;
+import static org.powermock.api.mockito.PowerMockito.whenNew;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = 28)
+@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "jdk.internal.reflect.*", "androidx.*"})
+@SuppressStaticInitializationFor("io.realm.internal.Util")
+@PrepareForTest({Realm.class, RealmConfiguration.class, RealmQuery.class, RealmResults.class, RealmCore.class, RealmLog.class})
+public class TestTest {
+ // bootstrap powermock
+ @Rule
+ public PowerMockRule rule = new PowerMockRule();
+
+ // mocked realm SDK components for tests
+ private Realm mockRealm;
+ private RealmResults cats;
+
+ @Before
+ public void setup() throws Exception {
+ // set up realm SDK components to be mocked. The order of these matters
+ mockStatic(RealmCore.class);
+ mockStatic(RealmLog.class);
+ mockStatic(Realm.class);
+ mockStatic(RealmConfiguration.class);
+ Realm.init(RuntimeEnvironment.application);
+ // boilerplate to mock realm components -- this prevents us from hitting any
+ // native code
+ doNothing().when(RealmCore.class);
+ RealmCore.loadLibrary(any(Context.class));
+
+ // create the mocked realm
+ final Realm mockRealm = mock(Realm.class);
+ final RealmConfiguration mockRealmConfig = mock(RealmConfiguration.class);
+ // use this mock realm config for all new realm configurations
+ whenNew(RealmConfiguration.class).withAnyArguments().thenReturn(mockRealmConfig);
+ // use this mock realm for all new default realms
+ when(Realm.getDefaultInstance()).thenReturn(mockRealm);
+
+ // any time we ask Realm to create a Cat, return a new instance.
+ when(mockRealm.createObject(Cat.class)).thenReturn(new Cat());
+
+ // set up test data
+ Cat p1 = new Cat();
+ p1.setName("Enoch");
+ Cat p2 = new Cat();
+ p2.setName("Quincy Endicott");
+ Cat p3 = new Cat();
+ p3.setName("Sara");
+ Cat p4 = new Cat();
+ p4.setName("Jimmy Brown");
+ List catList = Arrays.asList(p1, p2, p3, p4);
+
+ // create a mocked RealmQuery
+ RealmQuery catQuery = mockRealmQuery();
+ // when the RealmQuery performs findFirst, return the first record in the list.
+ when(catQuery.findFirst()).thenReturn(catList.get(0));
+ // when the where clause is called on the Realm, return the mock query.
+ when(mockRealm.where(Cat.class)).thenReturn(catQuery);
+ // when the RealmQuery is filtered on any string and any integer, return the query
+ when(catQuery.equalTo(anyString(), anyInt())).thenReturn(catQuery);
+ // when a between query is performed with any string as the field and any int as the
+ // value, then return the catQuery itself
+ when(catQuery.between(anyString(), anyInt(), anyInt())).thenReturn(catQuery);
+ // When a beginsWith clause is performed with any string field and any string value
+ // return the same cat query
+ when(catQuery.beginsWith(anyString(), anyString())).thenReturn(catQuery);
+
+ // RealmResults is final, must mock static and also place this in the PrepareForTest
+ // annotation array.
+ mockStatic(RealmResults.class);
+ // create a mock RealmResults
+ RealmResults cats = mockRealmResults();
+ // the for(...) loop in Java needs an iterator, so we're giving it one that has items,
+ // since the mock RealmResults does not provide an implementation. Therefore, any time
+ // anyone asks for the RealmResults Iterator, give them a functioning iterator from the
+ // ArrayList of Cats we created above. This will allow the loop to execute.
+ when(cats.iterator()).thenReturn(catList.iterator());
+ // Return the size of the mock list.
+ when(cats.size()).thenReturn(catList.size());
+
+ // when we ask Realm for all of the Cat instances, return the mock RealmResults
+ when(mockRealm.where(Cat.class).findAll()).thenReturn(cats);
+ // when we ask the RealmQuery for all of the Cat objects, return the mock RealmResults
+ when(catQuery.findAll()).thenReturn(cats);
+
+ this.mockRealm = mockRealm;
+ this.cats = cats;
+ }
+
+ @Test
+ public void shouldBeAbleToAccessActivityAndVerifyRealmInteractions() {
+ doCallRealMethod().when(mockRealm)
+ .executeTransaction(any(Realm.Transaction.class));
+
+ // create test activity -- onCreate method calls methods that
+ // query/write to realm
+ UnitTestActivity activity = Robolectric
+ .buildActivity(UnitTestActivity.class)
+ .create()
+ .start()
+ .resume()
+ .visible()
+ .get();
+
+ // click the clean up button
+ activity.findViewById(R.id.clean_up).performClick();
+
+ // verify that we queried for Cat instances five times in this run
+ // (2 in basicCrud(), 2 in complexQuery() and 1 in the button click)
+ verify(mockRealm, times(5)).where(Cat.class);
+
+ // verify that the delete method was called. We also call delete at
+ // the start of the activity to ensure we start with a clean db.
+ verify(mockRealm, times(2)).delete(Cat.class);
+
+ // call the destroy method so we can verify that the .close() method
+ // was called (below)
+ activity.onDestroy();
+
+ // verify that the realm got closed 2 separate times. Once in the
+ // AsyncTask, once in onDestroy
+ verify(mockRealm, times(2)).close();
+ }
+
+ @SuppressWarnings("unchecked")
+ private RealmQuery mockRealmQuery() {
+ return mock(RealmQuery.class);
+ }
+
+ @SuppressWarnings("unchecked")
+ private RealmResults mockRealmResults() {
+ return mock(RealmResults.class);
+ }
+}
+
+```
+
+#### Kotlin
+
+```kotlin
+package com.mongodb.realm.examples.kotlin
+
+import android.os.AsyncTask
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import com.mongodb.realm.examples.R
+import com.mongodb.realm.examples.model.java.Cat
+import io.realm.Realm
+
+class UnitTestActivity : AppCompatActivity() {
+ private var rootLayout: LinearLayout? = null
+ private var realm: Realm? = null
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ Realm.init(applicationContext)
+ setContentView(R.layout.activity_unit_test)
+ rootLayout = findViewById(R.id.container)
+ rootLayout!!.removeAllViews()
+
+ // open the default Realm for the UI thread.
+ realm = Realm.getDefaultInstance()
+
+ // clean up from previous run
+ cleanUp()
+
+ // small operation that is ok to run on the main thread
+ basicCRUD(realm)
+
+ // more complex operations can be executed on another thread.
+ val foo: AsyncTask = object : AsyncTask() {
+ protected override fun doInBackground(vararg params: Void?): String? {
+ var info = ""
+ info += complexQuery()
+ return info
+ }
+
+ override fun onPostExecute(result: String) {
+ showStatus(result)
+ }
+ }
+ foo.execute()
+ findViewById(R.id.clean_up).setOnClickListener { view: View ->
+ view.isEnabled = false
+ Log.d("TAG", "clean up")
+ cleanUp()
+ view.isEnabled = true
+ }
+ }
+
+ private fun cleanUp() {
+ // delete all cats
+ realm!!.executeTransaction { r: Realm -> r.delete(Cat::class.java) }
+ }
+
+ public override fun onDestroy() {
+ super.onDestroy()
+ realm!!.close() // remember to close realm when done.
+ }
+
+ private fun showStatus(txt: String) {
+ Log.i(TAG, txt)
+ val tv = TextView(this)
+ tv.text = txt
+ rootLayout!!.addView(tv)
+ }
+
+ private fun basicCRUD(realm: Realm?) {
+ showStatus("Perform basic Create/Read/Update/Delete (CRUD) operations...")
+
+ // all writes must be wrapped in a transaction to facilitate safe multi threading
+ realm!!.executeTransaction { r: Realm ->
+ // add a cat
+ val cat = r.createObject(Cat::class.java)
+ cat.name = "John Young"
+ }
+
+ // find the first cat (no query conditions) and read a field
+ val cat = realm.where(Cat::class.java).findFirst()
+ showStatus(cat!!.name)
+
+ // update cat in a transaction
+ realm.executeTransaction { r: Realm? ->
+ cat.name = "John Senior"
+ }
+ showStatus(cat.name)
+
+ // add two more cats
+ realm.executeTransaction { r: Realm ->
+ val jane = r.createObject(Cat::class.java)
+ jane.name = "Jane"
+ val doug = r.createObject(Cat::class.java)
+ doug.name = "Robert"
+ }
+ val cats = realm.where(Cat::class.java).findAll()
+ showStatus(String.format("Found %s cats", cats.size))
+ for (p in cats) {
+ showStatus("Found " + p.name)
+ }
+ }
+
+ private fun complexQuery(): String {
+ var status = "\n\nPerforming complex Query operation..."
+ val realm = Realm.getDefaultInstance()
+ status += """
+
+ Number of cats in the DB: ${realm.where(Cat::class.java).count()}
+ """.trimIndent()
+
+ // find all cats where name begins with "J".
+ val results = realm.where(Cat::class.java)
+ .beginsWith("name", "J")
+ .findAll()
+ status += """
+
+ Number of cats whose name begins with 'J': ${results.size}
+ """.trimIndent()
+ realm.close()
+ return status
+ }
+
+ companion object {
+ val TAG = UnitTestActivity::class.java.name
+ }
+}
+
+```
+
+```kotlin
+import android.content.Context
+import android.view.View
+import com.mongodb.realm.examples.R
+import com.mongodb.realm.examples.kotlin.UnitTestActivity
+import com.mongodb.realm.examples.model.java.Cat
+import io.realm.Realm
+import io.realm.RealmConfiguration
+import io.realm.RealmObject
+import io.realm.RealmQuery
+import io.realm.RealmResults
+import io.realm.internal.RealmCore
+import io.realm.log.RealmLog
+import java.lang.Exception
+import java.util.*
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.Mockito
+import org.powermock.api.mockito.PowerMockito
+import org.powermock.core.classloader.annotations.PowerMockIgnore
+import org.powermock.core.classloader.annotations.PrepareForTest
+import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor
+import org.powermock.modules.junit4.rule.PowerMockRule
+import org.robolectric.Robolectric
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.RuntimeEnvironment
+import org.robolectric.annotation.Config
+
+@RunWith(RobolectricTestRunner::class)
+@Config(sdk = [28])
+@PowerMockIgnore(
+ "org.mockito.*",
+ "org.robolectric.*",
+ "android.*",
+ "jdk.internal.reflect.*",
+ "androidx.*"
+)
+@SuppressStaticInitializationFor("io.realm.internal.Util")
+@PrepareForTest(
+ Realm::class,
+ RealmConfiguration::class,
+ RealmQuery::class,
+ RealmResults::class,
+ RealmCore::class,
+ RealmLog::class
+)
+class TestTest {
+ // bootstrap powermock
+ @Rule
+ var rule = PowerMockRule()
+
+ // mocked realm SDK components for tests
+ private var mockRealm: Realm? = null
+ private var cats: RealmResults? = null
+ @Before
+ @Throws(Exception::class)
+ fun setup() {
+ // set up realm SDK components to be mocked. The order of these matters
+ PowerMockito.mockStatic(RealmCore::class.java)
+ PowerMockito.mockStatic(RealmLog::class.java)
+ PowerMockito.mockStatic(Realm::class.java)
+ PowerMockito.mockStatic(RealmConfiguration::class.java)
+ Realm.init(RuntimeEnvironment.application)
+ PowerMockito.doNothing().`when`(RealmCore::class.java)
+ RealmCore.loadLibrary(ArgumentMatchers.any(Context::class.java))
+
+ // create the mocked realm
+ val mockRealm = PowerMockito.mock(Realm::class.java)
+ val mockRealmConfig = PowerMockito.mock(
+ RealmConfiguration::class.java
+ )
+ // use this mock realm config for all new realm configurations
+ PowerMockito.whenNew(RealmConfiguration::class.java).withAnyArguments()
+ .thenReturn(mockRealmConfig)
+ // use this mock realm for all new default realms
+ PowerMockito.`when`(Realm.getDefaultInstance()).thenReturn(mockRealm)
+
+ // any time we ask Realm to create a Cat, return a new instance.
+ PowerMockito.`when`(mockRealm.createObject(Cat::class.java)).thenReturn(Cat())
+
+ // set up test data
+ val p1 = Cat()
+ p1.name = "Enoch"
+ val p2 = Cat()
+ p2.name = "Quincy Endicott"
+ val p3 = Cat()
+ p3.name = "Sara"
+ val p4 = Cat()
+ p4.name = "Jimmy Brown"
+ val catList = Arrays.asList(p1, p2, p3, p4)
+
+ // create a mocked RealmQuery
+ val catQuery = mockRealmQuery()
+ // when the RealmQuery performs findFirst, return the first record in the list.
+ PowerMockito.`when`(catQuery!!.findFirst()).thenReturn(catList[0])
+ // when the where clause is called on the Realm, return the mock query.
+ PowerMockito.`when`(mockRealm.where(Cat::class.java)).thenReturn(catQuery)
+ // when the RealmQuery is filtered on any string and any integer, return the query
+ PowerMockito.`when`(
+ catQuery.equalTo(
+ ArgumentMatchers.anyString(),
+ ArgumentMatchers.anyInt()
+ )
+ ).thenReturn(catQuery)
+ // when a between query is performed with any string as the field and any int as the
+ // value, then return the catQuery itself
+ PowerMockito.`when`(
+ catQuery.between(
+ ArgumentMatchers.anyString(),
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.anyInt()
+ )
+ ).thenReturn(catQuery)
+ // When a beginsWith clause is performed with any string field and any string value
+ // return the same cat query
+ PowerMockito.`when`(
+ catQuery.beginsWith(
+ ArgumentMatchers.anyString(),
+ ArgumentMatchers.anyString()
+ )
+ ).thenReturn(catQuery)
+
+ // RealmResults is final, must mock static and also place this in the PrepareForTest
+ // annotation array.
+ PowerMockito.mockStatic(RealmResults::class.java)
+ // create a mock RealmResults
+ val cats = mockRealmResults()
+ // the for(...) loop in Java needs an iterator, so we're giving it one that has items,
+ // since the mock RealmResults does not provide an implementation. Therefore, any time
+ // anyone asks for the RealmResults Iterator, give them a functioning iterator from the
+ // ArrayList of Cats we created above. This will allow the loop to execute.
+ PowerMockito.`when`>(cats!!.iterator()).thenReturn(catList.iterator())
+ // Return the size of the mock list.
+ PowerMockito.`when`(cats.size).thenReturn(catList.size)
+
+ // when we ask Realm for all of the Cat instances, return the mock RealmResults
+ PowerMockito.`when`(mockRealm.where(Cat::class.java).findAll()).thenReturn(cats)
+ // when we ask the RealmQuery for all of the Cat objects, return the mock RealmResults
+ PowerMockito.`when`(catQuery.findAll()).thenReturn(cats)
+ this.mockRealm = mockRealm
+ this.cats = cats
+ }
+
+ @Test
+ fun shouldBeAbleToAccessActivityAndVerifyRealmInteractions() {
+ Mockito.doCallRealMethod().`when`(mockRealm)!!
+ .executeTransaction(ArgumentMatchers.any(Realm.Transaction::class.java))
+
+ // create test activity -- onCreate method calls methods that
+ // query/write to realm
+ val activity = Robolectric
+ .buildActivity(UnitTestActivity::class.java)
+ .create()
+ .start()
+ .resume()
+ .visible()
+ .get()
+
+ // click the clean up button
+ activity.findViewById(R.id.clean_up).performClick()
+
+ // verify that we queried for Cat instances five times in this run
+ // (2 in basicCrud(), 2 in complexQuery() and 1 in the button click)
+ Mockito.verify(mockRealm, Mockito.times(5))!!.where(Cat::class.java)
+
+ // verify that the delete method was called. We also call delete at
+ // the start of the activity to ensure we start with a clean db.
+ Mockito.verify(mockRealm, Mockito.times(2))!!.delete(Cat::class.java)
+
+ // call the destroy method so we can verify that the .close() method
+ // was called (below)
+ activity.onDestroy()
+
+ // verify that the realm got closed 2 separate times. Once in the
+ // AsyncTask, once in onDestroy
+ Mockito.verify(mockRealm, Mockito.times(2))!!.close()
+ }
+
+ private fun mockRealmQuery(): RealmQuery? {
+ @Suppress("UNCHECKED_CAST")
+ return PowerMockito.mock(RealmQuery::class.java) as RealmQuery
+ }
+
+ private fun mockRealmResults(): RealmResults? {
+ @Suppress("UNCHECKED_CAST")
+ return PowerMockito.mock(RealmResults::class.java) as RealmResults
+ }
+}
+
+```
+
+> Seealso:
+> See the [Unit Testing Example App](https://github.com/realm/realm-java/tree/master/examples/unitTestExample)
+for an example of unit testing an application that uses
+Realm.
+>
diff --git a/docs/guides/test-and-debug/troubleshooting.md b/docs/guides/test-and-debug/troubleshooting.md
new file mode 100644
index 0000000000..6c51c84f9a
--- /dev/null
+++ b/docs/guides/test-and-debug/troubleshooting.md
@@ -0,0 +1,199 @@
+# Troubleshooting - Java SDK
+## Couldn't load "librealm-jni.so"
+If your app uses native libraries that don't ship with support for
+64-bit architectures, Android will fail to load Realm's
+`librealm-jni.so` file on ARM64 devices. This happens because Android
+cannot load 32-bit and 64-bit native libraries concurrently. Ideally,
+all libraries could provide the same set of supported ABIs, but
+sometimes that may not be doable when using a 3rd-party library.
+
+To work around this issue, you can exclude Realm's ARM64 library from
+the APK file by adding the following code to the application's
+`build.gradle`. You can refer to Mixing 32- and 64-bit Dependencies in Android for more information.
+
+```gradle
+android {
+ //...
+ packagingOptions {
+ exclude "lib/arm64-v8a/librealm-jni.so"
+ }
+ //...
+}
+```
+
+> Seealso:
+> For more information, see [Mixing 32- and 64-bit Dependencies in Android](https://corbt.com/posts/2015/09/18/mixing-32-and-64bit-dependencies-in-android.html).
+>
+
+## Network Calls to Mixpanel
+Realm collects anonymous analytics when you run the
+Realm bytecode transformer on your source code. This is
+completely anonymous and helps us improve the product by flagging:
+
+- which version of the SDK you use
+- which operating system you use
+- if your application uses Kotlin
+
+Analytics do not run when your application runs on user devices - only
+when you compile your source code. To opt out of analytics, you can set
+the `REALM_DISABLE_ANALYTICS` environment variable to `true`.
+
+## Change Listeners in Android 12 with SDK Versions Below 10.5.1
+Due to a change in the Linux kernel,
+object, collection, and realm notifications do not work in SDK versions below
+10.5.1 on devices running certain early versions of
+Android 12.
+
+This change effects Linux kernel versions beginning with `5.5`.
+Linux kernel version `5.14-rc4` fixed the issue. The fix was
+also backported to `LTS 5.10.56` and `LTS 5.13.8`. All mainline
+and LTS Android 12 branches contain the fix or a backport of it.
+
+If you experience this issue, you can restore notification functionality
+with the following fixes:
+
+- upgrade to a version of the SDK later than 10.5.1.
+- upgrade to a version of Android 12 that uses a Linux kernel release
+that contains the fix (kernel commit `3a34b13a88caeb2800ab44a4918f230041b37dd9`)
+or the backport of the fix (kernel commit `4b20d2de0b367bca627b49efd8d2e9e01bb66753`).
+
+## Configurations Cannot be Different if Used to Open the Same File
+Realm runs checks whenever you open a realm file to
+avoid corruption. In order to avoid accidentally opening a realm
+file with incompatible settings, the SDK uses Java's `equals()` method
+to compare `RealmConfiguration` objects. This prevents the SDK from
+opening a single realm file with different schemas, durability levels,
+or writability settings. However, configurations that include lambda
+functions, such as those passed to
+`initialData()`
+and
+`compactOnLaunch()`,
+can break this `equals()` comparison, since two different lambdas are
+never considered equal using Java's built-in comparison.
+To avoid this error when using lambdas, you can either:
+
+1. Store a single configuration statically in your application, so that
+separate realm instances use the exact same
+`RealmConfiguration` object and it passes the check.
+2. Override the default equals check of the `RealmConfiguration`: `val config = RealmConfiguration.Builder()
+ .initialData(object: Realm.Transaction {
+ override fun execute(realm: Realm) {
+ TODO("Not yet implemented")
+ }
+
+ override fun equals(other: Any?): Boolean {
+ return true
+ }
+
+ override fun hashCode(): Int {
+ return 37
+ }
+ }).build()`
+
+## Kapt Exceptions During Builds
+If you experience an exception in the Kapt library with a description
+like the following:
+
+```
+A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptWithoutKotlincTask$KaptExecutionWorkAction
+```
+
+This most likely means there is an issue with one of your model classes.
+Possible causes include:
+
+- introducing a field type that is not supported by the SDK
+- using a visibility type other than `open` or `public` for a realm object model class
+- using a Realm annotation on an incompatible field
+
+If you experience this error, check any recent updates to your schema for
+problems.
+
+## Installation Size
+Once your app is built for release and split for distribution, the SDK
+should only add about 800KB to your APK in most cases. The releases are
+significantly larger because they include support for more architectures,
+such as ARM7, ARMv7, ARM64, x86, and MIPS. The APK file contains all
+supported architectures, but the Android installer only installs native
+code for the device's architecture. This means that the installed app
+is smaller than the size of the APK file.
+
+You can reduce the size of the Android APK itself by splitting the APK
+into a version for each architecture. Use the Android Build Tool ABI
+Split support by adding the following to your build.gradle:
+
+```gradle
+android {
+ splits {
+ abi {
+ enable true
+ reset()
+ include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
+ }
+ }
+}
+```
+
+Select the architectures that you'd like to include to build a separate
+APK for each.
+
+> Seealso:
+> See the [Android Tools documentation about ABI Splits](https://developer.android.com/studio/build/configure-apk-splits.html)
+for more information, or the [example on GitHub](https://github.com/realm/realm-java/tree/master/examples/gridViewExample).
+>
+
+If you don't want to handle multiple APKs, you can restrict the number
+of architectures supported in a single APK. This is done by adding
+`abiFilters` to your build.gradle:
+
+```gradle
+android {
+ defaultConfig {
+ ndk {
+ abiFilters 'armeabi-v7a', 'arm64-v8a', 'mips', 'x86', 'x86_64'
+ }
+ }
+}
+```
+
+> Seealso:
+> [Controlling APK Size When Using Native Libraries](https://medium.com/android-news/controlling-apk-size-when-using-native-libraries-45c6c0e5b70a).
+>
+
+## Customize Dependencies Defined by the Realm Gradle Plugin
+Realm uses a Gradle plugin because it makes it easier to set
+up a large number of dependencies. Unfortunately this also makes it a
+bit harder to ignore specific transitive dependencies.
+
+If you want to customize Realm beyond what is exposed by the
+plugin, you can manually set up all the dependencies and ignore the
+Gradle plugin. The following example demonstrates how to set up the SDK
+for an Android application using Kotlin manually:
+
+```gradle
+buildscript {
+ ext.kotlin_version = '1.5.21'
+ ext.realm_version = '10.18.0'
+ repositories {
+ jcenter()
+ mavenCentral()
+ }
+ dependencies {
+ classpath "io.realm:realm-transformer:$realm_version"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-kapt'
+
+import io.realm.transformer.RealmTransformer
+android.registerTransform(new RealmTransformer(project))
+
+dependencies {
+ api "io.realm:realm-annotations:$realm_version"
+ api "io.realm:realm-android-library:$realm_version"
+ api "io.realm:realm-android-kotlin-extensions:$realm_version"
+ kapt "io.realm:realm-annotations-processor:$realm_version"
+}
+```
diff --git a/docs/guides/troubleshooting.md b/docs/guides/troubleshooting.md
new file mode 100644
index 0000000000..602b53840d
--- /dev/null
+++ b/docs/guides/troubleshooting.md
@@ -0,0 +1,30 @@
+# Troubleshooting - Java SDK
+## Use in System Apps on Custom Android ROMs
+Realm SDKs use named pipes to support notifications and access to
+the Realm file from multiple processes. While this is allowed by
+default for normal user apps, it is disallowed for system apps.
+
+System apps are defined by setting `android:sharedUserId="android.uid.system"`
+in the Android manifest. For system apps, you may see a security violation in
+Logcat that looks something like this:
+
+```bash
+05-24 14:08:08.984 6921 6921 W .realmsystemapp: type=1400 audit(0.0:99): avc: denied { write } for name="realm.testapp.com.realmsystemapp-Bfqpnjj4mUvxWtfMcOXBCA==" dev="vdc" ino=14660 scontext=u:r:system_app:s0 tcontext=u:object_r:apk_data_file:s0 tclass=dir permissive=0
+05-24 14:08:08.984 6921 6921 W .realmsystemapp: type=1400 audit(0.0:100): avc: denied { write } for name="realm.testapp.com.realmsystemapp-Bfqpnjj4mUvxWtfMcOXBCA==" dev="vdc" ino=14660 scontext=u:r:system_app:s0 tcontext=u:object_r:apk_data_file:s0 tclass=dir permissive=0
+```
+
+To fix this, you need to adjust the SELinux security rules in the ROM. This can
+be done by using the tool `audit2allow`. This tool ships as part of
+[AOSP](https://source.android.com/).
+
+1. Pull the current policy from the device: `adb pull /sys/fs/selinux/policy`.
+2. Copy the SELinux error inside a text file called `input.txt`.
+3. Run the `audit2allow` tool: `audit2allow -p policy -i input.txt`.
+4. The tool should output a rule you can add to your existing policy.
+The rule allows you to access the Realm file from multiple processes.
+
+`audit2allow` is produced when compiling AOSP/ROM and only runs on
+Linux. Check out the details in the [Android Source documentation](https://source.android.com/security/selinux/validate#using_audit2allow).
+Also note that since Android Oreo, Google changed the way it configures
+SELinux and the default security policies are now more modularized. More details
+are in the [Android Source documentation](https://source.android.com/security/selinux/images/SELinux_Treble.pdf).