Here's a proposal:
join
takes 2 arguments:
left_key
that can be a string or a function (it's the same thing as what eqJoin require for the first argumentother_table
: the second sequence is a table.
The command has the following options:
index - <string>
: The name of the index used onother_table
to perform the join, default to the primary key.group - <string>
: If this option is undefined, the join command would operate as it does now. If it's set to a string, it would group the right values into an array and inject in the field (or just inject the document isunique
is set totrue
(the value forgroup
). The default isundefined
.outer_join - <boolean>
whether the join should behave like an outer join or not. By defaulttrue
. The value is always false whengroup
isfalse
. Edge case: the joined value should be an empty array, or null if no document can be joined.multi - <boolean>
: if set to true, the join is performed on each value of the left key (supposing that the left key maps to an array). Default to false. If the value is not an array, the join should not be performed:unique - <boolean>
: If more than one document is found, we should return an error.compose - <function>
: compose the joined sequence/object with a method. The function provides takes one argument which is the sequence/object returned by the join operation.overwrite - <boolean>
replace the document with the joined one.
Examples:
- Simple join:
r.table("posts").join("id", r.table("comments"), {index: "postId", group: "comments"}
// equivalent to
r.table("posts").merge(function(post) {
return post.merge({
comments: r.table("comments").getAll(post("id"), {index: "postId"}).coerceTo("ARRAY")
})
})
- With
outerJoin: false
r.table("posts").join("id", r.table("comments"), {index: "postId", group: "comments", outerJoin: false}
// equivalent to
r.table("posts").concatMap(function(post) {
return r.table("comments").getAll(post("id"), {index: "postId"}).coerceTo("ARRAY").do(function(sequence) {
r.branch(
sequence.isEmpty(),
[],
[post.merge({ comments: sequence })]
)
})
})
- Compose a joined sequence with an orderBy
r.table("posts").join("id", r.table("comments"), {
index: "postId",
group: "comments",
compose: function(sequence) {
return sequence.orderBy("date")
}
}
// equivalent to
r.table("posts").merge(function(post) {
return post.merge({
comments: r.table("comments").getAll(post("id"), {index: "postId"})
.orderBy("date").coerceTo("ARRAY") // the coerce here is optional
})
})
- Nested joins
r.table("posts").join("id", r.table("comments"), {
index: "postId",
group: "comments",
compose: function(sequence) {
return sequence.join("authorId", r.table("author"), {unique: true, group: "author"}
}
}
// equivalent to
r.table("posts").merge(function(post) {
return post.merge({
comments: r.table("comments").getAll(post("id"), {index: "postId"})
.merge(function(comment) {
return { author: r.table("author").get(comment("authorId") }
}).coerceTo("ARRAY") // the coerce here is optional
})
})
- Nested joined on a non primary key
r.table("posts").join("id", r.table("comments"), {
index: "postId",
group: "comments",
compose: function(sequence) {
return sequence.join("authorId", r.table("author"), {unique: true, group: "author", index: "uniqueId"}
}
}
// equivalent to
r.table("posts").merge(function(post) {
return post.merge({
comments: r.table("comments").getAll(post("id"), {index: "postId"})
.merge(function(comment) {
return {
author: r.do(
r.table("author").getAll(comment("authorId") , {index: "uniqueId"}).coerceTo("ARRAY"),
function(sequence) {
r.branch(
sequence.count().gt(1),
r.error("More than one document found")
sequence.nth(0).default(null)
)
})
}
}).coerceTo("ARRAY") // the coerce here is optional
})
})
- Many to many relations where the link between two documents is not returned.
r.table("students").join("id", r.table("student_class"), {
index: "studentId",
group: "classes",
compose: function(sequence) {
return sequence.join("classId", r.table("class"), {unique: true, overwrite: true}
}
}
// equivalent to
r.table("students").merge(function(student) {
return {
classes: r.table("student_class").getAll(student("id"), {index: "studentId"}).map(function(link) {
return r.table("class").get(link("classId"))
})
}
})