Parsed documentation:
View on GitHub
A join query expression.

Receives a model that is to be joined to the query and a condition for
the join. The join condition can be any expression that evaluates
to a boolean value. The join is by default an inner join, the qualifier
can be changed by giving the atoms: `:inner`, `:left`, `:right` or
`:full`. For a keyword query the `:join` keyword can be changed to:
`:inner_join`, `:left_join`, `:right_join` or `:full_join`.

Currently it is possible to join an existing model, an existing source
(table), an association or a fragment. See the examples below.

## Keywords examples

    from c in Comment,
      join: p in Post, on: c.post_id ==,
      select: {p.title, c.text}

    from p in Post,
      left_join: c in assoc(p, :comments),
      select: {p, c}

## Expressions examples

      |> join(:inner, [c], p in Post, c.post_id ==
      |> select([c, p], {p.title, c.text})

      |> join(:left, [p], c in assoc(p, :comments))
      |> select([p, c], {p, c})

## Joining with fragments

When you need to join on a complex expression that cannot be
expressed via Ecto associations, Ecto supports fragments in joins:

    |> join(:inner, [c], p in fragment("SOME COMPLEX QUERY",, ^some_param))

This style discouraged due to its complexity.
Please help! Open an issue on GitHub if this assessment is incorrect.