139 lines
3.7 KiB
TypeScript
139 lines
3.7 KiB
TypeScript
|
// 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<ListingResult, unknown>(() =>
|
||
|
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<ListingResult, unknown>(
|
||
|
() => `/api/obj?query=${query()}`
|
||
|
);
|
||
|
|
||
|
const result = derived(data, ($values) => {
|
||
|
return $values ? listingAsOrdered($values) : [];
|
||
|
});
|
||
|
|
||
|
return {
|
||
|
result,
|
||
|
data,
|
||
|
error,
|
||
|
revalidate,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
const queryOnceLRU = new LRU<string, OrderedListing>(128);
|
||
|
|
||
|
export async function queryOnce(query: string): Promise<OrderedListing> {
|
||
|
const cacheResult = queryOnceLRU.get(query);
|
||
|
if (!cacheResult) {
|
||
|
console.debug(`Querying: ${query}`);
|
||
|
const response = await fetch(`/api/obj?query=${query}`);
|
||
|
return listingAsOrdered(await response.json());
|
||
|
} else {
|
||
|
console.debug(`Returning cached: ${query}`);
|
||
|
return cacheResult;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
interface EntityIdentification {
|
||
|
type: string;
|
||
|
value: string;
|
||
|
}
|
||
|
|
||
|
export async function identify(
|
||
|
attributes: OrderedListing,
|
||
|
backlinks: OrderedListing
|
||
|
): Promise<EntityIdentification[]> {
|
||
|
// 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,
|
||
|
};
|
||
|
})
|
||
|
);
|
||
|
}
|