///|
fn collect_positive(query : Query, terms : Map[String, Bool]) -> Unit {
  match query {
    Term(_, term) => terms[term] = true
    Phrase(_, words) =>
      for word in words {
        terms[word] = true
      }
    And(a, b) | Or(a, b) => {
      collect_positive(a, terms)
      collect_positive(b, terms)
    }
    Not(_) => ()
  }
}

///|
fn highlights(
  document : Value,
  fields : Array[String],
  query : Query,
) -> Value raise {
  let terms : Map[String, Bool] = Map([])
  collect_positive(query, terms)
  let result : Map[String, Value] = Map([])
  for field in fields {
    let tokens = analyze(optional_str(get(get(document, "fields"), field), ""))
    result[field] = Array(
      tokens
      .filter(fn(token) { terms.contains(token.term) })
      .map(fn(token) {
        record([
          ("term", String(token.term)),
          ("start", Number(token.start.to_double())),
          ("end", Number(token.end.to_double())),
        ])
      }),
    )
  }
  Object(result)
}