// A product index in Elasticsearch or OpenSearch: documents written one at a time and in bulk,
// with the bulk answer read item by item, a search, deep paging with search_after, and a whole
// result set walked under a point in time.
//
//   ELASTICSEARCH_API_URL=http://localhost:9200 quillo run elasticsearch.qwe
//   then open /setup, /bulk, /search?q=mug, /pages, /scan, /broken and /remove
//
// ELASTICSEARCH_API_KEY sends an API key; ELASTICSEARCH_USERNAME and ELASTICSEARCH_PASSWORD basic
// auth, as OpenSearch uses.

use "elasticsearch.qwer"

route GET /setup = {
    made: elastic_create_index("products", { mappings: { properties: { name: { type: "text" }, price: { type: "float" }, sku: { type: "keyword" } } } })?,
    again: elastic_create_index("products", null)?,
    one: elastic_index("products", "p1", { name: "Blue mug", price: 9.5, sku: "m-1" }, { refresh: true })?,
    read: elastic_get("products", "p1")?,
    missing: elastic_get("products", "nope")?,
    stale: elastic_index("products", "p1", { name: "Old" }, { if_seq_no: 99, if_primary_term: 1 }).error,
}

// One line is a bad price and one id is in use: the rest still go in, and the answer says which.
route GET /bulk = elastic_bulk([
    { index: "products", id: "p2", doc: { name: "Red mug", price: 11.0, sku: "m-2" } },
    { index: "products", id: "p3", doc: { name: "Green mug", price: "free", sku: "m-3" } },
    { op: "create", index: "products", id: "p1", doc: { name: "Taken" } },
    { index: "products", id: "p4", doc: { name: "Tall mug\nline two", price: 14.0, sku: "m-4" } },
    { op: "update", index: "products", id: "p2", doc: { price: 12.0 } },
    { op: "delete", index: "products", id: "gone" },
], "true")?

route GET /search ? { q: str } = {
    results: elastic_search("products", { query: { match: { name: q } }, sort: [{ price: "asc" }], size: 10 })?,
    count: elastic_count("products", { match: { name: q } })?,
    escaped: elastic_escape("a+b (c) <d>"),
}

// Page after page: each page's `next` starts the following one.
route GET /pages {
    let body = { query: { match_all: {} }, sort: [{ price: "asc" }, { sku: "asc" }], size: 2 }
    let first = elastic_search_after("products", body, null)?
    let second = elastic_search_after("products", body, first.next)?
    { first: first.hits | map((h) => h.sku), next: first.next, second: second.hits | map((h) => h.sku), last: second.next }
}

route GET /scan {
    let all = elastic_search_all("products", { query: { match_all: {} }, sort: [{ sku: "asc" }], size: 2 }, 100)?
    { pit: all.pit, skus: all.hits | map((h) => h.sku), complete: all.complete }
}

route GET /broken = {
    no_index: elastic_search("no-such-index", { query: { match_all: {} } }).error,
    no_sort: elastic_search_after("products", { size: 2 }, null).error,
}

route GET /remove {
    let first = elastic_delete("products", "p4")?
    { deleted: first, deleted_again: elastic_delete("products", "p4")? }
}
