upend/ui/src/lib/entity.ts

139 lines
3.7 KiB
TypeScript
Raw Normal View History

2021-11-11 23:37:42 +01:00
// 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,
};
})
);
}