Current section

Files

Jump to
gleam_mongo src mongo aggregation.gleam
Raw

src/mongo/aggregation.gleam

import gleam/list
import gleam/queue
import mongo/client
import mongo/cursor
import mongo/utils.{MongoError, default_error}
import bson/value
pub opaque type Pipeline {
Pipeline(
collection: client.Collection,
options: List(AggregateOption),
stages: queue.Queue(value.Value),
)
}
pub type AggregateOption {
BatchSize(Int)
Let(value.Value)
}
pub fn aggregate(
collection: client.Collection,
options: List(AggregateOption),
) -> Pipeline {
Pipeline(collection, options, stages: queue.new())
}
pub fn stages(pipeline: Pipeline, docs: List(value.Value)) {
list.fold(
docs,
pipeline,
fn(new_pipeline, current) { append_stage(new_pipeline, current) },
)
}
pub fn match(pipeline: Pipeline, doc: value.Value) {
append_stage(pipeline, value.Document([#("$match", doc)]))
}
pub fn lookup(
pipeline: Pipeline,
from from: String,
local_field local_field: String,
foreign_field foreign_field: String,
alias alias: String,
) {
append_stage(
pipeline,
value.Document([
#(
"$lookup",
value.Document([
#("from", value.Str(from)),
#("localField", value.Str(local_field)),
#("foreignField", value.Str(foreign_field)),
#("as", value.Str(alias)),
]),
),
]),
)
}
pub fn project(pipeline: Pipeline, doc: value.Value) {
append_stage(pipeline, value.Document([#("$project", doc)]))
}
pub fn add_fields(pipeline: Pipeline, doc: value.Value) {
append_stage(pipeline, value.Document([#("$addFields", doc)]))
}
pub fn sort(pipeline: Pipeline, doc: value.Value) {
append_stage(pipeline, value.Document([#("$sort", doc)]))
}
pub fn group(pipeline: Pipeline, doc: value.Value) {
append_stage(pipeline, value.Document([#("$group", doc)]))
}
pub fn skip(pipeline: Pipeline, count: Int) {
append_stage(pipeline, value.Document([#("$skip", value.Int32(count))]))
}
pub fn limit(pipeline: Pipeline, count: Int) {
append_stage(pipeline, value.Document([#("$limit", value.Int32(count))]))
}
pub fn exec(pipeline: Pipeline) {
let body =
list.fold(
pipeline.options,
[
#("aggregate", value.Str(pipeline.collection.name)),
#(
"pipeline",
pipeline.stages
|> queue.to_list
|> value.Array,
),
#("cursor", value.Document([])),
],
fn(acc, opt) {
case opt {
BatchSize(size) ->
list.key_set(
acc,
"cursor",
value.Document([#("batchSize", value.Int32(size))]),
)
Let(value.Document(let_doc)) ->
list.key_set(acc, "let", value.Document(let_doc))
}
},
)
case client.execute(pipeline.collection, value.Document(body)) {
Ok(result) -> {
let [#("cursor", value.Document(result)), #("ok", ok)] = result
let [
#("firstBatch", value.Array(batch)),
#("id", value.Int64(id)),
#("ns", _),
] = result
case ok {
value.Double(1.0) ->
cursor.new(pipeline.collection, id, batch)
|> Ok
_ -> Error(default_error)
}
}
Error(#(code, msg)) -> Error(MongoError(code, msg, source: value.Null))
}
}
fn append_stage(pipeline: Pipeline, stage: value.Value) {
Pipeline(
collection: pipeline.collection,
options: pipeline.options,
stages: pipeline.stages
|> queue.push_back(stage),
)
}