1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
use ql2::term::TermType;
use serde::Serialize;
use unreql_macros::create_cmd;
use crate::{
cmd::{args::Opt, options::Index},
Command,
};
create_cmd!(
/// Returns an inner join of two sequences.
///
/// The returned sequence represents an intersection of the left-hand sequence
/// and the right-hand sequence: each row of the left-hand sequence will be compared
/// with each row of the right-hand sequence to find all pairs of rows which satisfy
/// the predicate. Each matched pair of rows of both sequences are combined into
/// a result row. In most cases, you will want to follow the join with zip to combine
/// the left and right results.
///
/// *Note* that `inner_join` is slower and much less efficient than using `eq_join` or
/// `concat_map` with `get_all`. You should avoid using `inner_join` in commands when possible.
///
/// ## Example
/// Return a list of all matchups between Marvel and DC heroes in which the DC hero
/// could beat the Marvel hero in a fight.
///
/// ```
/// # use unreql::func;
/// # unreql::example(|r, conn| {
/// r.table("marvel").inner_join(
/// r.table("dc"),
/// func!(|marvelRow, dcRow| {
/// marvelRow.g("strength").lt(dcRow.g("strength"))
/// })
/// ).zip().run(conn)
/// # })
/// ```
///
/// (Compare this to an [outer_join](Self::outer_join) with the same inputs and predicate, which would
/// return a list of all Marvel heroes along with any DC heroes with a higher strength.)
///
/// # Related commands
/// - [outer_join](Self::outer_join)
/// - [eq_join](Self::eq_join)
/// - [zip](Self::zip)
only_command,
inner_join(other_sequence: Serialize, predicate: Serialize)
);
create_cmd!(
/// Returns a left outer join of two sequences.
///
/// The returned sequence represents a union of the left-hand sequence and
/// the right-hand sequence: all documents in the left-hand sequence will be returned,
/// each matched with a document in the right-hand sequence if one satisfies
/// the predicate condition. In most cases, you will want to follow the join with
/// [zip](Self::zip) to combine the left and right results.
///
/// *Note* that `outer_join` is slower and much less efficient than using
/// [concat_map](Self::concat_map) with [get_all](Self::get_all). You should avoid using `outer_join` in commands when possible.
///
/// ## Example
/// Return a list of all Marvel heroes, paired with any DC heroes who could beat them in a fight.
///
/// ```
/// # use unreql::func;
/// # unreql::example(|r, conn| {
/// r.table("marvel").outer_join(
/// r.table("dc"),
/// func!(|marvelRow, dcRow| {
/// marvelRow.g("strength").lt(dcRow.g("strength"))
/// })
/// ).zip().run(conn)
/// # })
/// ```
///
/// (Compare this to an [inner_join](Self::inner_join) with the same inputs and predicate,
/// which would return a list only of the matchups in which the DC hero has
/// the higher strength.)
///
/// # Related commands
/// - [inner_join](Self::inner_join)
/// - [eq_join](Self::eq_join)
/// - [zip](Self::zip)
only_command,
outer_join(other_sequence: Serialize, predicate: Serialize)
);
create_cmd!(
/// Join tables using a field or function on the left-hand sequence
/// matching primary keys or secondary indexes on the right-hand table.
///
/// `eq_join` is more efficient than other ReQL join types, and operates
/// much faster. Documents in the result set consist of pairs of left-hand
/// and right-hand documents, matched when the field on the left-hand side
/// exists and is non-null and an entry with that field’s value exists
/// in the specified index on the right-hand side.
///
/// The result set of `eq_join` is a stream or array of objects. Each object
/// in the returned set will be an object of the form
/// `{ left: <left-document>, right: <right-document> }`, where the values
/// of `left` and `right` will be the joined documents. Use the [zip](Self::zip) command
/// to merge the `left` and `right` fields together.
///
/// The results from `eq_join` are, by default, not ordered. The optional
/// `ordered: true` parameter will cause `eq_join` to order the output
/// based on the left side input stream. (If there are multiple matches
/// on the right side for a document on the left side, their order is
/// not guaranteed even if `ordered` is `true`.) Requiring ordered results
/// can significantly slow down `eq_join`, and in many circumstances this
/// ordering will not be required. (See the first example, in which
/// ordered results are obtained by using `order_by` after `eq_join`.)
///
/// Suppose the players table contains these documents:
///
/// ```json
/// [
/// { "id": 1, "player": "George", "gameId": 1 },
/// { "id": 2, "player": "Agatha", "gameId": 3 },
/// { "id": 3, "player": "Fred", "gameId": 2 },
/// { "id": 4, "player": "Marie", "gameId": 2 },
/// { "id": 5, "player": "Earnest", "gameId": 1 },
/// { "id": 6, "player": "Beth", "gameId": 3 }
/// ]
/// ```
///
/// The games table contains these documents:
///
/// ```json
/// [
/// { "id": 1, "field": "Little Delving" },
/// { "id": 2, "field": "Rushock Bog" },
/// { "id": 3, "field": "Bucklebury" }
/// ]
/// ```
///
/// ## Example: Match players with the games they’ve played against one another.
///
/// Join these tables using gameId on the player table and id on the games table:
///
/// ```
/// # unreql::example(|r, conn| {
/// r.table("players").eq_join("game_id", r.table("games"), ()).run(conn)
/// # })
/// ```
///
/// This will return a result set such as the following:
///
/// ```json
/// [
/// {
/// "left" : { "gameId" : 3, "id" : 2, "player" : "Agatha" },
/// "right" : { "id" : 3, "field" : "Bucklebury" }
/// },
/// {
/// "left" : { "gameId" : 2, "id" : 3, "player" : "Fred" },
/// "right" : { "id" : 2, "field" : "Rushock Bog" }
/// },
/// ...
/// ]
/// ```
///
/// What you likely want is the result of using `zip` with that.
/// For clarity, we’ll use `without` to drop the `id` field from the games
/// table (it conflicts with the `id` field for the players
/// and it’s redundant anyway), and we’ll order it by the games.
///
/// ```
/// # use unreql::rjson;
/// # unreql::example(|r, conn| {
/// r.table("players")
/// .eq_join("game_id", r.table("games"), ())
/// .without(rjson!({ "right": "id" }))
/// .zip()
/// .order_by("game_id")
/// .run(conn)
/// # })
/// ```
///
/// ```json
/// [
/// { "field": "Little Delving", "gameId": 1, "id": 5, "player": "Earnest" },
/// { "field": "Little Delving", "gameId": 1, "id": 1, "player": "George" },
/// { "field": "Rushock Bog", "gameId": 2, "id": 3, "player": "Fred" },
/// { "field": "Rushock Bog", "gameId": 2, "id": 4, "player": "Marie" },
/// { "field": "Bucklebury", "gameId": 3, "id": 6, "player": "Beth" },
/// { "field": "Bucklebury", "gameId": 3, "id": 2, "player": "Agatha" }
/// ]
/// ```
///
/// For more information, see [Table joins in RethinkDB](https://rethinkdb.com/docs/table-joins/).
///
/// ## Example
/// Use a secondary index on the right table rather than
/// the primary key. If players have a secondary index on their cities,
/// we can get a list of arenas with players in the same area.
///
/// ```
/// # unreql::example(|r, conn| {
/// r.table("players")
/// .eq_join("game_id", r.table("games"), r.index("cityId"))
/// .run(conn)
/// # })
/// ```
///
/// ## Example
/// Use a nested key as the join field. Suppose the documents
/// in the players table were structured like this:
///
/// ```json
/// { "id": 1, "player": "George", "game": {"id": 1} },
/// { "id": 2, "player": "Agatha", "game": {"id": 3} },
/// ...
/// ```
///
/// Simply specify the field using the `row` command instead of a string.
///
/// ```
/// # use unreql::rjson;
/// # unreql::example(|r, conn| {
/// r.table("players")
/// .eq_join(r.row().g("game").g("id"), r.table("games"), ())
/// .without(rjson!({ "right": "id" }))
/// .zip()
/// .run(conn)
/// # })
/// ```
///
/// ```json
/// [
/// { "field": "Little Delving", "game": { "id": 1 }, "id": 5, "player": "Earnest" },
/// { "field": "Little Delving", "game": { "id": 1 }, "id": 1, "player": "George" },
/// ...
/// ]
/// ```
///
/// ## Example
/// Use a function instead of a field to join on a more complicated
/// expression. Suppose the players have lists of favorite games ranked
/// in order in a field such as `favorites: [3, 2, 1]`. Get a list of players
/// and their top favorite:
///
/// ```
/// # use unreql::{func, rjson};
/// # unreql::example(|r, conn| {
/// r.table("players")
/// .eq_join(
/// func!(|player| {
/// player.g("favorites").nth(0)
/// }),
/// r.table("games"),
/// ()
/// )
/// .without(rjson!([{ "left": ["favorites", "gameId", "id"]}, { "right": "id"}]))
/// .zip()
/// .run(conn)
/// # })
/// ```
///
/// Result:
///
/// ```json
/// [
/// { "field": "Rushock Bog", "name": "Fred" },
/// { "field": "Little Delving", "name": "George" },
/// ...
/// ]
/// ```
///
/// # Related commands
/// - [inner_join](Self::inner_join)
/// - [outer_join](Self::outer_join)
/// - [without](Self::without)
/// - [zip](Self::zip)
only_command,
eq_join(left_field: Serialize, right_table: Serialize, opt: Opt<Index>)
);
create_cmd!(
/// Used to ‘zip’ up the result of a join by merging the ‘right’ fields into ‘left’ fields of each member of the sequence.
///
/// ## Example
/// ‘zips up’ the sequence by merging the left and right fields produced by a join.
///
/// ```
/// # unreql::example(|r, conn| {
/// r.table("marvel")
/// .eq_join("main_dc_collaborator", r.table("dc"), ())
/// .zip()
/// .run(conn)
/// # })
/// ```
///
/// # Related commands
/// - [eq_join](Self::eq_join)
/// - [inner_join](Self::inner_join)
/// - [outer_join](Self::outer_join)
only_command,
zip,
);