vapor / fluent-mongo-driver

MongoDB support for Fluent built on MongoKittten.
MIT License
25 stars 11 forks source link

Fluent beta 5 preperation, and start developing join support #4

Closed Joannis closed 4 years ago

Joannis commented 4 years ago

Hey @tanner0101, I can't get joins working the way needed for fluent. It's a big pain to wrap my head around the SQL/Fluent expectations.

tanner0101 commented 4 years ago

Joins are specified by DatabaseQuery.joins. The only supported case at the moment is:

case join(schema: Schema, foreign: Field, local: Field, method: Method)

This specifies which schema (collection) is joined in, which fields are being matched for the join, and the method of the join: inner, left, right, outer.

If you do:

Planet.query(on: db)
    .join(Star.self, on: \Planet.$star.$id == \Star.$id)

The following join is added to the DatabaseQuery's [Join] array:

case join(
    schema: "stars", 
    foreign: .field(path: ["id"], schema: "stars"), 
    local: .field(path: ["star_id"], schema: "planets"), 
    method: .inner
)

This should cause a star to be included for each planet returned by the database. In SQL this is all flat obviously, but in Mongo that might look nested? Not sure. My guess is the result set would look something like this:

[
    { planet: { ... }, star: { ... } },
    { planet: { ... }, star: { ... } },
    { planet: { ... }, star: { ... } },
]

When a model is joined, the additional field keys will be added to DatabaseQuery.fields. Taking the previous example, the fields array would look something like:

[
    .field(path: ["id"], schema: "planets"),
    .field(path: ["name"], schema: "planets"),
    .field(path: ["id"], schema: "stars", as: "star_id"),
    .field(path: ["name"], schema: "stars", as: "star_name"),
]

What's important about this is it provides aliases for all of keys from the joined collection. This ensures that no keys will conflict in the resulting DatabaseRow.

Taking the joined results and combining it with the field aliases, the driver should generate rows of the following structure:

[
    "id": planet.id,
    "name": planet.name,
    "star_id": star.id,
    "star_name": star.name
]

Fluent handles the rest from there, allowing users to get the joined star by doing planet.joined(Star.self).

Let me know if that makes any sense. There's probably room for improvement here. The current design definitely feels biased toward SQL. Any info you can share on how exactly joins work in Mongo would be helpful to compare and contrast.