Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Data MongoDB - Embedded Document as Reference in Other Document

I'd like to know if it's possible (or even correct) to use embedded documents as reference in other documents.

I know I can move the embedded document to its own collection but the main goal is to have the performance benefit of embedded document and also avoid duplication.

For example:

User

{
    _id: ObjectId("4fed0591d17011868cf9c982"),
    _class: "User"
    ...
    addresses: [ {
        _id: ObjectId("87KJbk87gjgjjygREewakj86"),
        _class: "Address",
        ...
    } ]
}

Order

{
    _id: ObjectId("gdh60591d123487658cf9c982"),
    _class: "Order",
    ...
    address: ObjectId("87KJbk87gjgjjygREewakj86")
}
like image 657
Anderson Fernandes Silva Avatar asked Jul 01 '16 19:07

Anderson Fernandes Silva


People also ask

What should we embed one document within another in MongoDB?

An embedded, or nested, MongoDB Document is a normal document that's nested inside another document within a MongoDB collection. Embedded documents are particularly useful when a one-to-many relationship exists between documents.

Can a document in MongoDB contain another document?

A document in MongoDB can have fields that hold another document. It is also called nested documents.

How do I query embedded files in MongoDB?

Accessing embedded/nested documents – In MongoDB, you can access the fields of nested/embedded documents of the collection using dot notation and when you are using dot notation, then the field and the nested field must be inside the quotation marks.

How do I insert one document into another in MongoDB?

As we know that in the mongo shell, documents are represented using curly braces ( {} ) and inside these curly braces we have field-value pairs. Now inside these fields, we can embed another document using curly braces {} and this document may contain field-value pairs or another sub-document.


Video Answer


2 Answers

Your case reminds me of the typical relational approach, which I was a victim of, too, when starting to use document-oriented DBs. All of your entities in the example are referenced, there is no redundancy anymore.

You should start to get used to the idea of letting go normalization and starting to duplicate data. In many cases it is hard to determine which data should be referenced and which should be embedded. Your case tends to be quite clear, though.

Without knowing your entire domain model, the address seems to be a perfect candidate for a value object. Do not maintain an Address collection, embed it within the user object. In Order, you could either make a reference to the user, which gives you implicitly the address object and might make sense, since an order is made by a user.

But...I recommend that you embed the address entirely in the Order. First, it is faster since you don't need to resolve a reference. Second, the address in shipped orders should never change! Consider orders of the last year. If you hold a reference to the address you would lose the information to which address they were shipped, once the user changes his address.

Suggestion: Always take a snapshot of the address and embed it in the Order. Save the MongoDB ID of the user as a regular string (no @DBRef) within the `Order. If a user should change his address, you can make a query for all non-shipped orders of that user and amend the address.

like image 147
Jan B. Avatar answered Oct 27 '22 00:10

Jan B.


Since you asked if this is even correct, I would say, gently, "No." At least not typically.

But if you did want to insist on using an embedded address from user:

You can reference the user embedded address in the Order object, just not the way you might think! If you stored the id of the user in the order (it should already be there if Order belongs_to User), then you merely use user.address instead of copying the address instance as you have done.

ALTERNATIVE

I hope to illustrate a better approach to modeling the domain...

A more common approach is to instantiate a new order object, using the user's address as the default "ship to" address for the order, yet allow the user to override the shipping address if desired. In theory, each order could have a different "ship to" address.

Just because two classes have an address, does not mean they are necessarily the same address.

COMMENTARY

Orders are more of an historical document, versus one that changes. Therefore, Orders are generally immutable once placed, your model allows the address to change every time the user changes their address. That change ripples into the Orders, and would be incorrect insofar as normal order business logic goes.

Assume your address last year was in Spain and you had Order #1 show Spain when you ran a report of Orders last year. Imagine if your address this year is now Portugal and Order #1 now shows Portugal in the same report. That would be factually incorrect.

BTW: @Matt gave you the tip that from a "problem domain" perspective, you likely do not want to model it as you have. I am merely elaborating on that...

like image 34
Jon Kern Avatar answered Oct 27 '22 00:10

Jon Kern