///|
fn numeric_operator(
operator : @ast.BinaryOp,
left : Double,
right : Double,
) -> Double raise @model.PromqlError {
match operator {
@ast.BinaryOp::Add => left + right
@ast.BinaryOp::Sub => left - right
@ast.BinaryOp::Mul => left * right
@ast.BinaryOp::Div => left / right
@ast.BinaryOp::Mod => left % right
@ast.BinaryOp::Pow => @math.pow(left, right)
_ => raise @model.EvalError("operator is not arithmetic")
}
}
///|
fn comparison_operator(
operator : @ast.BinaryOp,
left : Double,
right : Double,
) -> Bool raise @model.PromqlError {
match operator {
@ast.BinaryOp::Eq => left == right
@ast.BinaryOp::Neq => left != right
@ast.BinaryOp::Gt => left > right
@ast.BinaryOp::Lt => left < right
@ast.BinaryOp::Gte => left >= right
@ast.BinaryOp::Lte => left <= right
_ => raise @model.EvalError("operator is not a comparison")
}
}
///|
fn is_comparison(operator : @ast.BinaryOp) -> Bool {
match operator {
@ast.BinaryOp::Eq
| @ast.BinaryOp::Neq
| @ast.BinaryOp::Gt
| @ast.BinaryOp::Lt
| @ast.BinaryOp::Gte
| @ast.BinaryOp::Lte => true
_ => false
}
}
///|
fn is_set_operator(operator : @ast.BinaryOp) -> Bool {
match operator {
@ast.BinaryOp::And | @ast.BinaryOp::Or | @ast.BinaryOp::Unless => true
_ => false
}
}
///|
fn signature(
labels : @model.LabelSet,
vector_match : @ast.VectorMatch?,
) -> @model.LabelSet {
match vector_match {
None => labels.remove("__name__")
Some({ on: true, labels: names, .. }) => labels.only(names)
Some({ on: false, labels: names, .. }) => {
let without : Array[String] = ["__name__"]
for name in names {
without.push(name)
}
labels.without(without)
}
}
}
///|
fn map_instant_scalar(
points : Array[@model.Point],
scalar : Double,
scalar_on_left : Bool,
operator : @ast.BinaryOp,
bool_modifier : Bool,
) -> Array[@model.Point] raise @model.PromqlError {
let output : Array[@model.Point] = []
for point in points {
let left = if scalar_on_left { scalar } else { point.value }
let right = if scalar_on_left { point.value } else { scalar }
if is_comparison(operator) {
let matched = comparison_operator(operator, left, right)
if bool_modifier {
output.push({
labels: point.labels.remove("__name__"),
value: if matched {
1.0
} else {
0.0
},
})
} else if matched {
output.push({
labels: point.labels.remove("__name__"),
value: point.value,
})
}
} else {
output.push({
labels: point.labels.remove("__name__"),
value: numeric_operator(operator, left, right),
})
}
}
output
}
///|
fn apply_set_operator(
operator : @ast.BinaryOp,
left : Array[@model.Point],
right : Array[@model.Point],
vector_match : @ast.VectorMatch?,
) -> Array[@model.Point] {
let right_keys : Array[String] = []
for point in right {
right_keys.push(signature(point.labels, vector_match).to_string())
}
let output : Array[@model.Point] = []
if operator is @ast.BinaryOp::Or {
for point in left {
output.push(point)
}
for point in right {
let key = signature(point.labels, vector_match).to_string()
let mut exists = false
for left_point in left {
if signature(left_point.labels, vector_match).to_string() == key {
exists = true
break
}
}
if !exists {
output.push(point)
}
}
} else {
for point in left {
let key = signature(point.labels, vector_match).to_string()
let mut found = false
for right_key in right_keys {
if right_key == key {
found = true
break
}
}
if operator is @ast.BinaryOp::And && found {
output.push(point)
}
if operator is @ast.BinaryOp::Unless && !found {
output.push(point)
}
}
}
output
}
///|
fn apply_vector_vector(
operator : @ast.BinaryOp,
left : Array[@model.Point],
right : Array[@model.Point],
bool_modifier : Bool,
vector_match : @ast.VectorMatch?,
) -> Array[@model.Point] raise @model.PromqlError {
if is_set_operator(operator) {
return apply_set_operator(operator, left, right, vector_match)
}
let output : Array[@model.Point] = []
for left_point in left {
let left_key = signature(left_point.labels, vector_match).to_string()
for right_point in right {
let right_key = signature(right_point.labels, vector_match).to_string()
if left_key != right_key {
continue
}
let mut labels = match vector_match {
Some({ side: @ast.GroupSide::GroupRight, .. }) => right_point.labels
_ => left_point.labels
}
match vector_match {
Some({ side: @ast.GroupSide::GroupLeft, include_labels, .. }) =>
for name in include_labels {
if right_point.labels.get(name) is Some(value) {
labels = labels.set(name, value)
}
}
Some({ side: @ast.GroupSide::GroupRight, include_labels, .. }) =>
for name in include_labels {
if left_point.labels.get(name) is Some(value) {
labels = labels.set(name, value)
}
}
_ => ()
}
if is_comparison(operator) {
let matched = comparison_operator(
operator,
left_point.value,
right_point.value,
)
if bool_modifier {
output.push({
labels: labels.remove("__name__"),
value: if matched {
1.0
} else {
0.0
},
})
} else if matched {
output.push({ labels, value: left_point.value, })
}
} else {
output.push({
labels: labels.remove("__name__"),
value: numeric_operator(operator, left_point.value, right_point.value),
})
}
}
}
output.sort_by((left_point, right_point) => {
left_point.labels.to_string().compare(right_point.labels.to_string())
})
output
}