elixir-mongo / mongodb_ecto

MongoDB adapter for Ecto
Apache License 2.0
368 stars 126 forks source link

_id always blank in embedded documents #184

Open bdtomlin opened 2 years ago

bdtomlin commented 2 years ago

Using embedded_schema like in the example below always just ends up with a null _id in the database. I would expect it to populate the object id just like it does when using a regular schema.

  @primary_key {:id, :binary_id, autogenerate: true}
  embedded_schema do
    field :name, :string
  end
scottmessinger commented 2 years ago

@bdtomlin This is definitely surprising. Here's why it's not happening now:

For a root document, the MongoDB server generates the ObjectId and returns it (though you can also insert a document with an _id of your own creation). When you're inserting/updating an embedded document, mongo doesn't see it as a document it needs to assign an _id to for it's own internal indexes. Instead, Mongo just sees an embedded document as an arbitrary map and doesn't add or remove any fields to it. So, in Elixir, when you add an embedded document object into a struct and save the struct, the embedded document is seen by Mongo as a map of arbitrary fields and no field (e.g. _id) is added. If you insert or update an embedded document in an existing documents, the insert/update is implemented as a$set (e.g. {$set: {"author": {name: "Scott"}}}) or a $push/$addToSet (e.g. {$push: {"authors": {"name: "Scott"}}}). Like with insert, Mongo doesn't manipulate those functions to add a _id to the map being inserted/updated.

Looking towards the future, should the adapter try to generate the _id on the client for embedded documents? Seems reasonable! I don't know how that should be implemented but I'd welcome the PR!

bdtomlin commented 2 years ago

I'm not sure how mongoid (ruby) handles this, but the _id is added by default in embedded documents. I believe there is an option to disable that. I think it should probably be the default here to add the _id as well. It looks like it is for the other mongo libraries.

If I get some time I'll try to look into it. I know elixir-mongo has a function for generating an object id here: https://github.com/elixir-mongo/mongodb/blob/6ae204966e0842c852466431c9fcb06056e1ceb9/lib/mongo.ex#L152-L158

scottmessinger commented 2 years ago

It would be great if @primary_key {:id, :binary_id, :autogenerate: true} worked for embedded docs! Let me know if you find out how to make it work!