Embedded Link / Document

I have been watching some MongoDB data modelling videos, as part of their MongoDB.live 2020 virtual event and from their University.

Is an “embedded document” the same as defining a Parse class with an Array field? As intermediate RDBMS guy, it looks like a link?

If not, how do you create embedded documents? Is that just an array of standard data types, like String and Int?

Using Parse Dashboard, this is the output from the iOS SDK with the “Linked” field as an Array, to which a PFObject is an element:

[
  {
    "__type": "Pointer",
    "className": "Test1",
    "objectId": "zPgoFhDeKA"
  }
]

You can easily define embedded documents in Parse Server by adding Array or Object fields to a class. You can just push whatever data you want to those fields and this data will be actually embedded (not only a link) in the outer object.

Thanks @davimacedo. I’m just trying to clarify the definition.

Do you mean the type of field I defined above is embedded data? To me, it just seems like a single field that happens to be of an array type, that I am filling with Object IDs (OID’s). Is it that simple?

If I populated an array of Strings, that contained the objectId value (I would have to do some form of lookup / query against it) but would that also count as embedding? Like:

[
“objectId”: “zPgoFhDeKA”,
“objectId”: “someString”
]

Is Parse any different in how it works compared to native MongoDB?

I am confused…

this is an example of the material in the data modelling session I was watching:

Let’s say you have two different entities in your app, Parent and Child, in which each Parent instance can be associated to multiple Child instances, and each Child instance can be associated to a single Parent instance, aka one-to-many relationship. There are many different ways to model your backend in Parse Server, including:

  • You can create a single class called Parent, with a field called children of type array in which you will store each Child instance as an embeded document inside the Parent instance. Your data would be:
// Parent Class Data
{
  "objectId": "ParentId1",
  "name": "Parent 1",
  "children": [
    { "name": "Child 1.1" },
    { "name": "Child 1.2" }
  ]
}

{
  "objectId": "ParentId2",
  "name": "Parent 2",
  "children": [
    { "name": "Child 2.1" },
    { "name": "Child 2.2" }
  ]
}
  • You can create a single class called Child, with a field called parent of type object in which you will store each Parent instance as an embeded document inside the Child instance. Your data would be:
// Child Class Data
{
  "objectId": "ChildId11",
  "name": "Child 1.1",
  "parent": {
    "name": "Parent 1"
  }
}

{
  "objectId": "ChildId12",
  "name": "Child 1.2",
  "parent": {
    "name": "Parent 1"
  }
}

{
  "objectId": "ChildId21",
  "name": "Child 2.1",
  "parent": {
    "name": "Parent 2"
  }
}

{
  "objectId": "ChildId22",
  "name": "Child 2.2",
  "parent": {
    "name": "Parent 2"
  }
}
  • You can create two different classes, Parent and Child, and create a pointer called parent in the class Child with target class set to Parent. In this case, you do not have an embeded document, since the information of each Parent and Child instances are stored in their own classes. Your data would be:
// Parent Class Data
{
  "objectId": "ParentId1",
  "name": "Parent 1"
}

{
  "objectId": "ParentId2",
  "name": "Parent 2"
}

// Child Class Data
{
  "objectId": "ChildId11",
  "name": "Child 1.1",
  "parent": {
    "__type": "Pointer",
    "className": "Parent",
   "objectId": "ParentId1"
  }
}

{
  "objectId": "ChildId12",
  "name": "Child 1.2",
  "parent": {
    "__type": "Pointer",
    "className": "Parent",
   "objectId": "ParentId1"
  }
}

{
  "objectId": "ChildId21",
  "name": "Child 2.1",
  "parent": {
    "__type": "Pointer",
    "className": "Parent",
    "objectId": "ParentId2"
  }
}

{
  "objectId": "ChildId22",
  "name": "Child 2.2",
  "parent": {
    "__type": "Pointer",
    "className": "Parent",
    "objectId": "ParentId2"
  }
}

There is no right way to go. You need to choose or item combine any of these options (and actually there are different ways to model the backend) according to your app needs, and mainly the queries that you will need to perform later.

Thanks again. The more I see, I understand a little more… So it is an Array field type, essentially. But it does not contain Pointers as the data type. My example was a link, NOT an embedded document.

I was wondering if Parse approached it differently, but as I see in your examples above it does not.

The hardest part I have, with first forays into NoSQL (from RDBMS) is not to normalise. Then add these different “relation” types.

Normalize or not depend on your app needs. You need to first think about your app features/queries and then choose the model that you believe will better fit. I’d also not spend too much time thinking on that. Choose one and give it a try. It is the best way to learn.

I am trying to achieve similar with Parse-Swift SDK. I was going through the playgrounds and documentation and have not found how exactly I should save an array of embedded objects without linking fo that it gets fetched.

let say I have following prompt struct:

struct Prompt {
    
    var type: Int
    var header: Int
    var text: String
    
    init(type: Int, header: Int, text: String) {
        self.type = type
        self.header = header
        self.text = text
    }
    
}

and I would like to save in database a profile object that has array of these objects as directly embedded documents:

struct PrsProfile: ParseObject {
    
    //: Those are required for Parse Object
    var objectId: String?
    var createdAt: Date?
    var updatedAt: Date?
    var ACL: ParseACL?

    //: Custom properties
    var p: [Prompt]? //array of eventual prompts

}

Do I need to code the embedded document into a JSON format string, or is this handled by SDK/Parse?

I believe this would be very similar to iOS SDK, but looking in to documentation I am not sure if Prompt object should conform to ParseObject, or it can be any struct.

If you want Prompt as an additional class/table in Parse, then you should make it conform to ParseObject (if you take this route, you will need to make type, header, and text optional properties). This will end up creating pointers to the Prompt objects.

If you don’t want Prompt as a class/table, you will most likely need to make Prompt conform to Codable in order for it to be embedded in your ParseObject:

struct Prompt: Codable {
    
    var type: Int
    var header: Int
    var text: String
    
    init(type: Int, header: Int, text: String) {
        self.type = type
        self.header = header
        self.text = text
    }
    
}
1 Like