API
Ka otsing on staatiline indeks: umbes 7000 väikest faili, mis katavad koos iga sarja, filmi ja inimese iga nime igas keeles. Päringuks on vaja ainult ühte neist.
Failid
https://api.watchittrackit.com/v1/search/manifest.jsonhttps://api.watchittrackit.com/v1/search/{build}/tree.jsonhttps://api.watchittrackit.com/v1/search/{build}/l/{hex}.jsonhttps://api.watchittrackit.com/v1/search/{build}/s/{hex}.jsonhttps://api.watchittrackit.com/v1/search/recent.json
Päringu otsimine
- Loe
v1/search/manifest.json, et saada praeguse ehituse id. - Loe
v1/search/{build}/tree.json. See ei muutu ühe ehituse jooksul kunagi, seega laadi see alla üks kord. - Normaliseeri päring: teisenda see väiketähtedeks, eemalda diakriitikud ja kirjavahemärgid. Otsi selle pikima sõna järgi, mis pole stoppsõna.
- Läbi selle sõnaga puu, et leida selle fail: ämber kaustas
l/või, kui sõna lõpeb täpselt jaotusprefiksil, kokkuvõte kaustass/. Failinimed on prefiksi UTF-8 baitide kuueteistkümnendkuju. - Jäta alles kirjed, kus iga päringusõna on mõne nimesõna algus. Pane ettepoole nimed, mis algavad kogu päringuga, ja seejärel sordi populaarsuse järgi.
- Rakenda
v1/search/recent.json: selles on kirjed, mida on pärast ehitust muudetud. Eemalda nende kirjete indeksikanded ja kasuta nende asemel neid. See muutub kirjete muutmisel, seega lisa värske koopia saamiseks päringustring, mis muutub iga paari minuti järel (?w=pluss praegune 5-minutiline aken).
Kirjed
Iga kirje on massiiv:
| Positsioon | Tüüp | Kirjeldus |
|---|---|---|
| 0 | string | Kirje tüüp. |
| 1 | number | Kirje id. |
| 2 | string | Sobinud nimi, olenemata selle keelest. |
| 3 | number | Koht tüübi piires: 1 000 000 on kõige populaarsem. |
| 4 | string | Kirje slug. |
| 5 | number | null | Aasta, pealkirjade puhul. |
| 6 | string | null | Pisipildi tee. |
Näide (JavaScript)
const BASE = "https://api.watchittrackit.com/v1/search"
const STOPWORDS = new Set(["a", "an", "and", "the", "of", "in", "on", "to", "for", "at", "by", "with", "or",
"de", "del", "la", "le", "les", "el", "los", "las", "lo", "un", "une", "et", "du", "des", "da", "do", "dos",
"der", "die", "das", "und", "ein", "eine", "den", "dem", "von", "zu", "il", "i", "e", "di", "y", "en", "het", "een", "van"])
const normalize = (s) => s.normalize("NFKD").replace(/\p{M}/gu, "").toLowerCase()
.replace(/[^\p{L}\p{N}]+/gu, " ").trim()
const hex = (s) => [...new TextEncoder().encode(s)].map((b) => b.toString(16).padStart(2, "0")).join("")
async function search(query) {
const { build } = await fetch(`${BASE}/manifest.json`).then((r) => r.json())
const tree = await fetch(`${BASE}/${build}/tree.json`).then((r) => r.json())
const q = normalize(query)
const words = q.split(" ").filter(Boolean)
const real = words.filter((w) => !STOPWORDS.has(w))
const word = (real.length ? real : words).sort((a, b) => [...b].length - [...a].length)[0]
// Walk the tree: descend while the prefix is split; stop at a bucket or a summary.
let key = "", file
for (const c of word) {
if (tree[key + c]) { key += c; continue }
const starts = tree[key] ?? []
const start = starts.filter((s) => s <= c).pop() ?? starts[0] ?? c
file = `l/${hex(key + start)}.json`
break
}
file ??= `s/${hex(key)}.json`
const res = await fetch(`${BASE}/${build}/${file}`)
let entries = res.ok ? await res.json() : []
// Records changed since the build replace the index's entries for them.
// The query string changes every 5 minutes, so caches don't serve an old copy.
const recent = await fetch(`${BASE}/recent.json?w=${Math.floor(Date.now() / 300000)}`).then((r) => r.json())
entries = entries.filter(([type, id]) => !(`${type}:${id}` in recent.records))
entries.push(...Object.values(recent.records).flat())
const seen = new Set()
return entries
.filter((e) => { const n = normalize(e[2]).split(" "); return words.every((w) => n.some((x) => x.startsWith(w))) })
.sort((a, b) => Number(normalize(b[2]).startsWith(q)) - Number(normalize(a[2]).startsWith(q)) || b[3] - a[3])
.filter(([type, id]) => !seen.has(`${type}:${id}`) && seen.add(`${type}:${id}`))
.slice(0, 30)
}
// [["tv", 81189, "Breaking Bad", 999998, "breaking-bad", 2008, "/images/tv/81189/….t.jpg"], …]
console.log(await search("breaking bad"))Indeks ehitatakse uuesti iga päev. Ehituse failid ei muutu kunagi, seega võid neid vahemällu salvestada nii kauaks, kui soovid; muutuvad ainult manifest ja recent.json.