db("networks").where({
	user_id: userId
}).with({
	tabs: db("tabs")
		.onField("network_id")
		.with({
			$merge: db("channel_events")
				.onOwnField("channel_id")
		})
	
	db.combine([
		db("channels")
			.onField("network_id")
			.with({
				events: db("channel_events")
					.onField("channel_id")
					.with({
						notifications: db("notifications").onField("channel_events_id")
					})
			}),
		db("private")
	])
})

/* FIXME: tagging for (potentially nested) query modifications for eg. batching */

db("networks")
	.where({ user_id: userId })
	.withMany("tabs", ({$db, id}) => {
		return $db("tabs")
			.where({ network_id: id })
			.withOne("channel_data", ({$db, type, channel_id}) => {
				if (type === "channel") {
					return $db("channels")
						.where({ id: channel_id })
				}
			})
			.withMany("events", ({$db, type, channel_id, nickname}) => {
				if (type === "channel") {
					return $db("presences")
						.where({
							channel_id: channel_id,
							user_id: userId
						})
						.flatten(({$db, first_event, last_event}) => {
							return $db("channel_events")
								.where({ channel_id: channel_id })
								.where(({id}) => id >= first_event && id <= last_event);
						});
				} else if (type === "privateSession") {
					return $db("private_messages")
						.where({
							user_id: userId,
							nickname: nickname
						});
				}
			})
	})


/*
# Relation operations
.withOne - subquery for each item, one result, store as property
.withMany - subquery for each item, many results, store as property
.merge - subquery for each item, one result, merge into item
.flatten - subquery for each item, many results, replace item set with combined subquery result sets (can be done recursively)

# Query phases
Compilation - turn query into structured data, parse callbacks, etc.
Validation - verify that all of the referenced tables/columns/etc. exist in the DB
Scheduling - turn into underlying SQL queries
Execution - run the query

Any modification to a query (which is immutable) produces a new query, and existing compilation/validation/scheduling results do not carry over!

Calling .execute(db/tx) directly on a built query will go through all four phases automatically; the developer may also choose to pre-compile their query with .compile(), and pre-validate it by calling .validate(db/tx), and then finally pre-schedule it by calling .schedule(). Alternatively, they can call .prepare(db/tx) which does all of those three steps?

How to capture user-specified variables from outer scope? Also should wrap parsed callbacks within arrow function wrapper instead of array, to pass in variable definitions from outer callbacks.
*/