@undefineds.co/xpod 0.3.6 → 0.3.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/cli.json +1 -1
- package/config/cloud.json +54 -22
- package/config/local.json +56 -12
- package/config/resolver.json +10 -2
- package/config/xpod.base.json +50 -0
- package/config/xpod.json +8 -8
- package/dist/agents/config/resolve.js +10 -10
- package/dist/agents/config/resolve.js.map +1 -1
- package/dist/api/chatkit/index.d.ts +1 -1
- package/dist/api/chatkit/index.js.map +1 -1
- package/dist/api/chatkit/pod-store.d.ts +14 -11
- package/dist/api/chatkit/pod-store.js +114 -78
- package/dist/api/chatkit/pod-store.js.map +1 -1
- package/dist/api/chatkit/runtime/AcpAgentRuntime.js +1 -1
- package/dist/api/chatkit/runtime/AcpAgentRuntime.js.map +1 -1
- package/dist/api/chatkit/service.js +1 -1
- package/dist/api/chatkit/service.js.map +1 -1
- package/dist/api/chatkit/types.d.ts +11 -11
- package/dist/api/chatkit/types.js +3 -3
- package/dist/api/chatkit/types.js.map +1 -1
- package/dist/api/container/cloud.js +0 -8
- package/dist/api/container/cloud.js.map +1 -1
- package/dist/api/container/index.js +2 -1
- package/dist/api/container/index.js.map +1 -1
- package/dist/api/container/local.js +0 -7
- package/dist/api/container/local.js.map +1 -1
- package/dist/api/container/routes.js +3 -17
- package/dist/api/container/routes.js.map +1 -1
- package/dist/api/container/types.d.ts +0 -2
- package/dist/api/container/types.js.map +1 -1
- package/dist/api/handlers/PodManagementHandler.d.ts +3 -0
- package/dist/api/handlers/PodManagementHandler.js +71 -1
- package/dist/api/handlers/PodManagementHandler.js.map +1 -1
- package/dist/api/handlers/RunHandler.js +5 -5
- package/dist/api/handlers/RunHandler.js.map +1 -1
- package/dist/api/runs/AgentRuntimeTypes.d.ts +7 -8
- package/dist/api/runs/AgentRuntimeTypes.js.map +1 -1
- package/dist/api/runs/InngestRunExecutionBackend.d.ts +2 -2
- package/dist/api/runs/ManagedRunWorker.d.ts +1 -1
- package/dist/api/runs/ManagedRunWorker.js +6 -6
- package/dist/api/runs/ManagedRunWorker.js.map +1 -1
- package/dist/api/runs/PiAgentRuntimeDriver.d.ts +16 -1
- package/dist/api/runs/PiAgentRuntimeDriver.js +182 -23
- package/dist/api/runs/PiAgentRuntimeDriver.js.map +1 -1
- package/dist/api/runs/RunStateCenter.d.ts +3 -3
- package/dist/api/runs/RunStateCenter.js +13 -13
- package/dist/api/runs/RunStateCenter.js.map +1 -1
- package/dist/api/runs/store.d.ts +4 -4
- package/dist/api/runs/store.js +2 -2
- package/dist/api/runs/store.js.map +1 -1
- package/dist/api/service/VectorStoreService.d.ts +1 -1
- package/dist/api/service/VectorStoreService.js +16 -16
- package/dist/api/service/VectorStoreService.js.map +1 -1
- package/dist/api/tasks/InngestTaskScheduler.d.ts +4 -4
- package/dist/api/tasks/TaskMaterializer.d.ts +3 -3
- package/dist/api/tasks/TaskMaterializer.js +11 -11
- package/dist/api/tasks/TaskMaterializer.js.map +1 -1
- package/dist/api/tasks/TaskService.d.ts +3 -3
- package/dist/api/tasks/TaskService.js +11 -7
- package/dist/api/tasks/TaskService.js.map +1 -1
- package/dist/api/tasks/store.d.ts +10 -4
- package/dist/api/tasks/store.js +14 -4
- package/dist/api/tasks/store.js.map +1 -1
- package/dist/api/workspace/types.d.ts +3 -3
- package/dist/api/workspace/types.js +6 -6
- package/dist/api/workspace/types.js.map +1 -1
- package/dist/cli/commands/config.js +2 -2
- package/dist/cli/commands/config.js.map +1 -1
- package/dist/cli/commands/start.js +9 -3
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/components/components.jsonld +8 -2
- package/dist/components/context.jsonld +308 -51
- package/dist/http/search/SearchHttpHandler.js +8 -8
- package/dist/http/search/SearchHttpHandler.js.map +1 -1
- package/dist/identity/drizzle/PodLookupRepository.d.ts +11 -1
- package/dist/identity/drizzle/PodLookupRepository.js +95 -4
- package/dist/identity/drizzle/PodLookupRepository.js.map +1 -1
- package/dist/identity/drizzle/db.js +4 -43
- package/dist/identity/drizzle/db.js.map +1 -1
- package/dist/identity/drizzle/schema.pg.d.ts +0 -5
- package/dist/identity/drizzle/schema.pg.js +2 -16
- package/dist/identity/drizzle/schema.pg.js.map +1 -1
- package/dist/identity/drizzle/schema.sqlite.d.ts +19 -176
- package/dist/identity/drizzle/schema.sqlite.js +2 -16
- package/dist/identity/drizzle/schema.sqlite.js.map +1 -1
- package/dist/identity/oidc/AutoDetectIdentityProviderHandler.d.ts +4 -4
- package/dist/identity/oidc/AutoDetectIdentityProviderHandler.js +7 -7
- package/dist/identity/oidc/AutoDetectIdentityProviderHandler.js.map +1 -1
- package/dist/identity/oidc/AutoDetectIdentityProviderHandler.jsonld +6 -6
- package/dist/identity/oidc/AutoDetectOidcHandler.d.ts +4 -4
- package/dist/identity/oidc/AutoDetectOidcHandler.js +6 -6
- package/dist/identity/oidc/AutoDetectOidcHandler.js.map +1 -1
- package/dist/identity/oidc/AutoDetectOidcHandler.jsonld +6 -6
- package/dist/identity/oidc/ScopedPickWebIdHandler.d.ts +37 -0
- package/dist/identity/oidc/ScopedPickWebIdHandler.js +211 -0
- package/dist/identity/oidc/ScopedPickWebIdHandler.js.map +1 -0
- package/dist/identity/oidc/ScopedPickWebIdHandler.jsonld +158 -0
- package/dist/index.d.ts +12 -2
- package/dist/index.js +16 -4
- package/dist/index.js.map +1 -1
- package/dist/main.js +8 -2
- package/dist/main.js.map +1 -1
- package/dist/provision/ProvisionPodCreator.d.ts +3 -4
- package/dist/provision/ProvisionPodCreator.js +8 -13
- package/dist/provision/ProvisionPodCreator.js.map +1 -1
- package/dist/provision/ProvisionPodCreator.jsonld +7 -7
- package/dist/runtime/Proxy.d.ts +0 -1
- package/dist/runtime/Proxy.js +0 -9
- package/dist/runtime/Proxy.js.map +1 -1
- package/dist/runtime/bootstrap.d.ts +1 -0
- package/dist/runtime/bootstrap.js +5 -2
- package/dist/runtime/bootstrap.js.map +1 -1
- package/dist/runtime/css-process.d.ts +12 -4
- package/dist/runtime/css-process.js +61 -14
- package/dist/runtime/css-process.js.map +1 -1
- package/dist/runtime/oidc-issuer.d.ts +3 -2
- package/dist/runtime/oidc-issuer.js +3 -2
- package/dist/runtime/oidc-issuer.js.map +1 -1
- package/dist/runtime/runtime-types.d.ts +1 -0
- package/dist/runtime/runtime-types.js.map +1 -1
- package/dist/solidfs/LocalFirstRdfRepresentationResolver.d.ts +21 -0
- package/dist/solidfs/LocalFirstRdfRepresentationResolver.js +38 -0
- package/dist/solidfs/LocalFirstRdfRepresentationResolver.js.map +1 -0
- package/dist/solidfs/LocalSolidFS.d.ts +18 -0
- package/dist/solidfs/LocalSolidFS.js +539 -0
- package/dist/solidfs/LocalSolidFS.js.map +1 -0
- package/dist/solidfs/PodSolidFsHttpClient.d.ts +16 -0
- package/dist/solidfs/PodSolidFsHttpClient.js +93 -0
- package/dist/solidfs/PodSolidFsHttpClient.js.map +1 -0
- package/dist/solidfs/PodSolidFsHydrator.d.ts +27 -0
- package/dist/solidfs/PodSolidFsHydrator.js +127 -0
- package/dist/solidfs/PodSolidFsHydrator.js.map +1 -0
- package/dist/solidfs/PodSolidFsSyncer.d.ts +21 -0
- package/dist/solidfs/PodSolidFsSyncer.js +78 -0
- package/dist/solidfs/PodSolidFsSyncer.js.map +1 -0
- package/dist/solidfs/RdfIndexSolidFsSyncer.d.ts +22 -0
- package/dist/solidfs/RdfIndexSolidFsSyncer.js +131 -0
- package/dist/solidfs/RdfIndexSolidFsSyncer.js.map +1 -0
- package/dist/solidfs/index.d.ts +7 -0
- package/dist/solidfs/index.js +24 -0
- package/dist/solidfs/index.js.map +1 -0
- package/dist/solidfs/types.d.ts +131 -0
- package/dist/solidfs/types.js +19 -0
- package/dist/solidfs/types.js.map +1 -0
- package/dist/storage/RepresentationPartialConvertingStore.js +6 -13
- package/dist/storage/RepresentationPartialConvertingStore.js.map +1 -1
- package/dist/storage/SparqlUpdateResourceStore.d.ts +4 -0
- package/dist/storage/SparqlUpdateResourceStore.js +13 -0
- package/dist/storage/SparqlUpdateResourceStore.js.map +1 -1
- package/dist/storage/SparqlUpdateResourceStore.jsonld +26 -0
- package/dist/storage/accessors/MixDataAccessor.d.ts +85 -4
- package/dist/storage/accessors/MixDataAccessor.js +511 -16
- package/dist/storage/accessors/MixDataAccessor.js.map +1 -1
- package/dist/storage/accessors/MixDataAccessor.jsonld +176 -1
- package/dist/storage/accessors/QuintStoreSparqlDataAccessor.d.ts +7 -0
- package/dist/storage/accessors/QuintStoreSparqlDataAccessor.js +72 -4
- package/dist/storage/accessors/QuintStoreSparqlDataAccessor.js.map +1 -1
- package/dist/storage/accessors/QuintStoreSparqlDataAccessor.jsonld +24 -0
- package/dist/storage/quint/BaseQuintStore.d.ts +3 -0
- package/dist/storage/quint/BaseQuintStore.js +51 -27
- package/dist/storage/quint/BaseQuintStore.js.map +1 -1
- package/dist/storage/quint/PgQuintStore.d.ts +1 -0
- package/dist/storage/quint/PgQuintStore.js +50 -32
- package/dist/storage/quint/PgQuintStore.js.map +1 -1
- package/dist/storage/quint/PgQuintStore.jsonld +4 -3
- package/dist/storage/quint/SqliteQuintStore.d.ts +5 -0
- package/dist/storage/quint/SqliteQuintStore.js +100 -0
- package/dist/storage/quint/SqliteQuintStore.js.map +1 -1
- package/dist/storage/quint/SqliteQuintStore.jsonld +20 -0
- package/dist/storage/quint/types.d.ts +16 -0
- package/dist/storage/quint/types.js.map +1 -1
- package/dist/storage/rdf/Rdf3xTripleIndex.d.ts +55 -0
- package/dist/storage/rdf/Rdf3xTripleIndex.js +1235 -0
- package/dist/storage/rdf/Rdf3xTripleIndex.js.map +1 -0
- package/dist/storage/rdf/RdfContentTypes.d.ts +9 -0
- package/dist/storage/rdf/RdfContentTypes.js +79 -0
- package/dist/storage/rdf/RdfContentTypes.js.map +1 -0
- package/dist/storage/rdf/RdfLocalQueryEngine.d.ts +79 -0
- package/dist/storage/rdf/RdfLocalQueryEngine.js +2705 -0
- package/dist/storage/rdf/RdfLocalQueryEngine.js.map +1 -0
- package/dist/storage/rdf/RdfQuadIndex.d.ts +98 -0
- package/dist/storage/rdf/RdfQuadIndex.js +1840 -0
- package/dist/storage/rdf/RdfQuadIndex.js.map +1 -0
- package/dist/storage/rdf/RdfQuadIndex.jsonld +416 -0
- package/dist/storage/rdf/RdfShadowComparator.d.ts +12 -0
- package/dist/storage/rdf/RdfShadowComparator.js +47 -0
- package/dist/storage/rdf/RdfShadowComparator.js.map +1 -0
- package/dist/storage/rdf/RdfSparqlAdapter.d.ts +147 -0
- package/dist/storage/rdf/RdfSparqlAdapter.js +2420 -0
- package/dist/storage/rdf/RdfSparqlAdapter.js.map +1 -0
- package/dist/storage/rdf/RdfSparqlAdapter.jsonld +414 -0
- package/dist/storage/rdf/RdfTermDictionary.d.ts +27 -0
- package/dist/storage/rdf/RdfTermDictionary.js +352 -0
- package/dist/storage/rdf/RdfTermDictionary.js.map +1 -0
- package/dist/storage/rdf/RdfTermDictionary.jsonld +114 -0
- package/dist/storage/rdf/RdfTermSemantics.d.ts +6 -0
- package/dist/storage/rdf/RdfTermSemantics.js +40 -0
- package/dist/storage/rdf/RdfTermSemantics.js.map +1 -0
- package/dist/storage/rdf/RdfTextIndex.d.ts +23 -0
- package/dist/storage/rdf/RdfTextIndex.js +569 -0
- package/dist/storage/rdf/RdfTextIndex.js.map +1 -0
- package/dist/storage/rdf/RdfVectorIndex.d.ts +22 -0
- package/dist/storage/rdf/RdfVectorIndex.js +631 -0
- package/dist/storage/rdf/RdfVectorIndex.js.map +1 -0
- package/dist/storage/rdf/RdfXmlSerializer.d.ts +2 -0
- package/dist/storage/rdf/RdfXmlSerializer.js +123 -0
- package/dist/storage/rdf/RdfXmlSerializer.js.map +1 -0
- package/dist/storage/rdf/ShadowRdfQuintStore.d.ts +58 -0
- package/dist/storage/rdf/ShadowRdfQuintStore.js +202 -0
- package/dist/storage/rdf/ShadowRdfQuintStore.js.map +1 -0
- package/dist/storage/rdf/ShadowRdfQuintStore.jsonld +308 -0
- package/dist/storage/rdf/SolidRdfEngine.d.ts +56 -0
- package/dist/storage/rdf/SolidRdfEngine.js +292 -0
- package/dist/storage/rdf/SolidRdfEngine.js.map +1 -0
- package/dist/storage/rdf/SolidRdfEngine.jsonld +372 -0
- package/dist/storage/rdf/SolidRdfSparqlEngine.d.ts +92 -0
- package/dist/storage/rdf/SolidRdfSparqlEngine.js +477 -0
- package/dist/storage/rdf/SolidRdfSparqlEngine.js.map +1 -0
- package/dist/storage/rdf/SolidRdfSparqlEngine.jsonld +257 -0
- package/dist/storage/rdf/index.d.ts +15 -0
- package/dist/storage/rdf/index.js +61 -0
- package/dist/storage/rdf/index.js.map +1 -0
- package/dist/storage/rdf/models-benchmark.d.ts +260 -0
- package/dist/storage/rdf/models-benchmark.js +1405 -0
- package/dist/storage/rdf/models-benchmark.js.map +1 -0
- package/dist/storage/rdf/types.d.ts +726 -0
- package/dist/storage/rdf/types.js +3 -0
- package/dist/storage/rdf/types.js.map +1 -0
- package/dist/storage/rdf/types.jsonld +316 -0
- package/dist/storage/vector/VectorIndexingListener.d.ts +5 -5
- package/dist/storage/vector/VectorIndexingListener.js +19 -19
- package/dist/storage/vector/VectorIndexingListener.js.map +1 -1
- package/package.json +3 -2
- package/templates/pod/acp/.acr.hbs +39 -0
- package/templates/pod/acp/README.acr +18 -0
- package/templates/pod/acp/profile/card.acr +22 -0
- package/templates/pod/base/README$.md.hbs +27 -0
- package/templates/pod/base/profile/card$.ttl.hbs +13 -0
- package/templates/pod/wac/.acl.hbs +26 -0
- package/templates/pod/wac/README.acl.hbs +14 -0
- package/templates/pod/wac/profile/card.acl.hbs +19 -0
- package/dist/api/handlers/WebIdProfileHandler.d.ts +0 -16
- package/dist/api/handlers/WebIdProfileHandler.js +0 -423
- package/dist/api/handlers/WebIdProfileHandler.js.map +0 -1
- package/dist/identity/drizzle/WebIdProfileRepository.d.ts +0 -63
- package/dist/identity/drizzle/WebIdProfileRepository.js +0 -168
- package/dist/identity/drizzle/WebIdProfileRepository.js.map +0 -1
- package/dist/identity/drizzle/WebIdProfileRepository.jsonld +0 -112
- package/dist/storage/quint/BaseQuintStore.jsonld +0 -257
|
@@ -0,0 +1,1405 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.rdfModelsLocalQueryBenchmarkCases = exports.rdfModelsBenchmarkCases = exports.RDF_MODELS_SYNTHETIC_MESSAGE_QUADS = void 0;
|
|
4
|
+
exports.rdfModelsBenchmarkCaseNames = rdfModelsBenchmarkCaseNames;
|
|
5
|
+
exports.rdfModelsLocalQueryBenchmarkCaseNames = rdfModelsLocalQueryBenchmarkCaseNames;
|
|
6
|
+
exports.rdfModelsBenchmarkScaleTargetQuads = rdfModelsBenchmarkScaleTargetQuads;
|
|
7
|
+
exports.rdfModelsBenchmarkSyntheticPodCount = rdfModelsBenchmarkSyntheticPodCount;
|
|
8
|
+
exports.estimateRdfModelsSyntheticQuadCount = estimateRdfModelsSyntheticQuadCount;
|
|
9
|
+
exports.defaultSyntheticMessagesForRdfModelsScale = defaultSyntheticMessagesForRdfModelsScale;
|
|
10
|
+
exports.rdfModelsBenchmarkScaleSatisfied = rdfModelsBenchmarkScaleSatisfied;
|
|
11
|
+
exports.runRdfModelsBenchmark = runRdfModelsBenchmark;
|
|
12
|
+
exports.runRdfModelsShadowBenchmark = runRdfModelsShadowBenchmark;
|
|
13
|
+
exports.runRdfModelsRdf3xShadowBenchmark = runRdfModelsRdf3xShadowBenchmark;
|
|
14
|
+
const node_crypto_1 = require("node:crypto");
|
|
15
|
+
const n3_1 = require("n3");
|
|
16
|
+
const n3_2 = require("n3");
|
|
17
|
+
const types_1 = require("../quint/types");
|
|
18
|
+
const RdfShadowComparator_1 = require("./RdfShadowComparator");
|
|
19
|
+
const RdfTermSemantics_1 = require("./RdfTermSemantics");
|
|
20
|
+
const { namedNode, literal } = n3_1.DataFactory;
|
|
21
|
+
exports.RDF_MODELS_SYNTHETIC_MESSAGE_QUADS = 4;
|
|
22
|
+
const RDF_MODELS_SCALE_TARGET_QUADS = {
|
|
23
|
+
small: 48,
|
|
24
|
+
medium: 10_000,
|
|
25
|
+
large: 1_000_000,
|
|
26
|
+
};
|
|
27
|
+
const RDF_MODELS_SYNTHETIC_POD_COUNTS = {
|
|
28
|
+
small: 1,
|
|
29
|
+
medium: 1,
|
|
30
|
+
large: 4,
|
|
31
|
+
};
|
|
32
|
+
const RDF_TYPE = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type';
|
|
33
|
+
const DCT_CREATED = 'http://purl.org/dc/terms/created';
|
|
34
|
+
const DCT_MODIFIED = 'http://purl.org/dc/terms/modified';
|
|
35
|
+
const DCT_TITLE = 'http://purl.org/dc/terms/title';
|
|
36
|
+
const SIOC_CONTENT = 'http://rdfs.org/sioc/ns#content';
|
|
37
|
+
const SIOC_HAS_MEMBER = 'http://rdfs.org/sioc/ns#has_member';
|
|
38
|
+
const UDFS = 'https://undefineds.co/ns#';
|
|
39
|
+
const MEETING = 'http://www.w3.org/ns/pim/meeting#';
|
|
40
|
+
const SIOC = 'http://rdfs.org/sioc/ns#';
|
|
41
|
+
const FOAF_AGENT = 'http://xmlns.com/foaf/0.1/Agent';
|
|
42
|
+
const VCARD_INDIVIDUAL = 'http://www.w3.org/2006/vcard/ns#Individual';
|
|
43
|
+
const SCHEMA_CREATIVE_WORK = 'http://schema.org/CreativeWork';
|
|
44
|
+
const PERFORMANCE_P95_MIN_ABSOLUTE_HEADROOM_MS = 25;
|
|
45
|
+
const PERFORMANCE_P95_MAX_RATIO = 8;
|
|
46
|
+
exports.rdfModelsBenchmarkCases = [
|
|
47
|
+
{
|
|
48
|
+
name: 'list chats',
|
|
49
|
+
resource: 'chat',
|
|
50
|
+
purpose: 'surface list with graph-scope and type filter',
|
|
51
|
+
minScale: 'small',
|
|
52
|
+
query: {
|
|
53
|
+
pattern: {
|
|
54
|
+
predicate: namedNode(RDF_TYPE),
|
|
55
|
+
object: namedNode(`${MEETING}LongChat`),
|
|
56
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/chat/' },
|
|
57
|
+
},
|
|
58
|
+
options: { order: ['subject'], limit: 50 },
|
|
59
|
+
},
|
|
60
|
+
expectedPlan: ['graph-scope', 'type-filter', 'limit'],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'list tasks',
|
|
64
|
+
resource: 'task',
|
|
65
|
+
purpose: 'task surface list with status/type filtering',
|
|
66
|
+
minScale: 'small',
|
|
67
|
+
query: {
|
|
68
|
+
pattern: {
|
|
69
|
+
predicate: namedNode(RDF_TYPE),
|
|
70
|
+
object: namedNode(`${UDFS}Task`),
|
|
71
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/task/' },
|
|
72
|
+
},
|
|
73
|
+
options: { order: ['subject'], limit: 50 },
|
|
74
|
+
},
|
|
75
|
+
expectedPlan: ['graph-scope', 'type-filter', 'limit'],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'list threads by chat',
|
|
79
|
+
resource: 'thread',
|
|
80
|
+
purpose: 'relation lookup under a chat index graph',
|
|
81
|
+
minScale: 'small',
|
|
82
|
+
query: {
|
|
83
|
+
pattern: {
|
|
84
|
+
predicate: namedNode(RDF_TYPE),
|
|
85
|
+
object: namedNode(`${SIOC}Thread`),
|
|
86
|
+
graph: namedNode('https://pod.example/alice/.data/chat/default/index.ttl'),
|
|
87
|
+
},
|
|
88
|
+
options: { order: ['subject'], limit: 100 },
|
|
89
|
+
},
|
|
90
|
+
expectedPlan: ['graph-scope', 'type-filter', 'limit'],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: 'list threads by task',
|
|
94
|
+
resource: 'thread',
|
|
95
|
+
purpose: 'relation lookup under a task index graph',
|
|
96
|
+
minScale: 'small',
|
|
97
|
+
query: {
|
|
98
|
+
pattern: {
|
|
99
|
+
predicate: namedNode(RDF_TYPE),
|
|
100
|
+
object: namedNode(`${SIOC}Thread`),
|
|
101
|
+
graph: namedNode('https://pod.example/alice/.data/task/default/index.ttl'),
|
|
102
|
+
},
|
|
103
|
+
options: { order: ['subject'], limit: 100 },
|
|
104
|
+
},
|
|
105
|
+
expectedPlan: ['graph-scope', 'type-filter', 'limit'],
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: 'list messages by thread',
|
|
109
|
+
resource: 'message',
|
|
110
|
+
purpose: 'date-bucketed message lookup through thread inverse membership',
|
|
111
|
+
minScale: 'small',
|
|
112
|
+
query: {
|
|
113
|
+
pattern: {
|
|
114
|
+
predicate: namedNode(SIOC_HAS_MEMBER),
|
|
115
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/chat/default/2026/05/' },
|
|
116
|
+
},
|
|
117
|
+
options: { order: ['subject'], limit: 100 },
|
|
118
|
+
},
|
|
119
|
+
expectedPlan: ['graph-scope', 'predicate-filter', 'limit'],
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: 'latest message',
|
|
123
|
+
resource: 'message',
|
|
124
|
+
purpose: 'ORDER BY + LIMIT over message date bucket',
|
|
125
|
+
minScale: 'small',
|
|
126
|
+
query: {
|
|
127
|
+
pattern: {
|
|
128
|
+
predicate: namedNode(DCT_CREATED),
|
|
129
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/chat/default/' },
|
|
130
|
+
},
|
|
131
|
+
options: { order: ['object'], reverse: true, limit: 1 },
|
|
132
|
+
},
|
|
133
|
+
expectedPlan: ['graph-scope', 'predicate-filter', 'order', 'limit'],
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: 'latest run',
|
|
137
|
+
resource: 'run',
|
|
138
|
+
purpose: 'ORDER BY + LIMIT over date-bucketed run documents',
|
|
139
|
+
minScale: 'small',
|
|
140
|
+
query: {
|
|
141
|
+
pattern: {
|
|
142
|
+
predicate: namedNode(DCT_CREATED),
|
|
143
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/task/default/' },
|
|
144
|
+
},
|
|
145
|
+
options: { order: ['object'], reverse: true, limit: 1 },
|
|
146
|
+
},
|
|
147
|
+
expectedPlan: ['graph-scope', 'predicate-filter', 'order', 'limit'],
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
name: 'pending runs',
|
|
151
|
+
resource: 'run',
|
|
152
|
+
purpose: 'status filter for scheduler and state center',
|
|
153
|
+
minScale: 'small',
|
|
154
|
+
query: {
|
|
155
|
+
pattern: {
|
|
156
|
+
predicate: namedNode(`${UDFS}status`),
|
|
157
|
+
object: literal('queued'),
|
|
158
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/task/' },
|
|
159
|
+
},
|
|
160
|
+
options: { order: ['subject'], limit: 100 },
|
|
161
|
+
},
|
|
162
|
+
expectedPlan: ['graph-scope', 'predicate-object-filter', 'limit'],
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: 'running runs',
|
|
166
|
+
resource: 'run',
|
|
167
|
+
purpose: 'status filter for active runtime execution',
|
|
168
|
+
minScale: 'small',
|
|
169
|
+
query: {
|
|
170
|
+
pattern: {
|
|
171
|
+
predicate: namedNode(`${UDFS}status`),
|
|
172
|
+
object: literal('running'),
|
|
173
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/task/' },
|
|
174
|
+
},
|
|
175
|
+
options: { order: ['subject'], limit: 100 },
|
|
176
|
+
},
|
|
177
|
+
expectedPlan: ['graph-scope', 'predicate-object-filter', 'limit'],
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: 'runs by workspace',
|
|
181
|
+
resource: 'run',
|
|
182
|
+
purpose: 'workspace relation filter for runtime placement and steering',
|
|
183
|
+
minScale: 'small',
|
|
184
|
+
query: {
|
|
185
|
+
pattern: {
|
|
186
|
+
predicate: namedNode(`${UDFS}workspace`),
|
|
187
|
+
object: namedNode('file://macbook.local/Users/alice/project/'),
|
|
188
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/task/default/' },
|
|
189
|
+
},
|
|
190
|
+
options: { order: ['subject'], limit: 100 },
|
|
191
|
+
},
|
|
192
|
+
expectedPlan: ['graph-scope', 'predicate-object-filter', 'limit'],
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: 'runs by numeric priority',
|
|
196
|
+
resource: 'run',
|
|
197
|
+
purpose: 'typed numeric literal range filter for scheduler priority queues',
|
|
198
|
+
minScale: 'small',
|
|
199
|
+
query: {
|
|
200
|
+
pattern: {
|
|
201
|
+
predicate: namedNode(`${UDFS}priority`),
|
|
202
|
+
object: { $gt: literal('9', namedNode('http://www.w3.org/2001/XMLSchema#integer')) },
|
|
203
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/task/' },
|
|
204
|
+
},
|
|
205
|
+
options: { order: ['subject'], limit: 100 },
|
|
206
|
+
},
|
|
207
|
+
expectedPlan: ['graph-scope', 'predicate-object-range-filter', 'limit'],
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
name: 'run with steps',
|
|
211
|
+
resource: 'runStep',
|
|
212
|
+
purpose: 'one-to-many run-step relation lookup',
|
|
213
|
+
minScale: 'small',
|
|
214
|
+
query: {
|
|
215
|
+
pattern: {
|
|
216
|
+
predicate: namedNode(`${UDFS}run`),
|
|
217
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/task/default/2026/05/' },
|
|
218
|
+
},
|
|
219
|
+
options: { order: ['subject'], limit: 200 },
|
|
220
|
+
},
|
|
221
|
+
expectedPlan: ['graph-scope', 'predicate-filter', 'limit'],
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
name: 'task materialization due time',
|
|
225
|
+
resource: 'schedule',
|
|
226
|
+
purpose: 'schedule due-time candidate lookup',
|
|
227
|
+
minScale: 'medium',
|
|
228
|
+
query: {
|
|
229
|
+
pattern: {
|
|
230
|
+
predicate: namedNode(`${UDFS}nextRunAt`),
|
|
231
|
+
object: { $lte: literal('2026-05-18T01:30:00.000Z') },
|
|
232
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/task/' },
|
|
233
|
+
},
|
|
234
|
+
options: { order: ['object'], limit: 100 },
|
|
235
|
+
},
|
|
236
|
+
expectedPlan: ['graph-scope', 'predicate-object-range-filter', 'order', 'limit'],
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
name: 'search message literals',
|
|
240
|
+
resource: 'message',
|
|
241
|
+
purpose: 'literal/text index candidate that reconnects to RDF subjects',
|
|
242
|
+
minScale: 'medium',
|
|
243
|
+
query: {
|
|
244
|
+
pattern: {
|
|
245
|
+
predicate: namedNode(SIOC_CONTENT),
|
|
246
|
+
object: { $contains: 'searchable' },
|
|
247
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/chat/' },
|
|
248
|
+
},
|
|
249
|
+
options: { order: ['subject'], limit: 50 },
|
|
250
|
+
},
|
|
251
|
+
expectedPlan: ['text-index', 'rdf-subject-join'],
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
name: 'load by exact id',
|
|
255
|
+
resource: 'any',
|
|
256
|
+
purpose: 'base-relative id expands to exact subject IRI',
|
|
257
|
+
minScale: 'small',
|
|
258
|
+
query: {
|
|
259
|
+
pattern: {
|
|
260
|
+
subject: namedNode('https://pod.example/alice/.data/chat/default/index.ttl#this'),
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
expectedPlan: ['SPOG'],
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
name: 'acl graph prefix scoped query',
|
|
267
|
+
resource: 'any',
|
|
268
|
+
purpose: 'scope filter must avoid unbounded full-pod scans',
|
|
269
|
+
minScale: 'medium',
|
|
270
|
+
query: {
|
|
271
|
+
pattern: {
|
|
272
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/chat/default/' },
|
|
273
|
+
predicate: namedNode(DCT_MODIFIED),
|
|
274
|
+
},
|
|
275
|
+
options: { order: ['subject'], limit: 100 },
|
|
276
|
+
},
|
|
277
|
+
expectedPlan: ['graph-scope', 'predicate-filter', 'limit'],
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
name: 'list providers',
|
|
281
|
+
resource: 'aiProvider',
|
|
282
|
+
purpose: 'AI provider settings list and provider/model relation baseline',
|
|
283
|
+
minScale: 'small',
|
|
284
|
+
query: {
|
|
285
|
+
pattern: {
|
|
286
|
+
predicate: namedNode(RDF_TYPE),
|
|
287
|
+
object: namedNode(`${UDFS}Provider`),
|
|
288
|
+
graph: { $startsWith: 'https://pod.example/alice/settings/providers/' },
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
expectedPlan: ['graph-scope', 'type-filter'],
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
name: 'models by provider',
|
|
295
|
+
resource: 'aiModel',
|
|
296
|
+
purpose: 'AI model lookup by provider relation',
|
|
297
|
+
minScale: 'small',
|
|
298
|
+
query: {
|
|
299
|
+
pattern: {
|
|
300
|
+
predicate: namedNode(`${UDFS}isProvidedBy`),
|
|
301
|
+
object: namedNode('https://pod.example/alice/settings/providers/anthropic.ttl'),
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
expectedPlan: ['POSG'],
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
name: 'credentials by provider',
|
|
308
|
+
resource: 'credential',
|
|
309
|
+
purpose: 'credential lookup by provider relation',
|
|
310
|
+
minScale: 'small',
|
|
311
|
+
query: {
|
|
312
|
+
pattern: {
|
|
313
|
+
predicate: namedNode(`${UDFS}provider`),
|
|
314
|
+
object: namedNode('https://pod.example/alice/settings/providers/anthropic.ttl'),
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
expectedPlan: ['POSG'],
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
name: 'list agents',
|
|
321
|
+
resource: 'agent',
|
|
322
|
+
purpose: 'agent identity list under the shared models agent resource base',
|
|
323
|
+
minScale: 'small',
|
|
324
|
+
query: {
|
|
325
|
+
pattern: {
|
|
326
|
+
predicate: namedNode(RDF_TYPE),
|
|
327
|
+
object: namedNode(FOAF_AGENT),
|
|
328
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/agents/' },
|
|
329
|
+
},
|
|
330
|
+
options: { order: ['subject'], limit: 50 },
|
|
331
|
+
},
|
|
332
|
+
expectedPlan: ['graph-scope', 'type-filter', 'limit'],
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
name: 'list contacts',
|
|
336
|
+
resource: 'contact',
|
|
337
|
+
purpose: 'contact index list under the shared models contact resource base',
|
|
338
|
+
minScale: 'small',
|
|
339
|
+
query: {
|
|
340
|
+
pattern: {
|
|
341
|
+
predicate: namedNode(RDF_TYPE),
|
|
342
|
+
object: namedNode(VCARD_INDIVIDUAL),
|
|
343
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/contacts/' },
|
|
344
|
+
},
|
|
345
|
+
options: { order: ['subject'], limit: 50 },
|
|
346
|
+
},
|
|
347
|
+
expectedPlan: ['graph-scope', 'type-filter', 'limit'],
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
name: 'list favorites',
|
|
351
|
+
resource: 'favorite',
|
|
352
|
+
purpose: 'favorite list over date-bucketed favorite documents',
|
|
353
|
+
minScale: 'small',
|
|
354
|
+
query: {
|
|
355
|
+
pattern: {
|
|
356
|
+
predicate: namedNode(RDF_TYPE),
|
|
357
|
+
object: namedNode(SCHEMA_CREATIVE_WORK),
|
|
358
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/favorites/' },
|
|
359
|
+
},
|
|
360
|
+
options: { order: ['subject'], limit: 50 },
|
|
361
|
+
},
|
|
362
|
+
expectedPlan: ['graph-scope', 'type-filter', 'limit'],
|
|
363
|
+
},
|
|
364
|
+
];
|
|
365
|
+
exports.rdfModelsLocalQueryBenchmarkCases = [
|
|
366
|
+
{
|
|
367
|
+
name: 'latest message by thread local query',
|
|
368
|
+
resource: 'message',
|
|
369
|
+
purpose: 'date-bucketed message timeline keeps ORDER BY/LIMIT inside SQL self-join',
|
|
370
|
+
minScale: 'small',
|
|
371
|
+
query: {
|
|
372
|
+
patterns: [
|
|
373
|
+
{
|
|
374
|
+
subject: { variable: 'message' },
|
|
375
|
+
predicate: namedNode(SIOC_HAS_MEMBER),
|
|
376
|
+
object: namedNode('https://pod.example/alice/.data/chat/default/index.ttl#thread_1'),
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/chat/default/' },
|
|
380
|
+
subject: { variable: 'message' },
|
|
381
|
+
predicate: namedNode(DCT_CREATED),
|
|
382
|
+
object: { variable: 'createdAt' },
|
|
383
|
+
},
|
|
384
|
+
],
|
|
385
|
+
select: ['message', 'createdAt'],
|
|
386
|
+
orderBy: [
|
|
387
|
+
{
|
|
388
|
+
variable: 'createdAt',
|
|
389
|
+
direction: 'desc',
|
|
390
|
+
},
|
|
391
|
+
],
|
|
392
|
+
limit: 1,
|
|
393
|
+
},
|
|
394
|
+
expectedPlan: ['join-index', 'join-order-pushdown', 'join-limit-pushdown'],
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
name: 'next queued run by workspace local query',
|
|
398
|
+
resource: 'run',
|
|
399
|
+
purpose: 'run state center scheduler query keeps status/workspace/date joins in SQL before LIMIT',
|
|
400
|
+
minScale: 'small',
|
|
401
|
+
query: {
|
|
402
|
+
patterns: [
|
|
403
|
+
{
|
|
404
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/task/default/' },
|
|
405
|
+
subject: { variable: 'run' },
|
|
406
|
+
predicate: namedNode(`${UDFS}status`),
|
|
407
|
+
object: literal('queued'),
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/task/default/' },
|
|
411
|
+
subject: { variable: 'run' },
|
|
412
|
+
predicate: namedNode(`${UDFS}workspace`),
|
|
413
|
+
object: namedNode('file://macbook.local/Users/alice/project/'),
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/task/default/' },
|
|
417
|
+
subject: { variable: 'run' },
|
|
418
|
+
predicate: namedNode(DCT_CREATED),
|
|
419
|
+
object: { variable: 'createdAt' },
|
|
420
|
+
},
|
|
421
|
+
],
|
|
422
|
+
select: ['run', 'createdAt'],
|
|
423
|
+
orderBy: [
|
|
424
|
+
{
|
|
425
|
+
variable: 'createdAt',
|
|
426
|
+
direction: 'asc',
|
|
427
|
+
},
|
|
428
|
+
],
|
|
429
|
+
limit: 1,
|
|
430
|
+
},
|
|
431
|
+
expectedPlan: ['join-index', 'join-order-pushdown', 'join-limit-pushdown'],
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
name: 'run steps by run local query',
|
|
435
|
+
resource: 'runStep',
|
|
436
|
+
purpose: 'one-to-many run-step lookup keeps type and run relation in SQL self-join',
|
|
437
|
+
minScale: 'small',
|
|
438
|
+
query: {
|
|
439
|
+
patterns: [
|
|
440
|
+
{
|
|
441
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/task/default/2026/05/' },
|
|
442
|
+
subject: { variable: 'step' },
|
|
443
|
+
predicate: namedNode(RDF_TYPE),
|
|
444
|
+
object: namedNode(`${UDFS}RunStep`),
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/task/default/2026/05/' },
|
|
448
|
+
subject: { variable: 'step' },
|
|
449
|
+
predicate: namedNode(`${UDFS}run`),
|
|
450
|
+
object: namedNode('https://pod.example/alice/.data/task/default/2026/05/18/runs.ttl#run_1'),
|
|
451
|
+
},
|
|
452
|
+
],
|
|
453
|
+
select: ['step'],
|
|
454
|
+
orderBy: [
|
|
455
|
+
{
|
|
456
|
+
variable: 'step',
|
|
457
|
+
direction: 'asc',
|
|
458
|
+
},
|
|
459
|
+
],
|
|
460
|
+
limit: 50,
|
|
461
|
+
},
|
|
462
|
+
expectedPlan: ['join-index', 'join-order-pushdown', 'join-limit-pushdown'],
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
name: 'task materialization active due local query',
|
|
466
|
+
resource: 'schedule',
|
|
467
|
+
purpose: 'task scheduler materialization keeps active status and due-time filter in SQL self-join',
|
|
468
|
+
minScale: 'small',
|
|
469
|
+
query: {
|
|
470
|
+
patterns: [
|
|
471
|
+
{
|
|
472
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/task/default/' },
|
|
473
|
+
subject: { variable: 'schedule' },
|
|
474
|
+
predicate: namedNode(RDF_TYPE),
|
|
475
|
+
object: namedNode(`${UDFS}Schedule`),
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/task/default/' },
|
|
479
|
+
subject: { variable: 'schedule' },
|
|
480
|
+
predicate: namedNode(`${UDFS}status`),
|
|
481
|
+
object: literal('active'),
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
graph: { $startsWith: 'https://pod.example/alice/.data/task/default/' },
|
|
485
|
+
subject: { variable: 'schedule' },
|
|
486
|
+
predicate: namedNode(`${UDFS}nextRunAt`),
|
|
487
|
+
object: { variable: 'nextRunAt' },
|
|
488
|
+
},
|
|
489
|
+
],
|
|
490
|
+
filters: [
|
|
491
|
+
{
|
|
492
|
+
variable: 'nextRunAt',
|
|
493
|
+
operator: '$lte',
|
|
494
|
+
value: literal('2026-05-18T01:30:00.000Z'),
|
|
495
|
+
},
|
|
496
|
+
],
|
|
497
|
+
select: ['schedule', 'nextRunAt'],
|
|
498
|
+
orderBy: [
|
|
499
|
+
{
|
|
500
|
+
variable: 'nextRunAt',
|
|
501
|
+
direction: 'asc',
|
|
502
|
+
},
|
|
503
|
+
],
|
|
504
|
+
limit: 100,
|
|
505
|
+
},
|
|
506
|
+
expectedPlan: ['join-index', 'range-filter-pushdown', 'join-order-pushdown', 'join-limit-pushdown'],
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
name: 'message count by thread with having',
|
|
510
|
+
resource: 'message',
|
|
511
|
+
purpose: 'grouped message count uses SQL GROUP BY/HAVING before pagination',
|
|
512
|
+
minScale: 'small',
|
|
513
|
+
query: {
|
|
514
|
+
patterns: [
|
|
515
|
+
{
|
|
516
|
+
subject: { variable: 'message' },
|
|
517
|
+
predicate: namedNode(SIOC_HAS_MEMBER),
|
|
518
|
+
object: { variable: 'thread' },
|
|
519
|
+
},
|
|
520
|
+
],
|
|
521
|
+
groupBy: ['thread'],
|
|
522
|
+
aggregate: {
|
|
523
|
+
type: 'count',
|
|
524
|
+
as: 'count',
|
|
525
|
+
variable: 'message',
|
|
526
|
+
},
|
|
527
|
+
having: [
|
|
528
|
+
{
|
|
529
|
+
variable: 'count',
|
|
530
|
+
operator: '$gt',
|
|
531
|
+
value: literal('2', namedNode('http://www.w3.org/2001/XMLSchema#integer')),
|
|
532
|
+
},
|
|
533
|
+
],
|
|
534
|
+
select: ['thread', 'count'],
|
|
535
|
+
orderBy: [
|
|
536
|
+
{
|
|
537
|
+
variable: 'count',
|
|
538
|
+
direction: 'desc',
|
|
539
|
+
},
|
|
540
|
+
],
|
|
541
|
+
limit: 1,
|
|
542
|
+
},
|
|
543
|
+
expectedPlan: ['group-count-index', 'having-pushdown', 'order', 'limit'],
|
|
544
|
+
},
|
|
545
|
+
{
|
|
546
|
+
name: 'message join count distinct',
|
|
547
|
+
resource: 'message',
|
|
548
|
+
purpose: 'message/thread BGP aggregate count stays inside SQL self-join',
|
|
549
|
+
minScale: 'small',
|
|
550
|
+
query: {
|
|
551
|
+
patterns: [
|
|
552
|
+
{
|
|
553
|
+
subject: { variable: 'message' },
|
|
554
|
+
predicate: namedNode(RDF_TYPE),
|
|
555
|
+
object: namedNode(`${MEETING}Message`),
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
subject: { variable: 'message' },
|
|
559
|
+
predicate: namedNode(SIOC_HAS_MEMBER),
|
|
560
|
+
object: { variable: 'thread' },
|
|
561
|
+
},
|
|
562
|
+
],
|
|
563
|
+
aggregates: [
|
|
564
|
+
{
|
|
565
|
+
type: 'count',
|
|
566
|
+
as: 'messageCount',
|
|
567
|
+
variable: 'message',
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
type: 'count',
|
|
571
|
+
as: 'threadCount',
|
|
572
|
+
variable: 'thread',
|
|
573
|
+
distinct: true,
|
|
574
|
+
},
|
|
575
|
+
],
|
|
576
|
+
},
|
|
577
|
+
expectedPlan: ['join-count-index'],
|
|
578
|
+
},
|
|
579
|
+
];
|
|
580
|
+
function rdfModelsBenchmarkCaseNames() {
|
|
581
|
+
return exports.rdfModelsBenchmarkCases.map((testCase) => testCase.name);
|
|
582
|
+
}
|
|
583
|
+
function rdfModelsLocalQueryBenchmarkCaseNames() {
|
|
584
|
+
return exports.rdfModelsLocalQueryBenchmarkCases.map((testCase) => testCase.name);
|
|
585
|
+
}
|
|
586
|
+
function rdfModelsBenchmarkScaleTargetQuads(scale) {
|
|
587
|
+
return RDF_MODELS_SCALE_TARGET_QUADS[scale];
|
|
588
|
+
}
|
|
589
|
+
function rdfModelsBenchmarkSyntheticPodCount(scale) {
|
|
590
|
+
return RDF_MODELS_SYNTHETIC_POD_COUNTS[scale];
|
|
591
|
+
}
|
|
592
|
+
function estimateRdfModelsSyntheticQuadCount(syntheticMessages) {
|
|
593
|
+
return Math.max(0, Math.floor(syntheticMessages)) * exports.RDF_MODELS_SYNTHETIC_MESSAGE_QUADS;
|
|
594
|
+
}
|
|
595
|
+
function defaultSyntheticMessagesForRdfModelsScale(scale) {
|
|
596
|
+
if (scale === 'small') {
|
|
597
|
+
return 12;
|
|
598
|
+
}
|
|
599
|
+
return Math.ceil(rdfModelsBenchmarkScaleTargetQuads(scale) / exports.RDF_MODELS_SYNTHETIC_MESSAGE_QUADS);
|
|
600
|
+
}
|
|
601
|
+
function rdfModelsBenchmarkScaleSatisfied(scale, seedQuadCount) {
|
|
602
|
+
return seedQuadCount >= rdfModelsBenchmarkScaleTargetQuads(scale);
|
|
603
|
+
}
|
|
604
|
+
function runRdfModelsBenchmark(engine, options = {}) {
|
|
605
|
+
const scale = options.scale ?? 'small';
|
|
606
|
+
const iterations = Math.max(1, Math.floor(options.iterations ?? 1));
|
|
607
|
+
const cases = (options.cases ?? exports.rdfModelsBenchmarkCases)
|
|
608
|
+
.filter((testCase) => scaleRank(testCase.minScale) <= scaleRank(scale));
|
|
609
|
+
const localQueryCases = (options.localQueryCases ?? exports.rdfModelsLocalQueryBenchmarkCases)
|
|
610
|
+
.filter((testCase) => scaleRank(testCase.minScale) <= scaleRank(scale));
|
|
611
|
+
const results = cases.map((testCase) => runBenchmarkCase(engine, testCase, iterations));
|
|
612
|
+
const localQueryResults = localQueryCases.map((testCase) => runLocalQueryBenchmarkCase(engine, testCase, iterations));
|
|
613
|
+
const failedPlanCases = [
|
|
614
|
+
...results.filter((result) => !result.planMatched).map((result) => result.name),
|
|
615
|
+
...localQueryResults.filter((result) => !result.planMatched).map((result) => result.name),
|
|
616
|
+
];
|
|
617
|
+
return {
|
|
618
|
+
engine: 'solid-rdf',
|
|
619
|
+
scale,
|
|
620
|
+
iterations,
|
|
621
|
+
generatedAt: new Date().toISOString(),
|
|
622
|
+
planMatched: failedPlanCases.length === 0,
|
|
623
|
+
failedPlanCases,
|
|
624
|
+
cases: results,
|
|
625
|
+
localQueryCases: localQueryResults,
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
async function runRdfModelsShadowBenchmark(engine, compatibilityStore, options = {}) {
|
|
629
|
+
const scale = options.scale ?? 'small';
|
|
630
|
+
const iterations = Math.max(1, Math.floor(options.iterations ?? 1));
|
|
631
|
+
const cases = (options.cases ?? exports.rdfModelsBenchmarkCases)
|
|
632
|
+
.filter((testCase) => scaleRank(testCase.minScale) <= scaleRank(scale));
|
|
633
|
+
const results = [];
|
|
634
|
+
const compatibilityStats = await compatibilityStore.stats();
|
|
635
|
+
for (const testCase of cases) {
|
|
636
|
+
results.push(await runShadowBenchmarkCase(engine, compatibilityStore, testCase, iterations, compatibilityStats));
|
|
637
|
+
}
|
|
638
|
+
const spaceGateEnforced = scaleRank(scale) >= scaleRank('medium');
|
|
639
|
+
const failedPerformanceCases = results.filter((result) => !result.performance.matched).map((result) => result.name);
|
|
640
|
+
const failedSpaceCases = spaceGateEnforced
|
|
641
|
+
? results.filter((result) => !result.space.matched).map((result) => result.name)
|
|
642
|
+
: [];
|
|
643
|
+
return {
|
|
644
|
+
engine: 'shadow',
|
|
645
|
+
compatibilityEngine: 'quint-store',
|
|
646
|
+
candidateEngine: 'solid-rdf',
|
|
647
|
+
scale,
|
|
648
|
+
iterations,
|
|
649
|
+
generatedAt: new Date().toISOString(),
|
|
650
|
+
matched: results.every((result) => result.matched),
|
|
651
|
+
orderedMatched: results.every((result) => result.orderedMatch),
|
|
652
|
+
planMatched: results.every((result) => result.planMatched),
|
|
653
|
+
spaceGateEnforced,
|
|
654
|
+
performanceMatched: failedPerformanceCases.length === 0,
|
|
655
|
+
spaceMatched: failedSpaceCases.length === 0,
|
|
656
|
+
failedPlanCases: results.filter((result) => !result.planMatched).map((result) => result.name),
|
|
657
|
+
failedPerformanceCases,
|
|
658
|
+
failedSpaceCases,
|
|
659
|
+
cases: results,
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
function runRdfModelsRdf3xShadowBenchmark(engine, options = {}) {
|
|
663
|
+
if (!engine.rdf3xIndex) {
|
|
664
|
+
throw new Error('runRdfModelsRdf3xShadowBenchmark requires SolidRdfEngine.rdf3xIndex');
|
|
665
|
+
}
|
|
666
|
+
const scale = options.scale ?? 'small';
|
|
667
|
+
const iterations = Math.max(1, Math.floor(options.iterations ?? 1));
|
|
668
|
+
const cases = (options.cases ?? exports.rdfModelsBenchmarkCases)
|
|
669
|
+
.filter((testCase) => scaleRank(testCase.minScale) <= scaleRank(scale));
|
|
670
|
+
const localQueryCases = (options.localQueryCases ?? exports.rdfModelsLocalQueryBenchmarkCases)
|
|
671
|
+
.filter((testCase) => scaleRank(testCase.minScale) <= scaleRank(scale));
|
|
672
|
+
const rebuild = engine.rdf3xIndex.rebuildFromCurrentQuads();
|
|
673
|
+
const results = cases.map((testCase) => runRdf3xShadowBenchmarkCase(engine, testCase, iterations));
|
|
674
|
+
const joinResults = localQueryCases.map((testCase) => runRdf3xShadowJoinBenchmarkCase(engine, testCase, iterations));
|
|
675
|
+
const supportedResults = results.filter((result) => result.supported);
|
|
676
|
+
const supportedJoinResults = joinResults.filter((result) => result.supported);
|
|
677
|
+
return {
|
|
678
|
+
engine: 'rdf3x-shadow',
|
|
679
|
+
primaryEngine: 'solid-rdf',
|
|
680
|
+
candidateEngine: 'solid-rdf3x',
|
|
681
|
+
scale,
|
|
682
|
+
iterations,
|
|
683
|
+
generatedAt: new Date().toISOString(),
|
|
684
|
+
matched: supportedResults.every((result) => result.matched)
|
|
685
|
+
&& supportedJoinResults.every((result) => result.matched),
|
|
686
|
+
orderedMatched: supportedResults.every((result) => result.orderedMatch)
|
|
687
|
+
&& supportedJoinResults.every((result) => result.orderedMatch),
|
|
688
|
+
skippedCases: results.filter((result) => !result.supported).map((result) => result.name),
|
|
689
|
+
skippedJoinCases: joinResults.filter((result) => !result.supported).map((result) => result.name),
|
|
690
|
+
failedCases: supportedResults.filter((result) => !result.matched || !result.orderedMatch).map((result) => result.name),
|
|
691
|
+
failedJoinCases: supportedJoinResults
|
|
692
|
+
.filter((result) => !result.matched || !result.orderedMatch)
|
|
693
|
+
.map((result) => result.name),
|
|
694
|
+
rebuild,
|
|
695
|
+
cases: results,
|
|
696
|
+
joinCases: joinResults,
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
function runBenchmarkCase(engine, testCase, iterations) {
|
|
700
|
+
const durationsMs = [];
|
|
701
|
+
let metrics;
|
|
702
|
+
let keys = [];
|
|
703
|
+
for (let i = 0; i < iterations; i += 1) {
|
|
704
|
+
const start = Date.now();
|
|
705
|
+
const result = engine.scan(testCase.query);
|
|
706
|
+
durationsMs.push(Math.max(0, Date.now() - start));
|
|
707
|
+
metrics = result.metrics;
|
|
708
|
+
keys = result.quads.map(RdfShadowComparator_1.canonicalQuadKey);
|
|
709
|
+
}
|
|
710
|
+
const finalMetrics = metrics ?? {
|
|
711
|
+
engine: 'solid-rdf',
|
|
712
|
+
indexChoice: 'not-run',
|
|
713
|
+
matchedRows: 0,
|
|
714
|
+
returnedRows: 0,
|
|
715
|
+
durationMs: 0,
|
|
716
|
+
};
|
|
717
|
+
const missingPlan = missingExpectedPlan(testCase, finalMetrics);
|
|
718
|
+
const execution = benchmarkExecution(finalMetrics);
|
|
719
|
+
return {
|
|
720
|
+
name: testCase.name,
|
|
721
|
+
resource: testCase.resource,
|
|
722
|
+
purpose: testCase.purpose,
|
|
723
|
+
minScale: testCase.minScale,
|
|
724
|
+
query: {
|
|
725
|
+
pattern: serializePattern(testCase.query.pattern),
|
|
726
|
+
...(testCase.query.options ? { options: testCase.query.options } : {}),
|
|
727
|
+
},
|
|
728
|
+
expectedPlan: [...testCase.expectedPlan],
|
|
729
|
+
planMatched: missingPlan.length === 0,
|
|
730
|
+
missingPlan,
|
|
731
|
+
...execution,
|
|
732
|
+
returnedRows: keys.length,
|
|
733
|
+
checksum: checksum(keys, false),
|
|
734
|
+
orderedChecksum: checksum(keys, true),
|
|
735
|
+
durationsMs,
|
|
736
|
+
p50DurationMs: percentile(durationsMs, 0.5),
|
|
737
|
+
p95DurationMs: percentile(durationsMs, 0.95),
|
|
738
|
+
metrics: finalMetrics,
|
|
739
|
+
indexStats: engine.index.stats(),
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
function runLocalQueryBenchmarkCase(engine, testCase, iterations) {
|
|
743
|
+
const durationsMs = [];
|
|
744
|
+
let metrics;
|
|
745
|
+
let keys = [];
|
|
746
|
+
for (let i = 0; i < iterations; i += 1) {
|
|
747
|
+
const start = Date.now();
|
|
748
|
+
const result = engine.query(testCase.query);
|
|
749
|
+
durationsMs.push(Math.max(0, Date.now() - start));
|
|
750
|
+
metrics = result.metrics;
|
|
751
|
+
keys = result.bindings.map(bindingKey);
|
|
752
|
+
}
|
|
753
|
+
const finalMetrics = metrics ?? {
|
|
754
|
+
engine: 'solid-rdf',
|
|
755
|
+
plan: [],
|
|
756
|
+
scannedRows: 0,
|
|
757
|
+
joinedRows: 0,
|
|
758
|
+
returnedRows: 0,
|
|
759
|
+
durationMs: 0,
|
|
760
|
+
indexChoices: [],
|
|
761
|
+
filtersApplied: 0,
|
|
762
|
+
filtersPushedDown: 0,
|
|
763
|
+
};
|
|
764
|
+
const missingPlan = missingExpectedLocalQueryPlan(testCase, finalMetrics);
|
|
765
|
+
return {
|
|
766
|
+
name: testCase.name,
|
|
767
|
+
resource: testCase.resource,
|
|
768
|
+
purpose: testCase.purpose,
|
|
769
|
+
minScale: testCase.minScale,
|
|
770
|
+
query: serializeLocalQuery(testCase.query),
|
|
771
|
+
expectedPlan: [...testCase.expectedPlan],
|
|
772
|
+
planMatched: missingPlan.length === 0,
|
|
773
|
+
missingPlan,
|
|
774
|
+
physicalPlan: finalMetrics.plan,
|
|
775
|
+
scannedRows: finalMetrics.scannedRows,
|
|
776
|
+
indexChoices: [...finalMetrics.indexChoices],
|
|
777
|
+
fallbackReason: null,
|
|
778
|
+
returnedRows: keys.length,
|
|
779
|
+
checksum: checksum(keys, false),
|
|
780
|
+
orderedChecksum: checksum(keys, true),
|
|
781
|
+
durationsMs,
|
|
782
|
+
p50DurationMs: percentile(durationsMs, 0.5),
|
|
783
|
+
p95DurationMs: percentile(durationsMs, 0.95),
|
|
784
|
+
metrics: finalMetrics,
|
|
785
|
+
indexStats: engine.index.stats(),
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
function runRdf3xShadowBenchmarkCase(engine, testCase, iterations) {
|
|
789
|
+
const baseResult = baseRdf3xShadowBenchmarkResult(testCase);
|
|
790
|
+
const unsupportedReason = unsupportedRdf3xPatternReason(testCase.query.pattern);
|
|
791
|
+
if (unsupportedReason) {
|
|
792
|
+
return {
|
|
793
|
+
...baseResult,
|
|
794
|
+
supported: false,
|
|
795
|
+
unsupportedReason,
|
|
796
|
+
matched: false,
|
|
797
|
+
orderedMatch: false,
|
|
798
|
+
diff: {
|
|
799
|
+
missingFromPrimary: [],
|
|
800
|
+
extraInPrimary: [],
|
|
801
|
+
},
|
|
802
|
+
solidRdf: emptySolidRdfBenchmarkSide(engine),
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
const solidRdfDurationsMs = [];
|
|
806
|
+
const rdf3xDurationsMs = [];
|
|
807
|
+
let solidRdfQuads = [];
|
|
808
|
+
let rdf3xQuads = [];
|
|
809
|
+
let solidRdfMetrics;
|
|
810
|
+
let rdf3xMetrics;
|
|
811
|
+
for (let i = 0; i < iterations; i += 1) {
|
|
812
|
+
let start = Date.now();
|
|
813
|
+
const solidRdfResult = engine.scan(testCase.query);
|
|
814
|
+
solidRdfDurationsMs.push(Math.max(0, Date.now() - start));
|
|
815
|
+
solidRdfQuads = solidRdfResult.quads;
|
|
816
|
+
solidRdfMetrics = solidRdfResult.metrics;
|
|
817
|
+
start = Date.now();
|
|
818
|
+
const rdf3xResult = engine.rdf3xIndex.scan(rdf3xPatternFor(testCase.query.pattern), testCase.query.options);
|
|
819
|
+
rdf3xDurationsMs.push(Math.max(0, Date.now() - start));
|
|
820
|
+
rdf3xQuads = rdf3xResult.quads;
|
|
821
|
+
rdf3xMetrics = rdf3xResult.metrics;
|
|
822
|
+
}
|
|
823
|
+
const solidRdfKeys = solidRdfQuads.map(RdfShadowComparator_1.canonicalQuadKey);
|
|
824
|
+
const rdf3xKeys = rdf3xQuads.map(RdfShadowComparator_1.canonicalQuadKey);
|
|
825
|
+
const diff = (0, RdfShadowComparator_1.diffQuads)(solidRdfQuads, rdf3xQuads);
|
|
826
|
+
const orderedMatch = isSemanticallyOrdered(testCase.query.options)
|
|
827
|
+
? rdf3xKeys.join('\n') === solidRdfKeys.join('\n')
|
|
828
|
+
: true;
|
|
829
|
+
const finalSolidRdfMetrics = solidRdfMetrics ?? {
|
|
830
|
+
engine: 'solid-rdf',
|
|
831
|
+
indexChoice: 'not-run',
|
|
832
|
+
matchedRows: 0,
|
|
833
|
+
returnedRows: 0,
|
|
834
|
+
durationMs: 0,
|
|
835
|
+
};
|
|
836
|
+
const finalRdf3xMetrics = rdf3xMetrics ?? {
|
|
837
|
+
engine: 'solid-rdf3x',
|
|
838
|
+
indexChoice: 'none',
|
|
839
|
+
matchedRows: 0,
|
|
840
|
+
returnedRows: 0,
|
|
841
|
+
durationMs: 0,
|
|
842
|
+
};
|
|
843
|
+
return {
|
|
844
|
+
...baseResult,
|
|
845
|
+
supported: true,
|
|
846
|
+
matched: diff.missingFromPrimary.length === 0 && diff.extraInPrimary.length === 0,
|
|
847
|
+
orderedMatch,
|
|
848
|
+
diff,
|
|
849
|
+
solidRdf: {
|
|
850
|
+
...benchmarkSide(solidRdfKeys, solidRdfDurationsMs),
|
|
851
|
+
...benchmarkExecution(finalSolidRdfMetrics),
|
|
852
|
+
metrics: finalSolidRdfMetrics,
|
|
853
|
+
indexStats: engine.index.stats(),
|
|
854
|
+
},
|
|
855
|
+
rdf3x: {
|
|
856
|
+
...benchmarkSide(rdf3xKeys, rdf3xDurationsMs),
|
|
857
|
+
...rdf3xBenchmarkExecution(finalRdf3xMetrics),
|
|
858
|
+
metrics: finalRdf3xMetrics,
|
|
859
|
+
indexStats: engine.rdf3xIndex.stats(),
|
|
860
|
+
},
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
function runRdf3xShadowJoinBenchmarkCase(engine, testCase, iterations) {
|
|
864
|
+
const unsupportedReason = unsupportedRdf3xJoinQueryReason(testCase.query);
|
|
865
|
+
if (unsupportedReason) {
|
|
866
|
+
return {
|
|
867
|
+
...baseRdf3xShadowJoinBenchmarkResult(testCase),
|
|
868
|
+
supported: false,
|
|
869
|
+
unsupportedReason,
|
|
870
|
+
matched: false,
|
|
871
|
+
orderedMatch: false,
|
|
872
|
+
diff: {
|
|
873
|
+
missingFromPrimary: [],
|
|
874
|
+
extraInPrimary: [],
|
|
875
|
+
},
|
|
876
|
+
solidRdf: emptySolidRdfBenchmarkSide(engine),
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
const joinShape = rdf3xJoinShapeFor(testCase.query);
|
|
880
|
+
const solidRdfDurationsMs = [];
|
|
881
|
+
const rdf3xDurationsMs = [];
|
|
882
|
+
let solidRdfBindings = [];
|
|
883
|
+
let rdf3xBindings = [];
|
|
884
|
+
let solidRdfMetrics;
|
|
885
|
+
let rdf3xMetrics;
|
|
886
|
+
for (let i = 0; i < iterations; i += 1) {
|
|
887
|
+
let start = Date.now();
|
|
888
|
+
const solidRdfResult = engine.index.joinPatterns(joinShape.patterns, joinShape.options);
|
|
889
|
+
solidRdfDurationsMs.push(Math.max(0, Date.now() - start));
|
|
890
|
+
solidRdfBindings = solidRdfResult.bindings;
|
|
891
|
+
solidRdfMetrics = solidRdfResult.metrics;
|
|
892
|
+
start = Date.now();
|
|
893
|
+
const rdf3xResult = engine.rdf3xIndex.joinPatterns(joinShape.patterns, joinShape.options);
|
|
894
|
+
rdf3xDurationsMs.push(Math.max(0, Date.now() - start));
|
|
895
|
+
rdf3xBindings = rdf3xResult.bindings;
|
|
896
|
+
rdf3xMetrics = rdf3xResult.metrics;
|
|
897
|
+
}
|
|
898
|
+
const solidRdfKeys = solidRdfBindings.map(bindingKey);
|
|
899
|
+
const rdf3xKeys = rdf3xBindings.map(bindingKey);
|
|
900
|
+
const diff = diffBindingKeys(solidRdfKeys, rdf3xKeys);
|
|
901
|
+
const orderedMatch = isSemanticallyOrderedJoin(joinShape.options)
|
|
902
|
+
? rdf3xKeys.join('\n') === solidRdfKeys.join('\n')
|
|
903
|
+
: true;
|
|
904
|
+
const finalSolidRdfMetrics = solidRdfMetrics ?? {
|
|
905
|
+
engine: 'solid-rdf',
|
|
906
|
+
indexChoice: 'not-run',
|
|
907
|
+
matchedRows: 0,
|
|
908
|
+
returnedRows: 0,
|
|
909
|
+
durationMs: 0,
|
|
910
|
+
};
|
|
911
|
+
const finalRdf3xMetrics = rdf3xMetrics ?? {
|
|
912
|
+
engine: 'solid-rdf3x',
|
|
913
|
+
indexChoice: 'none',
|
|
914
|
+
matchedRows: 0,
|
|
915
|
+
returnedRows: 0,
|
|
916
|
+
durationMs: 0,
|
|
917
|
+
};
|
|
918
|
+
return {
|
|
919
|
+
...baseRdf3xShadowJoinBenchmarkResult(testCase),
|
|
920
|
+
supported: true,
|
|
921
|
+
matched: diff.missingFromPrimary.length === 0 && diff.extraInPrimary.length === 0,
|
|
922
|
+
orderedMatch,
|
|
923
|
+
diff,
|
|
924
|
+
solidRdf: {
|
|
925
|
+
...benchmarkSide(solidRdfKeys, solidRdfDurationsMs),
|
|
926
|
+
...benchmarkExecution(finalSolidRdfMetrics),
|
|
927
|
+
metrics: finalSolidRdfMetrics,
|
|
928
|
+
indexStats: engine.index.stats(),
|
|
929
|
+
},
|
|
930
|
+
rdf3x: {
|
|
931
|
+
...benchmarkSide(rdf3xKeys, rdf3xDurationsMs),
|
|
932
|
+
...rdf3xJoinBenchmarkExecution(finalRdf3xMetrics),
|
|
933
|
+
metrics: finalRdf3xMetrics,
|
|
934
|
+
indexStats: engine.rdf3xIndex.stats(),
|
|
935
|
+
},
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
function baseRdf3xShadowBenchmarkResult(testCase) {
|
|
939
|
+
return {
|
|
940
|
+
name: testCase.name,
|
|
941
|
+
resource: testCase.resource,
|
|
942
|
+
purpose: testCase.purpose,
|
|
943
|
+
minScale: testCase.minScale,
|
|
944
|
+
query: {
|
|
945
|
+
pattern: serializePattern(testCase.query.pattern),
|
|
946
|
+
...(testCase.query.options ? { options: testCase.query.options } : {}),
|
|
947
|
+
},
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
function emptySolidRdfBenchmarkSide(engine) {
|
|
951
|
+
const metrics = {
|
|
952
|
+
engine: 'solid-rdf',
|
|
953
|
+
indexChoice: 'not-run',
|
|
954
|
+
matchedRows: 0,
|
|
955
|
+
returnedRows: 0,
|
|
956
|
+
durationMs: 0,
|
|
957
|
+
};
|
|
958
|
+
return {
|
|
959
|
+
...benchmarkSide([], []),
|
|
960
|
+
...benchmarkExecution(metrics),
|
|
961
|
+
metrics,
|
|
962
|
+
indexStats: engine.index.stats(),
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
async function runShadowBenchmarkCase(engine, compatibilityStore, testCase, iterations, compatibilityStats) {
|
|
966
|
+
const compatibilityDurationsMs = [];
|
|
967
|
+
const solidRdfDurationsMs = [];
|
|
968
|
+
let compatibilityQuads = [];
|
|
969
|
+
let solidRdfQuads = [];
|
|
970
|
+
let metrics;
|
|
971
|
+
for (let i = 0; i < iterations; i += 1) {
|
|
972
|
+
let start = Date.now();
|
|
973
|
+
compatibilityQuads = await compatibilityStore.get(testCase.query.pattern, testCase.query.options);
|
|
974
|
+
compatibilityDurationsMs.push(Math.max(0, Date.now() - start));
|
|
975
|
+
start = Date.now();
|
|
976
|
+
const solidRdfResult = engine.scan(testCase.query);
|
|
977
|
+
solidRdfDurationsMs.push(Math.max(0, Date.now() - start));
|
|
978
|
+
solidRdfQuads = solidRdfResult.quads;
|
|
979
|
+
metrics = solidRdfResult.metrics;
|
|
980
|
+
}
|
|
981
|
+
const compatibilityKeys = compatibilityQuads.map(RdfShadowComparator_1.canonicalQuadKey);
|
|
982
|
+
const solidRdfKeys = solidRdfQuads.map(RdfShadowComparator_1.canonicalQuadKey);
|
|
983
|
+
const diff = (0, RdfShadowComparator_1.diffQuads)(solidRdfQuads, compatibilityQuads);
|
|
984
|
+
const orderedMatch = isSemanticallyOrdered(testCase.query.options)
|
|
985
|
+
? solidRdfKeys.join('\n') === compatibilityKeys.join('\n')
|
|
986
|
+
: true;
|
|
987
|
+
const finalMetrics = metrics ?? {
|
|
988
|
+
engine: 'solid-rdf',
|
|
989
|
+
indexChoice: 'not-run',
|
|
990
|
+
matchedRows: 0,
|
|
991
|
+
returnedRows: 0,
|
|
992
|
+
durationMs: 0,
|
|
993
|
+
};
|
|
994
|
+
const missingPlan = missingExpectedPlan(testCase, finalMetrics);
|
|
995
|
+
const execution = benchmarkExecution(finalMetrics);
|
|
996
|
+
const compatibilitySide = {
|
|
997
|
+
...benchmarkSide(compatibilityKeys, compatibilityDurationsMs),
|
|
998
|
+
storeStats: compatibilityStats,
|
|
999
|
+
};
|
|
1000
|
+
const solidRdfSide = {
|
|
1001
|
+
...benchmarkSide(solidRdfKeys, solidRdfDurationsMs),
|
|
1002
|
+
...execution,
|
|
1003
|
+
metrics: finalMetrics,
|
|
1004
|
+
indexStats: engine.index.stats(),
|
|
1005
|
+
};
|
|
1006
|
+
return {
|
|
1007
|
+
name: testCase.name,
|
|
1008
|
+
resource: testCase.resource,
|
|
1009
|
+
purpose: testCase.purpose,
|
|
1010
|
+
minScale: testCase.minScale,
|
|
1011
|
+
query: {
|
|
1012
|
+
pattern: serializePattern(testCase.query.pattern),
|
|
1013
|
+
...(testCase.query.options ? { options: testCase.query.options } : {}),
|
|
1014
|
+
},
|
|
1015
|
+
expectedPlan: [...testCase.expectedPlan],
|
|
1016
|
+
planMatched: missingPlan.length === 0,
|
|
1017
|
+
missingPlan,
|
|
1018
|
+
matched: diff.missingFromPrimary.length === 0 && diff.extraInPrimary.length === 0,
|
|
1019
|
+
orderedMatch,
|
|
1020
|
+
diff,
|
|
1021
|
+
compatibility: compatibilitySide,
|
|
1022
|
+
solidRdf: solidRdfSide,
|
|
1023
|
+
performance: comparePerformance(compatibilitySide, solidRdfSide),
|
|
1024
|
+
space: compareSpace(compatibilityStats, solidRdfSide.indexStats),
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
function benchmarkExecution(metrics) {
|
|
1028
|
+
return {
|
|
1029
|
+
physicalPlan: metrics.queryPlan ?? [],
|
|
1030
|
+
scannedRows: metrics.matchedRows,
|
|
1031
|
+
indexChoice: metrics.indexChoice,
|
|
1032
|
+
joinOrder: [metrics.indexChoice],
|
|
1033
|
+
fallbackReason: null,
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
function rdf3xBenchmarkExecution(metrics) {
|
|
1037
|
+
return {
|
|
1038
|
+
physicalPlan: metrics.queryPlan ?? [],
|
|
1039
|
+
scannedRows: metrics.matchedRows,
|
|
1040
|
+
indexChoice: metrics.indexChoice,
|
|
1041
|
+
joinOrder: [metrics.indexChoice],
|
|
1042
|
+
fallbackReason: null,
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
function rdf3xJoinBenchmarkExecution(metrics) {
|
|
1046
|
+
return {
|
|
1047
|
+
physicalPlan: metrics.queryPlan ?? [],
|
|
1048
|
+
scannedRows: metrics.matchedRows,
|
|
1049
|
+
indexChoice: metrics.indexChoice,
|
|
1050
|
+
joinOrder: [metrics.indexChoice],
|
|
1051
|
+
fallbackReason: null,
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
function baseRdf3xShadowJoinBenchmarkResult(testCase) {
|
|
1055
|
+
return {
|
|
1056
|
+
name: testCase.name,
|
|
1057
|
+
resource: testCase.resource,
|
|
1058
|
+
purpose: testCase.purpose,
|
|
1059
|
+
minScale: testCase.minScale,
|
|
1060
|
+
query: serializeLocalQuery(testCase.query),
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
function unsupportedRdf3xJoinQueryReason(query) {
|
|
1064
|
+
if (query.patterns.length === 0) {
|
|
1065
|
+
return 'RDF-3X join shadow requires at least one required BGP pattern';
|
|
1066
|
+
}
|
|
1067
|
+
if (query.values?.length) {
|
|
1068
|
+
return 'RDF-3X join shadow does not support VALUES yet';
|
|
1069
|
+
}
|
|
1070
|
+
if (query.textSearch?.length || query.vectorSearch?.length) {
|
|
1071
|
+
return 'RDF-3X join shadow does not support search sources yet';
|
|
1072
|
+
}
|
|
1073
|
+
if (query.unions?.length || query.minus?.length || query.exists?.length || query.optional?.length) {
|
|
1074
|
+
return 'RDF-3X join shadow only supports required BGP queries';
|
|
1075
|
+
}
|
|
1076
|
+
if (query.binds?.length || query.filters?.length || query.having?.length) {
|
|
1077
|
+
return 'RDF-3X join shadow does not support local query filters or BIND yet';
|
|
1078
|
+
}
|
|
1079
|
+
if (query.groupBy?.length || query.aggregate || query.aggregates?.length) {
|
|
1080
|
+
return 'RDF-3X join shadow does not support aggregate queries yet';
|
|
1081
|
+
}
|
|
1082
|
+
for (const pattern of query.patterns) {
|
|
1083
|
+
const joinPattern = rdf3xJoinPatternFor(pattern);
|
|
1084
|
+
const unsupportedPattern = unsupportedRdf3xPatternReason(joinPattern.pattern);
|
|
1085
|
+
if (unsupportedPattern) {
|
|
1086
|
+
return unsupportedPattern;
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
return undefined;
|
|
1090
|
+
}
|
|
1091
|
+
function rdf3xJoinShapeFor(query) {
|
|
1092
|
+
return {
|
|
1093
|
+
patterns: query.patterns.map(rdf3xJoinPatternFor),
|
|
1094
|
+
options: {
|
|
1095
|
+
...(query.select ? { project: query.select } : {}),
|
|
1096
|
+
...(query.distinct ? { distinct: true } : {}),
|
|
1097
|
+
...(query.orderBy ? { orderBy: query.orderBy } : {}),
|
|
1098
|
+
...(query.limit !== undefined ? { limit: query.limit } : {}),
|
|
1099
|
+
...(query.offset !== undefined ? { offset: query.offset } : {}),
|
|
1100
|
+
},
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
function rdf3xJoinPatternFor(pattern) {
|
|
1104
|
+
const compiledPattern = {};
|
|
1105
|
+
const variables = {};
|
|
1106
|
+
for (const key of ['graph', 'subject', 'predicate', 'object']) {
|
|
1107
|
+
const value = pattern[key];
|
|
1108
|
+
if (!value) {
|
|
1109
|
+
continue;
|
|
1110
|
+
}
|
|
1111
|
+
if (isQueryVariable(value)) {
|
|
1112
|
+
variables[key] = value.variable;
|
|
1113
|
+
}
|
|
1114
|
+
else {
|
|
1115
|
+
compiledPattern[key] = value;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
return {
|
|
1119
|
+
pattern: compiledPattern,
|
|
1120
|
+
variables,
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
function isQueryVariable(value) {
|
|
1124
|
+
return value !== null
|
|
1125
|
+
&& typeof value === 'object'
|
|
1126
|
+
&& !('termType' in value)
|
|
1127
|
+
&& 'variable' in value
|
|
1128
|
+
&& typeof value.variable === 'string';
|
|
1129
|
+
}
|
|
1130
|
+
function unsupportedRdf3xPatternReason(pattern) {
|
|
1131
|
+
for (const key of ['graph', 'subject', 'predicate', 'object']) {
|
|
1132
|
+
const value = pattern[key];
|
|
1133
|
+
if (!value || (0, types_1.isTerm)(value)) {
|
|
1134
|
+
continue;
|
|
1135
|
+
}
|
|
1136
|
+
if (key === 'graph' && isGraphPrefixPattern(value)) {
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
1139
|
+
if (key === 'object' && isSupportedRdf3xNumericObjectRangePattern(value)) {
|
|
1140
|
+
continue;
|
|
1141
|
+
}
|
|
1142
|
+
return `unsupported ${key} pattern for RDF-3X shadow`;
|
|
1143
|
+
}
|
|
1144
|
+
return undefined;
|
|
1145
|
+
}
|
|
1146
|
+
function rdf3xPatternFor(pattern) {
|
|
1147
|
+
const result = {};
|
|
1148
|
+
for (const key of ['graph', 'subject', 'predicate', 'object']) {
|
|
1149
|
+
const value = pattern[key];
|
|
1150
|
+
if (!value) {
|
|
1151
|
+
continue;
|
|
1152
|
+
}
|
|
1153
|
+
if (key === 'graph' && isGraphPrefixPattern(value)) {
|
|
1154
|
+
result.graph = { $startsWith: value.$startsWith };
|
|
1155
|
+
continue;
|
|
1156
|
+
}
|
|
1157
|
+
if (key === 'object' && isSupportedRdf3xNumericObjectRangePattern(value)) {
|
|
1158
|
+
result.object = value;
|
|
1159
|
+
continue;
|
|
1160
|
+
}
|
|
1161
|
+
if (!(0, types_1.isTerm)(value)) {
|
|
1162
|
+
throw new Error(`RDF-3X shadow benchmark only supports exact ${key} terms or graph prefixes`);
|
|
1163
|
+
}
|
|
1164
|
+
result[key] = value;
|
|
1165
|
+
}
|
|
1166
|
+
return result;
|
|
1167
|
+
}
|
|
1168
|
+
function isGraphPrefixPattern(value) {
|
|
1169
|
+
return value !== null
|
|
1170
|
+
&& typeof value === 'object'
|
|
1171
|
+
&& '$startsWith' in value
|
|
1172
|
+
&& typeof value.$startsWith === 'string';
|
|
1173
|
+
}
|
|
1174
|
+
function isSupportedRdf3xNumericObjectRangePattern(value) {
|
|
1175
|
+
if (value === null || typeof value !== 'object' || 'termType' in value) {
|
|
1176
|
+
return false;
|
|
1177
|
+
}
|
|
1178
|
+
let hasRange = false;
|
|
1179
|
+
for (const operator of ['$gt', '$gte', '$lt', '$lte']) {
|
|
1180
|
+
const rangeValue = value[operator];
|
|
1181
|
+
if (rangeValue === undefined) {
|
|
1182
|
+
continue;
|
|
1183
|
+
}
|
|
1184
|
+
hasRange = true;
|
|
1185
|
+
if (numericRangeValue(rangeValue) === undefined) {
|
|
1186
|
+
return false;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
return hasRange;
|
|
1190
|
+
}
|
|
1191
|
+
function numericRangeValue(value) {
|
|
1192
|
+
if (typeof value === 'number') {
|
|
1193
|
+
return Number.isFinite(value) ? value : undefined;
|
|
1194
|
+
}
|
|
1195
|
+
if (typeof value === 'string') {
|
|
1196
|
+
const parsed = Number(value);
|
|
1197
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
1198
|
+
}
|
|
1199
|
+
if (value.termType !== 'Literal' || !(0, RdfTermSemantics_1.isRdfNumericDatatype)(value.datatype.value)) {
|
|
1200
|
+
return undefined;
|
|
1201
|
+
}
|
|
1202
|
+
const parsed = (0, RdfTermSemantics_1.rdfNumericValue)(value.value);
|
|
1203
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
1204
|
+
}
|
|
1205
|
+
function isSemanticallyOrdered(options) {
|
|
1206
|
+
return Boolean(options?.order && options.order.length > 0);
|
|
1207
|
+
}
|
|
1208
|
+
function isSemanticallyOrderedJoin(options) {
|
|
1209
|
+
return Boolean(options?.orderBy && options.orderBy.length > 0);
|
|
1210
|
+
}
|
|
1211
|
+
function benchmarkSide(keys, durationsMs) {
|
|
1212
|
+
return {
|
|
1213
|
+
returnedRows: keys.length,
|
|
1214
|
+
checksum: checksum(keys, false),
|
|
1215
|
+
orderedChecksum: checksum(keys, true),
|
|
1216
|
+
durationsMs,
|
|
1217
|
+
p50DurationMs: percentile(durationsMs, 0.5),
|
|
1218
|
+
p95DurationMs: percentile(durationsMs, 0.95),
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1221
|
+
function diffBindingKeys(primaryKeys, candidateKeys) {
|
|
1222
|
+
const primarySet = new Set(primaryKeys);
|
|
1223
|
+
const candidateSet = new Set(candidateKeys);
|
|
1224
|
+
return {
|
|
1225
|
+
missingFromPrimary: Array.from(candidateSet).filter((key) => !primarySet.has(key)).sort(),
|
|
1226
|
+
extraInPrimary: Array.from(primarySet).filter((key) => !candidateSet.has(key)).sort(),
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
function comparePerformance(compatibility, solidRdf) {
|
|
1230
|
+
const p95DeltaMs = solidRdf.p95DurationMs - compatibility.p95DurationMs;
|
|
1231
|
+
const p95Ratio = solidRdf.p95DurationMs / Math.max(1, compatibility.p95DurationMs);
|
|
1232
|
+
return {
|
|
1233
|
+
p95DeltaMs,
|
|
1234
|
+
p95Ratio,
|
|
1235
|
+
matched: p95DeltaMs <= PERFORMANCE_P95_MIN_ABSOLUTE_HEADROOM_MS
|
|
1236
|
+
|| p95Ratio <= PERFORMANCE_P95_MAX_RATIO,
|
|
1237
|
+
};
|
|
1238
|
+
}
|
|
1239
|
+
function compareSpace(compatibility, solidRdf) {
|
|
1240
|
+
if (compatibility.databaseBytes === undefined
|
|
1241
|
+
|| compatibility.tableBytes === undefined
|
|
1242
|
+
|| compatibility.indexBytes === undefined) {
|
|
1243
|
+
return {
|
|
1244
|
+
databaseDeltaBytes: 0,
|
|
1245
|
+
tableDeltaBytes: 0,
|
|
1246
|
+
indexDeltaBytes: 0,
|
|
1247
|
+
databaseRatio: 0,
|
|
1248
|
+
tableRatio: 0,
|
|
1249
|
+
indexRatio: 0,
|
|
1250
|
+
matched: false,
|
|
1251
|
+
unavailableReason: 'compatibility store did not report database/table/index bytes',
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
const databaseDeltaBytes = solidRdf.databaseBytes - compatibility.databaseBytes;
|
|
1255
|
+
const tableDeltaBytes = solidRdf.tableBytes - compatibility.tableBytes;
|
|
1256
|
+
const indexDeltaBytes = solidRdf.indexBytes - compatibility.indexBytes;
|
|
1257
|
+
return {
|
|
1258
|
+
databaseDeltaBytes,
|
|
1259
|
+
tableDeltaBytes,
|
|
1260
|
+
indexDeltaBytes,
|
|
1261
|
+
databaseRatio: ratio(solidRdf.databaseBytes, compatibility.databaseBytes),
|
|
1262
|
+
tableRatio: ratio(solidRdf.tableBytes, compatibility.tableBytes),
|
|
1263
|
+
indexRatio: ratio(solidRdf.indexBytes, compatibility.indexBytes),
|
|
1264
|
+
matched: databaseDeltaBytes <= 0 && tableDeltaBytes <= 0 && indexDeltaBytes <= 0,
|
|
1265
|
+
};
|
|
1266
|
+
}
|
|
1267
|
+
function ratio(candidate, baseline) {
|
|
1268
|
+
if (baseline <= 0) {
|
|
1269
|
+
return candidate <= 0 ? 1 : Number.POSITIVE_INFINITY;
|
|
1270
|
+
}
|
|
1271
|
+
return candidate / baseline;
|
|
1272
|
+
}
|
|
1273
|
+
function missingExpectedPlan(testCase, metrics) {
|
|
1274
|
+
return testCase.expectedPlan.filter((label) => !matchesExpectedPlanLabel(label, testCase, metrics));
|
|
1275
|
+
}
|
|
1276
|
+
function matchesExpectedPlanLabel(label, testCase, metrics) {
|
|
1277
|
+
const pattern = testCase.query.pattern;
|
|
1278
|
+
const planText = (metrics.queryPlan ?? []).join('\n');
|
|
1279
|
+
switch (label) {
|
|
1280
|
+
case 'graph-scope':
|
|
1281
|
+
return Boolean(pattern.graph) && metrics.indexChoice.includes('G');
|
|
1282
|
+
case 'type-filter':
|
|
1283
|
+
return (0, types_1.isTerm)(pattern.predicate)
|
|
1284
|
+
&& (0, n3_2.termToId)(pattern.predicate) === RDF_TYPE
|
|
1285
|
+
&& Boolean(pattern.object)
|
|
1286
|
+
&& metrics.indexChoice !== 'full-scan';
|
|
1287
|
+
case 'predicate-filter':
|
|
1288
|
+
return Boolean(pattern.predicate) && metrics.indexChoice.includes('P');
|
|
1289
|
+
case 'predicate-object-filter':
|
|
1290
|
+
return Boolean(pattern.predicate) && Boolean(pattern.object) && metrics.indexChoice.includes('P');
|
|
1291
|
+
case 'predicate-object-range-filter':
|
|
1292
|
+
return Boolean(pattern.predicate)
|
|
1293
|
+
&& (planText.includes('_range') || planText.includes('LexicalRange(') || planText.includes('NumericRange('));
|
|
1294
|
+
case 'limit':
|
|
1295
|
+
return testCase.query.options?.limit !== undefined && planText.includes('LIMIT');
|
|
1296
|
+
case 'order':
|
|
1297
|
+
return Boolean(testCase.query.options?.order?.length) && planText.includes('ORDER BY');
|
|
1298
|
+
case 'text-index':
|
|
1299
|
+
return planText.includes('TextSearch(');
|
|
1300
|
+
case 'rdf-subject-join':
|
|
1301
|
+
return planText.includes('TextSearch(')
|
|
1302
|
+
&& metrics.indexChoice !== 'full-scan'
|
|
1303
|
+
&& metrics.matchedRows >= metrics.returnedRows;
|
|
1304
|
+
case 'SPOG':
|
|
1305
|
+
case 'POSG':
|
|
1306
|
+
case 'GSPO':
|
|
1307
|
+
case 'GPOS':
|
|
1308
|
+
case 'OSPG':
|
|
1309
|
+
return metrics.indexChoice === label;
|
|
1310
|
+
default:
|
|
1311
|
+
return false;
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
function missingExpectedLocalQueryPlan(testCase, metrics) {
|
|
1315
|
+
return testCase.expectedPlan.filter((label) => !matchesExpectedLocalQueryPlanLabel(label, metrics));
|
|
1316
|
+
}
|
|
1317
|
+
function matchesExpectedLocalQueryPlanLabel(label, metrics) {
|
|
1318
|
+
const planText = metrics.plan.join('\n');
|
|
1319
|
+
switch (label) {
|
|
1320
|
+
case 'group-count-index':
|
|
1321
|
+
return planText.includes('Aggregate(group-count-index)');
|
|
1322
|
+
case 'having-pushdown':
|
|
1323
|
+
return planText.includes('IndexGroupCountHaving(')
|
|
1324
|
+
&& !planText.includes('\nHaving(');
|
|
1325
|
+
case 'order':
|
|
1326
|
+
return planText.includes('IndexGroupCountOrder(') && !planText.includes('\nSort');
|
|
1327
|
+
case 'limit':
|
|
1328
|
+
return planText.includes('IndexGroupCountLimit') && !planText.includes('\nLimit');
|
|
1329
|
+
case 'join-index':
|
|
1330
|
+
return planText.includes('IndexJoin(')
|
|
1331
|
+
&& !planText.includes('\nIndexScan(');
|
|
1332
|
+
case 'join-order-pushdown':
|
|
1333
|
+
return planText.includes('IndexJoinOrder(')
|
|
1334
|
+
&& !planText.includes('\nSort');
|
|
1335
|
+
case 'join-limit-pushdown':
|
|
1336
|
+
return planText.includes('IndexJoinLimit')
|
|
1337
|
+
&& !planText.includes('\nLimit');
|
|
1338
|
+
case 'range-filter-pushdown':
|
|
1339
|
+
return metrics.filtersPushedDown > 0
|
|
1340
|
+
&& planText.includes('LexicalRange(');
|
|
1341
|
+
case 'join-count-index':
|
|
1342
|
+
return planText.includes('Aggregate(join-count-distinct-index)')
|
|
1343
|
+
&& planText.includes('IndexJoinCount(')
|
|
1344
|
+
&& !planText.includes('\nIndexScan(');
|
|
1345
|
+
default:
|
|
1346
|
+
return false;
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
function bindingKey(binding) {
|
|
1350
|
+
return Object.keys(binding)
|
|
1351
|
+
.sort()
|
|
1352
|
+
.map((key) => `${key}=${(0, n3_2.termToId)(binding[key])}`)
|
|
1353
|
+
.join('\u001f');
|
|
1354
|
+
}
|
|
1355
|
+
function checksum(keys, ordered) {
|
|
1356
|
+
const normalized = ordered ? keys : [...keys].sort();
|
|
1357
|
+
return (0, node_crypto_1.createHash)('sha256')
|
|
1358
|
+
.update(normalized.join('\n'))
|
|
1359
|
+
.digest('hex');
|
|
1360
|
+
}
|
|
1361
|
+
function percentile(values, percentileValue) {
|
|
1362
|
+
if (values.length === 0) {
|
|
1363
|
+
return 0;
|
|
1364
|
+
}
|
|
1365
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
1366
|
+
const index = Math.min(sorted.length - 1, Math.max(0, Math.ceil(sorted.length * percentileValue) - 1));
|
|
1367
|
+
return sorted[index];
|
|
1368
|
+
}
|
|
1369
|
+
function scaleRank(scale) {
|
|
1370
|
+
switch (scale) {
|
|
1371
|
+
case 'small':
|
|
1372
|
+
return 1;
|
|
1373
|
+
case 'medium':
|
|
1374
|
+
return 2;
|
|
1375
|
+
case 'large':
|
|
1376
|
+
return 3;
|
|
1377
|
+
default: {
|
|
1378
|
+
const exhaustive = scale;
|
|
1379
|
+
return exhaustive;
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
function serializePattern(pattern) {
|
|
1384
|
+
return Object.fromEntries(Object.entries(pattern).map(([key, value]) => [key, serializePatternValue(value)]));
|
|
1385
|
+
}
|
|
1386
|
+
function serializeLocalQuery(query) {
|
|
1387
|
+
return serializePatternValue(query);
|
|
1388
|
+
}
|
|
1389
|
+
function serializePatternValue(value) {
|
|
1390
|
+
if (!value) {
|
|
1391
|
+
return value;
|
|
1392
|
+
}
|
|
1393
|
+
if ((0, types_1.isTerm)(value)) {
|
|
1394
|
+
return (0, n3_2.termToId)(value);
|
|
1395
|
+
}
|
|
1396
|
+
if (Array.isArray(value)) {
|
|
1397
|
+
return value.map(serializePatternValue);
|
|
1398
|
+
}
|
|
1399
|
+
if (typeof value === 'object') {
|
|
1400
|
+
return Object.fromEntries(Object.entries(value)
|
|
1401
|
+
.map(([key, nested]) => [key, serializePatternValue(nested)]));
|
|
1402
|
+
}
|
|
1403
|
+
return value;
|
|
1404
|
+
}
|
|
1405
|
+
//# sourceMappingURL=models-benchmark.js.map
|