question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

MongoDB: Support for embedded documents

See original GitHub issue

Problem

One of the major features of MongoDB is how you can store embedded data within a record. MongoDB also offers rich querying capabilities to work with this data.

Currently the Prisma Client supports writing embedded documents with the Json type

model User {
  name String
  address Json
}
prisma.user.create({
  data: {
    name: "Alice"
    address: {
      street: "1234 Wonderland Ave"
      zip: "12351"
    }
  }
})

Unfortunately this isn’t type-safe. Nothing stops you from adding another random field to the address or writing “street” as “st”.

Additionally, we currently only support a few operations on the Json type for MongoDB.

Goal

  • We’d like to introduce a new way to store and query embedded data in a type-safe way.
  • We’d like to be able to apply this new approach to other DBs with JSON support like Postgres.

Suggested solution

Add support for embeds. Potentially introduce an embed keyword in the Prisma schema

model User {
  name String
  address Address
}

embed Address {
  street String
  zip String
}

Alternatives

While above feels clean, one issue is that embed seems to be an attribute of the Address, not a defining feature of the Address. You might want Address to be embedded in some model, but relational in others. That makes me think that the caller should decide, not the definer.

model User {
  name String
  address Address @embed
}

model Address {
  street String
  zip String
}

The upside to this is that there’s no new blocks either.

Additional context

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:115
  • Comments:19 (7 by maintainers)

github_iconTop GitHub Comments

62reactions
matthewmuellercommented, Aug 9, 2021

Hey everyone! I just want to share that we’ve come up with a design for this. Please let me know what you think and if you have any questions!

Also if you feel like discussing the proposal in more detail, you can schedule a short call with me here: https://calendly.com/matthewmueller/embedded-documents.

Overview of the solution

This solution extends the Prisma Schema to support defining your own custom types.

In the following example, we define Address and Pet as embedded types that we use within our User model:

type Address {
  zip     Int
  street  String 
  country String
}

type Pet {
  type  String
  breed String
  name  String
}

model User {
  id      String @id @default(dbgenerated()) @db.ObjectId @map("_id")
  name    String
  address Address
  pets    Pet[]
}

Outcome

This solution has the following design goals:

  • Embedded data access has the same API as relational data access
  • Introspecting embedded data will be possible to add in the future with this design
  • Migrating embedded data will be possible to add in the future with this design
  • General enough to eventually support Typed JSON for SQL databases

By achieving these goals, developers using MongoDB will be able to work with embedded documents in the same way they can work with relational documents. There won’t be anything new to learn and you get a new way of storing and accessing data.

This can solution can be ported to SQL databases that support a json column, giving developers a type-safe embedded data access for relational databases.

Lastly, this may give us a way to “mixin” types with models, allowing developers to reuse fields inside a model.

FAQ

  • Is it a property of the “relation” or a property of the embedded model itself?

    In Prisma1, it was basically both - a relation to a model annotated with embed

  • Do embeds need IDs?

    No, embed aren’t addressable on their own. They are always reached from a model which does have an ID.

  • What about uniques in embeds?

    Depends on the underlying capability. If it’s possible in the underlying source it should be possible. However, it doesn’t seem like it’s currently possible https://joegornick.com/2012/10/25/mongodb-unique-indexes-on-single/embedded-documents/

  • Do embeds have backrelations (e.g. accessing posts from authors)?

    No. Since the embeds are not a top-level, standalone model, there isn’t a case where you have an embed and need to reach the model.

  • Can embeds have relations to other models?

    If the underlying datasource can define a foreign key on the embedded type, it should be possible, but this would be out of scope for the first iteration. You could always pull the foreign key and lookup the relation in a separate query.

  • Can an embed be nested within another embed (type in a type)?

    Nothing should prevent this, but not a high priority for the first iteration of the solution.

  • Embeds as an ID?

    This should be possible because MongoDB supports objects as primary keys, but supporting this use case is low priority.

  • Do embeds need to be uniquely named?

    Yes, for now because it simplifies the solution. Since types are a purely Prisma construct and not tied to the database, you could have a type name that’s also a model name.

References

21reactions
matthewmuellercommented, Feb 28, 2022

Hey all, I just wanted to share that Embedded Document support for MongoDB is now in Preview!

It’s easy to get started. First, install the latest Prisma (3.10.0) and add the mongoDb preview feature flag:

datasource db {
  provider = "mongodb"
  url      = env("DATABASE_URL")
}

generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["mongoDb"]
}

Then you’ll have a new type keyword in your Prisma Schema (or will get schemas with type for your existing MongoDB databases that use Embedded Documents when running prisma db pull), for example:

model Order {
  id              String   @id @default(auto()) @map("_id") @db.ObjectId
  product         Product  @relation(fields: [productId], references: [id])
  color           Color
  size            Size
  shippingAddress Address
  billingAddress  Address?
  productId       String   @db.ObjectId
}

// New composite type!
type Address {
  street String
  city   String
  zip    String
}

Once you run prisma generate, you’ll have a new type-safe API for working with these types.

For example, here’s how you’d create a new Order with an embedded shipping address:

const order = await prisma.order.create({
  data: {
    // Normal relation
    product: { connect: { id: 'some-object-id' } },
    color: 'Red',
    size: 'Large',
    // Composite type
    shippingAddress: {
      street: '1084 Candycane Lane',
      city: 'Silverlake',
      zip: '84323',
    },
  },
})

This is just the tip of the iceberg. Dive deeper in our documentation.


If you have any feedback for us, I’d love to jump on a call with you.

We’re actively working towards getting MongoDB production ready, so now’s the time to give this a try and share your thoughts!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Embedding MongoDB Documents For Ease And Performance
Embedded documents are an efficient and clean way to store related data, especially data that's regularly accessed together. In general, when designing schemas ......
Read more >
MongoDB - Embedded Documents - GeeksforGeeks
In MongoDB, you can easily embed a document inside another document. As we know that in the mongo shell, documents are represented using...
Read more >
MongoDB Documents: Document, Array, Embedded Document
A document in MongoDB can have fields that hold another document. It is also called nested documents. The following is an embedded document...
Read more >
Embedded Documents in a MongoDB Collection - ObjectRocket
What is an Embedded MongoDB Document? ... At this point, we're just about ready to dive into some code examples, but first, let's...
Read more >
Querying Embedded Documents in MongoDB Arrays - Studio 3T
An Array field can contain zero or more values (an array) of any supported type, including the Object type, which is used for...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found