Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(DOCSP-15613): Node.js embedded objects type #1047

126 changes: 125 additions & 1 deletion examples/node/Examples/data-types.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,128 @@
import Realm from "realm";
import BSON from "bson";

// :code-block-start: define-embedded-objects
const AddressSchema = {
name: "Address",
embedded: true, // default: false
properties: {
street: "string?",
city: "string?",
country: "string?",
postalCode: "string?",
},
};

const ContactSchema = {
name: "Contact",
primaryKey: "_id",
properties: {
_id: "objectId",
name: "string",
address: "Address", // Embed a single object
},
};

const BusinessSchema = {
name: "Business",
primaryKey: "_id",
properties: {
_id: "objectId",
name: "string",
addresses: { type: "list", objectType: "Address" }, // Embed an array of objects
},
};
// :code-block-end:
describe("Node.js Data Types", () => {
})
test("should create and read and delete an embedded object", async () => {
const realm = await Realm.open({
schema: [AddressSchema, ContactSchema],
});

// :code-block-start: create-an-embedded-object
// create an embedded address object
const sydneyOrthodontics = {
street: "42 Wallaby Way",
city: "Sydney",
country: "Australia",
postalCode: "2774",
};
realm.write(() => {
// create a contact object
realm.create("Contact", {
_id: new BSON.ObjectId(),
name: "Philip Sherman",
address: sydneyOrthodontics, // embed the address in the contact object
});
});
// :code-block-end:

// :code-block-start: query-an-embedded-object
const philipShermanAddress = realm
.objects("Contact")
.filtered("name = 'Philip Sherman'")[0].address.street;
console.log(`Philip Sherman's address is ${philipShermanAddress}`);
// :code-block-end:
expect(philipShermanAddress).toBe("42 Wallaby Way"); // this assertion tests both the 'query-an-embedded-object' and 'create-an-embedded-object' code blocks

// // :code-block-start: delete-an-embedded-object
realm.write(() => {
// Deleting the contact will delete the embedded address of that contact
realm.delete(
realm.objects("Contact").filtered("name = 'Philip Sherman'")
);
});
// :code-block-end:

// close the realm
realm.close();
});
// update and delete an embedded object
test("should update and overwrite an embedded object", async () => {
const realm = await Realm.open({
schema: [AddressSchema, ContactSchema],
});
const harryAddress = {
street: "4 Privet Drive",
city: "Little Whinging, Surrey",
country: "UK",
postalCode: "WD4 8PN",
};
realm.write(() => {
realm.create("Contact", {
_id: new BSON.ObjectId(),
name: "Harry Potter",
address: harryAddress,
});
});

// :code-block-start: update-an-embedded-object
// Find the contact with the address you want to update
const harryPotter = realm
.objects("Contact")
.filtered("name = 'Harry Potter'")[0];
// modify the property of the embedded object in a write transaction
realm.write(() => {
// update the embedded object directly through the contact
harryPotter.address.street = "1 Hogwarts Ave";
});
// :code-block-end:
expect(harryPotter.address.street).toBe("1 Hogwarts Ave");

// :code-block-start: overwrite-an-embedded-object
// create a new address
const harryNewAddress = {
street: "12 Grimmauld Place",
city: "London",
country: "UK",
postalCode: "E1 7AA",
};
realm.write(() => {
// overwrite the embedded object with the new address within a write transaction
harryPotter.address = harryNewAddress;
});
// :code-block-end:

expect(harryPotter.address.city).toBe("London");
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// create an embedded address object
const sydneyOrthodontics = {
street: "42 Wallaby Way",
city: "Sydney",
country: "Australia",
postalCode: "2774",
};
realm.write(() => {
// create a contact object
realm.create("Contact", {
_id: new BSON.ObjectId(),
name: "Philip Sherman",
address: sydneyOrthodontics, // embed the address in the contact object
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const AddressSchema = {
name: "Address",
embedded: true, // default: false
properties: {
street: "string?",
city: "string?",
country: "string?",
postalCode: "string?",
},
};

const ContactSchema = {
name: "Contact",
primaryKey: "_id",
properties: {
_id: "objectId",
name: "string",
address: "Address", // Embed a single object
},
};

const BusinessSchema = {
name: "Business",
primaryKey: "_id",
properties: {
_id: "objectId",
name: "string",
addresses: { type: "list", objectType: "Address" }, // Embed an array of objects
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
realm.write(() => {
// Deleting the contact will delete the embedded address of that contact
realm.delete(
realm.objects("Contact").filtered("name = 'Philip Sherman'")
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// create a new address
const harryNewAddress = {
street: "12 Grimmauld Place",
city: "London",
country: "UK",
postalCode: "E1 7AA",
};
realm.write(() => {
// overwrite the embedded object with the new address within a write transaction
harryPotter.address = harryNewAddress;
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const philipShermanAddress = realm
.objects("Contact")
.filtered("name = 'Philip Sherman'")[0].address.street;
console.log(`Philip Sherman's address is ${philipShermanAddress}`);
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Find the contact with the address you want to update
const harryPotter = realm
.objects("Contact")
.filtered("name = 'Harry Potter'")[0];
// modify the property of the embedded object in a write transaction
realm.write(() => {
// update the embedded object directly through the contact
harryPotter.address.street = "1 Hogwarts Ave";
});
107 changes: 107 additions & 0 deletions source/sdk/node/data-types/embedded-objects.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,110 @@ Embedded Objects - Node.js SDK
:backlinks: none
:depth: 2
:class: singlecol

Overview
--------

An embedded object is a special type of :ref:`Realm object <node-object-schemas>`
that models complex data about a specific object. Embedded objects are similar
to :ref:`relationships <node-client-relationships>`, but they provide additional
constraints and map more naturally to the denormalized :manual:`MongoDB document
model </core/data-modeling-introduction/>`.

Realm enforces unique ownership constraints that treat each embedded object as
nested data inside a single, specific parent object. An embedded object
inherits the lifecycle of its parent object and cannot exist as an independent
Realm object. This means that embedded objects cannot have a primary key and
that Realm automatically deletes embedded objects if their parent object is
deleted.

.. tip:: Embedded object types are reusable and composable

You can use the same embedded object type in multiple parent object types and
mohammadhunan-dev marked this conversation as resolved.
Show resolved Hide resolved
you can embed objects inside of other embedded objects. You can even
mohammadhunan-dev marked this conversation as resolved.
Show resolved Hide resolved
recursively reference an embedded object type as an optional property in its
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm kind of fascinated by the idea of recursively referencing an embedded object type as an optional property in its own definition, but am having trouble visualizing a use case for it. I wonder if this might be a good example to add to the page?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might look something like:

const AddressSchema = {
  name: "Address",
  embedded: true, // default: false
  properties: {
    street: "string?",
    city: "string?",
    country: "string?",
    postalCode: "string?",
    address: Address?
  },
};

Though that tip is copied over from the existing docs, and is a scenario we probably don't want to actually show in our docs since it's so unique that I can't think of a practical use-case. The tip does show the sheer flexibility of embedded objects though.

own definition.

.. note:: Realm Uses Cascading Deletes for Embedded Objects

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 :ref:`relationships <node-client-relationships>`
instead.

Realm Object Models
~~~~~~~~~~~~~~~~~~~

To specify that a Realm object model define an embedded object, set ``embedded``
mohammadhunan-dev marked this conversation as resolved.
Show resolved Hide resolved
to ``true``. You can reference an embedded object type from parent object types
in the same way as you would define a relationship:

.. important::

Embedded objects cannot have a :ref:`primary key <node-primary-keys>`.

.. literalinclude:: /examples/generated/node/data-types.codeblock.define-embedded-objects.js
:language: javascript
:emphasize-lines: 3, 18, 28


JSON Schema
~~~~~~~~~~~

.. include:: /includes/embedded-object-json-schema.rst


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:

.. literalinclude:: /examples/generated/node/data-types.codeblock.create-an-embedded-object.js
:language: javascript

Update an Embedded Object Property
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

To update a property in an embedded object, modify the property in a
write transaction:

.. literalinclude:: /examples/generated/node/data-types.codeblock.update-an-embedded-object.js
:language: javascript


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:

.. literalinclude:: /examples/generated/node/data-types.codeblock.overwrite-an-embedded-object.js
:language: javascript


Query a Collection on Embedded Object Properties
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Use dot notation to filter or sort a :ref:`collection
<node-results-collections>` of objects based on an embedded object
property value:

.. include:: /includes/directly-query-embedded-objects-note.rst

.. literalinclude:: /examples/generated/node/data-types.codeblock.query-an-embedded-object.js
:language: javascript



Delete an Embedded Object
~~~~~~~~~~~~~~~~~~~~~~~~~
Realm Uses Cascading Deletes for Embedded Objects. To delete an embedded object,
delete the embedded objects parent.
mohammadhunan-dev marked this conversation as resolved.
Show resolved Hide resolved

.. literalinclude:: /examples/generated/node/data-types.codeblock.delete-an-embedded-object.js
:language: javascript
Original file line number Diff line number Diff line change
Expand Up @@ -175,62 +175,6 @@ to :ref:`relationships <node-client-relationships>`, but they provide additional
constraints and map more naturally to the denormalized :manual:`MongoDB document
model </core/data-modeling-introduction/>`.

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. This means that embedded objects cannot have a primary key and
that Realm automatically deletes embedded objects if their parent object is
deleted.

.. tip:: 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. You can even
recursively reference an embedded object type as an optional property in its
own definition.

Realm Object Models
~~~~~~~~~~~~~~~~~~~

To specify that a Realm object model define an embedded object, set ``embedded``
to ``true``. You can reference an embedded object type from parent object types
in the same way as you would define a relationship:

.. code-block:: javascript
:emphasize-lines: 3, 18, 28

const AddressSchema = {
name: "Address",
embedded: true, // default: false
properties: {
street: "string?",
city: "string?",
country: "string?",
postalCode: "string?",
},
};

const ContactSchema = {
name: "Contact",
primaryKey: "_id",
properties: {
_id: "objectId",
name: "string",
address: "Address", // Embed a single object
},
};

const BusinessSchema = {
name: "Business",
primaryKey: "_id",
properties: {
_id: "objectId",
name: "string",
addresses: { type: "list", objectType: "Address" }, // Embed an array of objects
},
};

JSON Schema
~~~~~~~~~~~

.. include:: /includes/embedded-object-json-schema.rst
Learn more about :doc:`Embedded objects
</sdk/node/data-types/embedded-objects>`, including how to read and write
embedded objects.