// import { useSWR } from "sswr"; import { useSWR } from "../util/fetch"; import { derived, Readable, readable, writable } from "svelte/store"; import type { IEntry, ListingResult, OrderedListing } from "upend/types"; import { listingAsOrdered } from "upend"; import LRU from "lru-cache"; export function useEntity( address: string | (() => string), condition?: () => Boolean ) { const { data, error, revalidate } = useSWR(() => condition === undefined || condition() ? `/api/obj/${typeof address === "string" ? address : address()}` : null ); const entries = derived(data, ($values) => $values ? listingAsOrdered($values) : [] ); const attributes = derived(entries, ($entries) => { const addr = typeof address === "string" ? address : address(); return $entries.filter(([_, e]) => e.entity === addr); }); const backlinks = derived(entries, ($entries) => { const addr = typeof address === "string" ? address : address(); return $entries.filter(([_, e]) => e.entity !== addr); }); return { entries, attributes, backlinks, data, error, revalidate, }; } export function query(query: () => string) { let queryString = typeof query === "string" ? query : query(); console.debug(`Querying: ${queryString}`); const { data, error, revalidate } = useSWR( () => `/api/obj?query=${query()}` ); const result = derived(data, ($values) => { return $values ? listingAsOrdered($values) : []; }); return { result, data, error, revalidate, }; } const queryOnceLRU = new LRU(128); const inFlightRequests: { [key: string]: Promise } = {}; export async function queryOnce(query: string): Promise { const cacheResult = queryOnceLRU.get(query); if (!cacheResult) { const url = `/api/obj?query=${query}`; let response; if (!inFlightRequests[url]) { console.debug(`Querying: ${query}`); inFlightRequests[url] = new Promise(async (resolve, reject) => { const response = await fetch(url); resolve(listingAsOrdered(await response.json())); }); } else { console.debug(`Chaining request for ${query}...`); } return await inFlightRequests[url]; } else { console.debug(`Returning cached: ${query}`); return cacheResult; } } interface EntityIdentification { type: string; value: string; } export async function identify( attributes: OrderedListing, backlinks: OrderedListing ): Promise { // Get all entries where the object is linked const hasEntries = backlinks .filter(([_, entry]) => entry.attribute === "HAS") .map(([addr, _]) => addr); // Out of those relations, retrieve their ALIAS attrs const hasAliases = hasEntries.length ? await queryOnce( `(matches (in ${hasEntries.map((e) => `"${e}"`).join(" ")}) "ALIAS" ?)` ) : []; const aliasValues = hasAliases.map(([_, entry]) => { return entry.value.c; }); // Get all identities of the object const isEntries = attributes .filter(([_, entry]) => entry.attribute === "IS") .map(([_, entry]) => entry.value.c); // Out of those, retrieve their TYPE_ID entries const typeIdListing = isEntries.length ? await queryOnce( `(matches (in ${isEntries.map((e) => `"${e}"`).join(" ")}) "TYPE_ID" ?)` ) : []; const typeIdAttributes = typeIdListing.map(([_, entry]) => { return [entry.entity, entry.value.c]; }); // Finally, filter own object's attributes according to TYPE_IDs // For each identity/TYPE_ID pair return typeIdAttributes .map(([type, attrName]) => { // And each associated TYPE_ID attribute... // return own matchin attributes return attributes .filter(([_, e]) => e.attribute === attrName) .map(([_, attr]) => { return { type, value: attr.value.c, }; }); }) .flat() .concat( aliasValues.map((value) => { return { type: "ALIAS", value, }; }) ); }