@harperfast/harper 5.3.0-beta.4 → 5.3.0
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/bin/backup.ts +6 -1
- package/components/Application.ts +486 -115
- package/components/DESIGN.md +109 -21
- package/components/deploymentProvenance.ts +28 -0
- package/components/operations.js +14 -7
- package/components/packageComponent.ts +5 -1
- package/config-root.schema.json +1 -1
- package/dataLayer/DESIGN.md +35 -7
- package/dataLayer/blobBackup.ts +71 -30
- package/dataLayer/harperBridge/ResourceBridge.ts +16 -2
- package/dataLayer/rocksdbBackup.ts +88 -34
- package/dataLayer/schemaDescribe.ts +45 -1
- package/dist/bin/backup.js +1 -1
- package/dist/bin/backup.js.map +1 -1
- package/dist/components/Application.d.ts +11 -21
- package/dist/components/Application.js +436 -105
- package/dist/components/Application.js.map +1 -1
- package/dist/components/deploymentProvenance.d.ts +4 -0
- package/dist/components/deploymentProvenance.js +32 -0
- package/dist/components/deploymentProvenance.js.map +1 -0
- package/dist/components/operations.js +14 -7
- package/dist/components/operations.js.map +1 -1
- package/dist/components/packageComponent.js +6 -1
- package/dist/components/packageComponent.js.map +1 -1
- package/dist/dataLayer/blobBackup.d.ts +30 -3
- package/dist/dataLayer/blobBackup.js +73 -36
- package/dist/dataLayer/blobBackup.js.map +1 -1
- package/dist/dataLayer/harperBridge/ResourceBridge.d.ts +1 -0
- package/dist/dataLayer/harperBridge/ResourceBridge.js +15 -3
- package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
- package/dist/dataLayer/rocksdbBackup.d.ts +4 -1
- package/dist/dataLayer/rocksdbBackup.js +74 -36
- package/dist/dataLayer/rocksdbBackup.js.map +1 -1
- package/dist/dataLayer/schemaDescribe.js +45 -1
- package/dist/dataLayer/schemaDescribe.js.map +1 -1
- package/dist/resources/DatabaseTransaction.d.ts +1 -0
- package/dist/resources/DatabaseTransaction.js +33 -8
- package/dist/resources/DatabaseTransaction.js.map +1 -1
- package/dist/resources/ResourceInterface.d.ts +7 -3
- package/dist/resources/ResourceInterface.js +2 -0
- package/dist/resources/ResourceInterface.js.map +1 -1
- package/dist/resources/Table.d.ts +151 -445
- package/dist/resources/Table.js +209 -8
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/analytics/write.d.ts +11 -0
- package/dist/resources/analytics/write.js +3 -0
- package/dist/resources/analytics/write.js.map +1 -1
- package/dist/resources/blob.d.ts +30 -0
- package/dist/resources/blob.js +189 -1
- package/dist/resources/blob.js.map +1 -1
- package/dist/resources/databases.d.ts +1 -0
- package/dist/resources/databases.js +80 -15
- package/dist/resources/databases.js.map +1 -1
- package/dist/resources/derivedIndexRuntime.d.ts +13 -0
- package/dist/resources/derivedIndexRuntime.js +57 -5
- package/dist/resources/derivedIndexRuntime.js.map +1 -1
- package/dist/resources/derivedIndexes.d.ts +1 -0
- package/dist/resources/derivedIndexes.js +116 -15
- package/dist/resources/derivedIndexes.js.map +1 -1
- package/dist/resources/fullTextSchema.d.ts +5 -3
- package/dist/resources/fullTextSchema.js +48 -5
- package/dist/resources/fullTextSchema.js.map +1 -1
- package/dist/resources/fullTextSchemaLifecycle.d.ts +2 -0
- package/dist/resources/fullTextSchemaLifecycle.js +33 -0
- package/dist/resources/fullTextSchemaLifecycle.js.map +1 -1
- package/dist/resources/graphql.js +30 -12
- package/dist/resources/graphql.js.map +1 -1
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +21 -1
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +114 -5
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
- package/dist/resources/indexes/fullTextDerivedIndex.d.ts +11 -2
- package/dist/resources/indexes/fullTextDerivedIndex.js +187 -24
- package/dist/resources/indexes/fullTextDerivedIndex.js.map +1 -1
- package/dist/resources/indexes/fullTextNativeBinding.d.ts +99 -1
- package/dist/resources/indexes/fullTextNativeBinding.js +35 -1
- package/dist/resources/indexes/fullTextNativeBinding.js.map +1 -1
- package/dist/resources/indexes/fullTextQueryIndex.d.ts +58 -0
- package/dist/resources/indexes/fullTextQueryIndex.js +943 -0
- package/dist/resources/indexes/fullTextQueryIndex.js.map +1 -0
- package/dist/resources/indexes/fullTextQueryProtocol.d.ts +6 -0
- package/dist/resources/indexes/fullTextQueryProtocol.js +32 -0
- package/dist/resources/indexes/fullTextQueryProtocol.js.map +1 -0
- package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.d.ts +4 -1
- package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.js +30 -7
- package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.js.map +1 -1
- package/dist/resources/search.d.ts +41 -0
- package/dist/resources/search.js +468 -26
- package/dist/resources/search.js.map +1 -1
- package/dist/resources/transactionBroadcast.js +101 -50
- package/dist/resources/transactionBroadcast.js.map +1 -1
- package/dist/resources/typeParity.d.ts +9 -0
- package/dist/resources/typeParity.js +3 -0
- package/dist/resources/typeParity.js.map +1 -0
- package/dist/server/REST.js.map +1 -1
- package/dist/server/itc/serverHandlers.js +48 -8
- package/dist/server/itc/serverHandlers.js.map +1 -1
- package/dist/server/serverHelpers/contentTypes.d.ts +7 -0
- package/dist/server/serverHelpers/contentTypes.js +36 -23
- package/dist/server/serverHelpers/contentTypes.js.map +1 -1
- package/dist/server/serverHelpers/uwsServer.js +28 -5
- package/dist/server/serverHelpers/uwsServer.js.map +1 -1
- package/dist/server/threads/itc.js +19 -0
- package/dist/server/threads/itc.js.map +1 -1
- package/dist/server/threads/manageThreads.d.ts +3 -0
- package/dist/server/threads/manageThreads.js +90 -23
- package/dist/server/threads/manageThreads.js.map +1 -1
- package/dist/utility/operation_authorization.js +25 -7
- package/dist/utility/operation_authorization.js.map +1 -1
- package/dist/utility/signalling.js +3 -0
- package/dist/utility/signalling.js.map +1 -1
- package/dist/validation/searchValidator.js +22 -2
- package/dist/validation/searchValidator.js.map +1 -1
- package/npm-shrinkwrap.json +22 -22
- package/package.json +2 -4
- package/resources/DESIGN.md +19 -2
- package/resources/DatabaseTransaction.ts +40 -10
- package/resources/FULL_TEXT.md +97 -0
- package/resources/ResourceInterface.ts +8 -2
- package/resources/Table.ts +767 -17
- package/resources/analytics/write.ts +3 -3
- package/resources/blob.ts +183 -1
- package/resources/databases.ts +126 -14
- package/resources/derivedIndexRuntime.ts +79 -6
- package/resources/derivedIndexes.ts +142 -18
- package/resources/fullTextSchema.ts +55 -9
- package/resources/fullTextSchemaLifecycle.ts +45 -0
- package/resources/graphql.ts +33 -12
- package/resources/indexes/DESIGN.md +167 -15
- package/resources/indexes/HierarchicalNavigableSmallWorld.ts +125 -5
- package/resources/indexes/fullTextDerivedIndex.ts +216 -32
- package/resources/indexes/fullTextNativeBinding.ts +133 -3
- package/resources/indexes/fullTextQueryIndex.ts +1120 -0
- package/resources/indexes/fullTextQueryProtocol.ts +35 -0
- package/resources/indexes/nativeFullTextDerivedIndexLifecycle.ts +28 -8
- package/resources/search.ts +513 -26
- package/resources/transactionBroadcast.ts +86 -47
- package/resources/typeParity.ts +20 -0
- package/schema.graphql +18 -5
- package/server/DESIGN.md +18 -0
- package/server/REST.ts +1 -1
- package/server/itc/serverHandlers.js +61 -8
- package/server/serverHelpers/contentTypes.ts +31 -26
- package/server/serverHelpers/uwsServer.ts +35 -7
- package/server/threads/itc.js +21 -0
- package/server/threads/manageThreads.js +95 -21
- package/studio/web/assets/{Chat-Bq60AO-g.js → Chat-CNEKMllJ.js} +13 -13
- package/studio/web/assets/FloatingChat-CEnWhHeR.js +23 -0
- package/studio/web/assets/SearchDialog-CwhJdEdY.js +1 -0
- package/studio/web/assets/{abnfDiagram-O67JEVCF-DpPZWMOf.js → abnfDiagram-O67JEVCF-raXE027J.js} +1 -1
- package/studio/web/assets/alertDialog-BbWD9Xxa.js +1 -0
- package/studio/web/assets/apiToken-DC5vsaaO.js +1 -0
- package/studio/web/assets/applications-DQZxDy0x.js +296 -0
- package/studio/web/assets/architecture-7GRP2DOG-DDmz3hpI.js +1 -0
- package/studio/web/assets/{architectureDiagram-NJMV4G6O-OmgLraID.js → architectureDiagram-NJMV4G6O-C8_Eet7w.js} +1 -1
- package/studio/web/assets/arrow-up-D2g_57vT.js +1 -0
- package/studio/web/assets/authStore-CJCaADw9.js +3 -0
- package/studio/web/assets/{blockDiagram-BEXU5L5S-Cv7CD3Ai.js → blockDiagram-BEXU5L5S-DR6S5-1D.js} +1 -1
- package/studio/web/assets/{button-MFP9n8Ck.js → button-DWwfgGWI.js} +2 -2
- package/studio/web/assets/{c4Diagram-YGBWAQC7-Ds1vKYnN.js → c4Diagram-YGBWAQC7-BAfZ1G01.js} +1 -1
- package/studio/web/assets/channel-D0AkPjJ8.js +1 -0
- package/studio/web/assets/{chevron-up-CFe8x5Bp.js → chevron-up-CFxbMGv2.js} +1 -1
- package/studio/web/assets/{chunk-3FUC2YCW-DpnBPH8B.js → chunk-3FUC2YCW-DuRp-j19.js} +1 -1
- package/studio/web/assets/{chunk-5DYCD2WN-LLLCQSzg.js → chunk-5DYCD2WN-BceBfIHT.js} +1 -1
- package/studio/web/assets/{chunk-742MDFTN-BL8J_pIO.js → chunk-742MDFTN-CIkt8ijt.js} +1 -1
- package/studio/web/assets/{chunk-7INBJB4K-C-hJHiiK.js → chunk-7INBJB4K-BpeAXyOi.js} +1 -1
- package/studio/web/assets/{chunk-7M6MHVWA-C6ZqkFbA.js → chunk-7M6MHVWA-h6WlaTzO.js} +1 -1
- package/studio/web/assets/{chunk-7PRAP22T-CUXM8BG6.js → chunk-7PRAP22T-KyqX6x3f.js} +1 -1
- package/studio/web/assets/{chunk-GTNCS2PH-DJ2BFvS1.js → chunk-GTNCS2PH-DGHLXyow.js} +1 -1
- package/studio/web/assets/{chunk-GWA4HPMP-C42bSqLL.js → chunk-GWA4HPMP-DsejB6gA.js} +1 -1
- package/studio/web/assets/{chunk-MBY4JIJT-C-5jz6Lo.js → chunk-MBY4JIJT-BwSGweQP.js} +1 -1
- package/studio/web/assets/{chunk-NETBCI7D-D-bzlN4r.js → chunk-NETBCI7D-C8UnOw_f.js} +1 -1
- package/studio/web/assets/{chunk-O7XYJQB3-ep14zA23.js → chunk-O7XYJQB3-CBS3nZYV.js} +1 -1
- package/studio/web/assets/{chunk-UA2S7LBM-BMRWRHED.js → chunk-UA2S7LBM-BHpEdH8D.js} +1 -1
- package/studio/web/assets/{chunk-WEXAMYUT-DdLYVdX1.js → chunk-WEXAMYUT-BAIhTwyh.js} +1 -1
- package/studio/web/assets/{chunk-Z7XXMR3K-BmKEDjcP.js → chunk-Z7XXMR3K-DSHbImdf.js} +1 -1
- package/studio/web/assets/{chunk-ZIGJFQKS-DLZgdNxA.js → chunk-ZIGJFQKS-Cs8fUn7y.js} +1 -1
- package/studio/web/assets/{classDiagram-v2-NBCMYWYE-DOI5vgKT.js → classDiagram-v2-NBCMYWYE-DmDPkt2u.js} +1 -1
- package/studio/web/assets/cn-6T8VranJ.js +1 -0
- package/studio/web/assets/{codicon-Brq4_Ui5.ttf → codicon-DF1abBS2.ttf} +0 -0
- package/studio/web/assets/{createLucideIcon-bziYxJli.js → createLucideIcon-fO5dL7D5.js} +1 -1
- package/studio/web/assets/{css.worker-l1psR_wG.js → css.worker-DoT-OOzx.js} +36 -36
- package/studio/web/assets/{cssMode-DJyc9mHj.js → cssMode-Dz2DxF4e.js} +1 -1
- package/studio/web/assets/{cynefin-OW5HDTMX-BSW-DB7S.js → cynefin-OW5HDTMX-DIvbXLSj.js} +1 -1
- package/studio/web/assets/{cynefinDiagram-VND7K2PF-CbAHEwxt.js → cynefinDiagram-VND7K2PF-BdtaXiCE.js} +1 -1
- package/studio/web/assets/{dagre-6A5THRUB-Csxewgs4.js → dagre-6A5THRUB-C7RUWHyz.js} +1 -1
- package/studio/web/assets/{diagram-22UHCM2B-a0rt8tY4.js → diagram-22UHCM2B-DJCvcaWP.js} +1 -1
- package/studio/web/assets/{diagram-3UASUU5V-lEj2SVNh.js → diagram-3UASUU5V-Du6VKWln.js} +1 -1
- package/studio/web/assets/{diagram-ATOU4E4O-HcrAeB-q.js → diagram-ATOU4E4O-dxRfeG4B.js} +1 -1
- package/studio/web/assets/{diagram-CDSNMT55-CYxiuyEx.js → diagram-CDSNMT55-l5Ga0u-W.js} +1 -1
- package/studio/web/assets/{diagram-MLGK6HIB-ChbCJTgJ.js → diagram-MLGK6HIB-CNQY47LA.js} +1 -1
- package/studio/web/assets/{diagram-MPIPVDR6-DaX9Y-N3.js → diagram-MPIPVDR6-7prveD1B.js} +1 -1
- package/studio/web/assets/{dist-Cz2Y6-6G.js → dist-BrRuMUzt.js} +1 -1
- package/studio/web/assets/{download-CtostjYn.js → download-Cn6tPG4L.js} +1 -1
- package/studio/web/assets/{ebnfDiagram-ZINNZB2B-CwAsENB1.js → ebnfDiagram-ZINNZB2B-5OnkTpfi.js} +1 -1
- package/studio/web/assets/{editor-DDIsQfj6.js → editor-B3mjd3tz.js} +1 -1
- package/studio/web/assets/editor.worker-Yd1NR0QT.js +33 -0
- package/studio/web/assets/{elk-276RUBZZ-Hexg9E4D.js → elk-276RUBZZ-DxDQPjQT.js} +1 -1
- package/studio/web/assets/{erDiagram-OPXOYQCR-CeC-wwO1.js → erDiagram-OPXOYQCR-CQlCX84D.js} +1 -1
- package/studio/web/assets/eventmodeling-NTZA5JFV-DONQf5aG.js +1 -0
- package/studio/web/assets/flowDiagram-KWPJA3E3-BoZ8_QR1.js +1 -0
- package/studio/web/assets/{ganttDiagram-FUAMR5RP-C2_Hhl1-.js → ganttDiagram-FUAMR5RP-bloX3Voy.js} +1 -1
- package/studio/web/assets/{getAnalytics-CjthMQqF.js → getAnalytics-C0FHjtdQ.js} +1 -1
- package/studio/web/assets/{gitGraph-4MIJSDKK-xIk5Vuew.js → gitGraph-4MIJSDKK-BrXWmJt2.js} +1 -1
- package/studio/web/assets/{gitGraphDiagram-X574FWY7-CMxJ34up.js → gitGraphDiagram-X574FWY7-C2KnnZeY.js} +1 -1
- package/studio/web/assets/{html-g1BJHxLy.js → html-BsRGM7tp.js} +1 -1
- package/studio/web/assets/{html.worker-DdwM4IrA.js → html.worker-B1dwY0Ay.js} +27 -27
- package/studio/web/assets/{htmlMode-Vh5IglCQ.js → htmlMode-tUKJaA3o.js} +1 -1
- package/studio/web/assets/index-72cEH474.css +1 -0
- package/studio/web/assets/index-BuqFjN5K.js +828 -0
- package/studio/web/assets/index.lazy-CcpfQFY_.js +2 -0
- package/studio/web/assets/{info-A6RAGUB7-o49TEbj-.js → info-A6RAGUB7-BhzBHe4U.js} +1 -1
- package/studio/web/assets/{infoDiagram-VRGFBTTK-Cjhh7yzZ.js → infoDiagram-VRGFBTTK-B0J1132p.js} +1 -1
- package/studio/web/assets/{ishikawaDiagram-OU5B5YK6-C63aXRmh.js → ishikawaDiagram-OU5B5YK6-BNk0tuQx.js} +1 -1
- package/studio/web/assets/{javascript-5VIolqf7.js → javascript-BZ_uRGHl.js} +1 -1
- package/studio/web/assets/{journeyDiagram-ZHPQQLJL-D_o0P5Mu.js → journeyDiagram-ZHPQQLJL-Be79RMQM.js} +1 -1
- package/studio/web/assets/json.worker-BQqXGI3X.js +65 -0
- package/studio/web/assets/{jsonMode-DG4zQraI.js → jsonMode-DgB-aFMj.js} +1 -1
- package/studio/web/assets/{kanban-definition-PNTS6WVX-CTI9SSxY.js → kanban-definition-PNTS6WVX-4uuoQP9W.js} +1 -1
- package/studio/web/assets/{languageServices-t5FBpX-i.js → languageServices-BleuBw17.js} +1 -1
- package/studio/web/assets/localStorageKeys-etzFY_xV.js +1 -0
- package/studio/web/assets/{lspLanguageFeatures-DgVnNPh-.js → lspLanguageFeatures-ByVYLSsQ.js} +1 -1
- package/studio/web/assets/{mermaid-parser.core-C2lcH_jP.js → mermaid-parser.core-V1UYMeXV.js} +3 -3
- package/studio/web/assets/{mermaid.core-BG5bjfIG.js → mermaid.core-1PX6R49U.js} +4 -4
- package/studio/web/assets/{mindmap-definition-NLK3R4M7-BK7ho6JD.js → mindmap-definition-NLK3R4M7-C70Ctm0J.js} +1 -1
- package/studio/web/assets/notifications-D1Nf-Edn.js +1 -0
- package/studio/web/assets/notifications-TuzN0IMi.js +1 -0
- package/studio/web/assets/{packet-AYTQ26CC-BHPy6KBV.js → packet-AYTQ26CC-DWbXHx5g.js} +1 -1
- package/studio/web/assets/{pegDiagram-GJSIUBJH-DzjwG3jO.js → pegDiagram-GJSIUBJH-d6z1cc7W.js} +1 -1
- package/studio/web/assets/{pie-WAS4IAKB-D0cP-DTU.js → pie-WAS4IAKB-DjoTm3yr.js} +1 -1
- package/studio/web/assets/{pieDiagram-5QR66LMP-C_hOP9Lv.js → pieDiagram-5QR66LMP-Dz3RHpDA.js} +1 -1
- package/studio/web/assets/profile-D7C7Lupz.js +1 -0
- package/studio/web/assets/{quadrantDiagram-O4NWA36T-CZW8PbV1.js → quadrantDiagram-O4NWA36T-CIF5Qkou.js} +1 -1
- package/studio/web/assets/{radar-RG4KPBEZ-C8fk0U81.js → radar-RG4KPBEZ-CVBgjCOj.js} +1 -1
- package/studio/web/assets/{railroad-74A4TZTK-B60FWm17.js → railroad-74A4TZTK-BdhXw10Q.js} +1 -1
- package/studio/web/assets/railroad-abnf-HS5TGJTU-BcoQPN39.js +1 -0
- package/studio/web/assets/railroad-ebnf-LZEXJU2U-B1Lke2JN.js +1 -0
- package/studio/web/assets/railroad-peg-WCYAUIDC-D-oJFkiV.js +1 -0
- package/studio/web/assets/{railroadDiagram-XR7U4H2S-DyyNmgbU.js → railroadDiagram-XR7U4H2S-y3Jv2U6N.js} +1 -1
- package/studio/web/assets/regions-nGQCm-v8.js +1 -0
- package/studio/web/assets/register-C3x9Ze6P.js +909 -0
- package/studio/web/assets/register-DjpsE8to.css +1 -0
- package/studio/web/assets/{requirementDiagram-PLB6GJNP-C4H-mLKu.js → requirementDiagram-PLB6GJNP-ChINZEmu.js} +1 -1
- package/studio/web/assets/restartGate-nHAFLDh-.js +1 -0
- package/studio/web/assets/{sankeyDiagram-IPEJSGJF-DHDyga4c.js → sankeyDiagram-IPEJSGJF-D9AyLce2.js} +1 -1
- package/studio/web/assets/{sequenceDiagram-PO4LG4MO-CdNcHXRI.js → sequenceDiagram-PO4LG4MO-hfk4aIEm.js} +1 -1
- package/studio/web/assets/{setComponentFile-z4-qJCbA.js → setComponentFile-DpjrRqeG.js} +1 -1
- package/studio/web/assets/{setup-CS50uJ4x.js → setup-CGiKLZ9e.js} +2 -2
- package/studio/web/assets/{stateDiagram-v2-GCMORJYK-CpKuXq15.js → stateDiagram-v2-GCMORJYK-Bs8RIb9K.js} +1 -1
- package/studio/web/assets/status-CGJwrNPH.js +61 -0
- package/studio/web/assets/{swimlanes-2SLR337P-NRHa2D8I.js → swimlanes-2SLR337P-B52xrf6t.js} +1 -1
- package/studio/web/assets/swimlanesDiagram-TC7HE7FX-DfN7BAP5.js +8 -0
- package/studio/web/assets/tabs-BCTQzoQK.js +1 -0
- package/studio/web/assets/{timeline-definition-EJHVYXUP-D39Y-xpq.js → timeline-definition-EJHVYXUP-DJSITpQX.js} +1 -1
- package/studio/web/assets/toggleHighContrast-C-Ne6P7T.js +51 -0
- package/studio/web/assets/toggleHighContrast-n7clI0si.css +1 -0
- package/studio/web/assets/{treeView-Q6P3EWNA-CRWR9OqD.js → treeView-Q6P3EWNA-C-epdxRo.js} +1 -1
- package/studio/web/assets/{treemap-WGGIJYW6-ZfXyJgLO.js → treemap-WGGIJYW6-DOVyp_zw.js} +1 -1
- package/studio/web/assets/{ts.worker-DDMUrpqh.js → ts.worker-Clm12uY6.js} +131 -131
- package/studio/web/assets/{tsMode-D83Gg3cq.js → tsMode-CSreYTys.js} +1 -1
- package/studio/web/assets/{typescript-Dfp-GrNq.js → typescript-MnxBk89W.js} +1 -1
- package/studio/web/assets/{useEntityRestURL-BKetlINf.js → useEntityRestURL-TNrh77-s.js} +1 -1
- package/studio/web/assets/{usecaseDiagram-POWQR4AR-jf-zIDZ_.js → usecaseDiagram-POWQR4AR-B6252cbq.js} +1 -1
- package/studio/web/assets/{vendor-core-BdkdbUek.js → vendor-core-6LhfJlRC.js} +1 -1
- package/studio/web/assets/vendor-datadog-ozKf-pts.js +6 -0
- package/studio/web/assets/{vendor-react-izWchTRL.js → vendor-react-BOliS0BL.js} +1 -1
- package/studio/web/assets/vendor-tanstack-Bntfka-M.js +2 -0
- package/studio/web/assets/vendor-ui-9EYYDgoN.js +41 -0
- package/studio/web/assets/{vennDiagram-UO4OBE2U-BKYAZUh_.js → vennDiagram-UO4OBE2U-DZTsDA4k.js} +1 -1
- package/studio/web/assets/{wardley-WFR3VGLG-BO7f36ci.js → wardley-WFR3VGLG-Cfqoe6z4.js} +1 -1
- package/studio/web/assets/{wardleyDiagram-VNRHLVJA-Djb0JDQc.js → wardleyDiagram-VNRHLVJA-qu_f7Tvl.js} +1 -1
- package/studio/web/assets/{workers-CCvmDLH3.js → workers-BfZlUNVI.js} +3 -3
- package/studio/web/assets/{xml-06pZp00U.js → xml-BHd6qGH7.js} +1 -1
- package/studio/web/assets/{xychartDiagram-PMCCYNJV-BQh9ubvb.js → xychartDiagram-PMCCYNJV-UrOKRsmR.js} +1 -1
- package/studio/web/assets/{yaml-iSnWMVlV.js → yaml-C53R8OQq.js} +1 -1
- package/studio/web/assets/{yaml.worker-DAK2o_H1.js → yaml.worker-D9JekGdp.js} +183 -183
- package/studio/web/index.html +15 -15
- package/utility/operation_authorization.ts +27 -7
- package/utility/signalling.ts +3 -0
- package/validation/searchValidator.ts +22 -13
- package/studio/web/assets/FloatingChat-D4560T0p.js +0 -23
- package/studio/web/assets/alertDialog-UrbiMgQt.js +0 -1
- package/studio/web/assets/apiToken-BEvbFPMc.js +0 -1
- package/studio/web/assets/applications-CSUVTH4E.js +0 -296
- package/studio/web/assets/architecture-7GRP2DOG-11l4Ny1l.js +0 -1
- package/studio/web/assets/authStore-5jSS5pTW.js +0 -3
- package/studio/web/assets/channel-BDtouPd6.js +0 -1
- package/studio/web/assets/dialog-DTepachC.js +0 -1
- package/studio/web/assets/editor.worker-CkkvdXvO.js +0 -33
- package/studio/web/assets/eventmodeling-NTZA5JFV-CFb03e0o.js +0 -1
- package/studio/web/assets/flowDiagram-KWPJA3E3-Cidi00_Y.js +0 -1
- package/studio/web/assets/index-CPoDzQTe.css +0 -1
- package/studio/web/assets/index-DGRAzvO4.js +0 -824
- package/studio/web/assets/index.lazy-vgSic7et.js +0 -2
- package/studio/web/assets/json.worker-CSi2hF-a.js +0 -65
- package/studio/web/assets/notifications-BeWcX04m.js +0 -1
- package/studio/web/assets/notifications-DUGWrWoO.js +0 -1
- package/studio/web/assets/profile-BvUEDBzW.js +0 -1
- package/studio/web/assets/railroad-abnf-HS5TGJTU-CLMVZvH0.js +0 -1
- package/studio/web/assets/railroad-ebnf-LZEXJU2U--tlwFoVB.js +0 -1
- package/studio/web/assets/railroad-peg-WCYAUIDC-Dpuslblm.js +0 -1
- package/studio/web/assets/regions-CB-_2m7M.js +0 -1
- package/studio/web/assets/register-1w1Zr3b2.js +0 -848
- package/studio/web/assets/register-jjEx9u7D.css +0 -1
- package/studio/web/assets/setLocalStorage-D_kflv4U.js +0 -1
- package/studio/web/assets/status-CI0wOdOE.js +0 -61
- package/studio/web/assets/swimlanesDiagram-TC7HE7FX-BBk4t4hO.js +0 -8
- package/studio/web/assets/tabs-CnQ_zenf.js +0 -1
- package/studio/web/assets/toggleHighContrast--ixms01u.css +0 -1
- package/studio/web/assets/toggleHighContrast-BsAE4jPe.js +0 -49
- package/studio/web/assets/useLocalStorage-BYoZF60G.js +0 -1
- package/studio/web/assets/vendor-datadog-fCQhBHGk.js +0 -6
- package/studio/web/assets/vendor-tanstack-CQ1aRjVa.js +0 -2
- package/studio/web/assets/vendor-ui-aE1tHcW7.js +0 -41
- package/studio/web/assets/x-vYntCxm4.js +0 -1
package/dist/resources/search.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.COERCIBLE_OPERATORS = void 0;
|
|
3
|
+
exports.CandidateKeySet = exports.COERCIBLE_OPERATORS = exports.fullTextComparatorMode = void 0;
|
|
4
4
|
exports.executeConditions = executeConditions;
|
|
5
5
|
exports.searchByIndex = searchByIndex;
|
|
6
6
|
exports.findAttribute = findAttribute;
|
|
@@ -22,6 +22,8 @@ const DatabaseTransaction_ts_1 = require("./DatabaseTransaction.js");
|
|
|
22
22
|
const write_1 = require("./analytics/write");
|
|
23
23
|
const rocksdb_js_1 = require("@harperfast/rocksdb-js");
|
|
24
24
|
const Headers_ts_1 = require("../server/serverHelpers/Headers.js");
|
|
25
|
+
const fullTextQueryProtocol_ts_1 = require("./indexes/fullTextQueryProtocol.js");
|
|
26
|
+
Object.defineProperty(exports, "fullTextComparatorMode", { enumerable: true, get: function () { return fullTextQueryProtocol_ts_1.fullTextComparatorMode; } });
|
|
25
27
|
// these are ratios/percentages of overall table size
|
|
26
28
|
const OPEN_RANGE_ESTIMATE = 0.3;
|
|
27
29
|
const BETWEEN_ESTIMATE = 0.1;
|
|
@@ -70,7 +72,12 @@ exports.COERCIBLE_OPERATORS = {
|
|
|
70
72
|
eq: true,
|
|
71
73
|
};
|
|
72
74
|
function executeConditions(conditions, operator, table, txn, request, context, transformToEntries, filtered, recordAccess) {
|
|
75
|
+
conditions = combineFullTextConditions(conditions, operator, table);
|
|
73
76
|
const firstSearch = conditions[0];
|
|
77
|
+
// A same-index full-text OR is already one native expression. Route that single traversal through
|
|
78
|
+
// the AND path so row and vector guards are pushed down before the requested page is filled.
|
|
79
|
+
if (operator === 'or' && conditions.length === 1 && firstSearch.fullTextQuery)
|
|
80
|
+
operator = 'and';
|
|
74
81
|
// Record-level guards (caller-supplied vectorFilter + rowFilter) apply to every record
|
|
75
82
|
// the query returns, independent of which condition leads (#1241). `recordAccess` is supplied only on
|
|
76
83
|
// the top-level executeConditions call (Table.search) and deliberately NOT threaded into the recursive
|
|
@@ -104,18 +111,27 @@ function executeConditions(conditions, operator, table, txn, request, context, t
|
|
|
104
111
|
}
|
|
105
112
|
else {
|
|
106
113
|
// AND: use the indexed query for the first condition and filter by all subsequent conditions.
|
|
107
|
-
|
|
114
|
+
const pushdownIndex = filterablePushdownIndex(firstSearch, table);
|
|
115
|
+
if (pushdownIndex) {
|
|
108
116
|
// The lead is a filterable custom index (HNSW). It doesn't populate the join map (`filtered`)
|
|
109
117
|
// that sibling filters read, so — unlike the general case below — we can build the sibling
|
|
110
118
|
// filters BEFORE executing the lead and push them into the traversal, so filtering happens
|
|
111
119
|
// during the graph walk instead of post-filtering an under-filled candidate set (#1241). They
|
|
112
120
|
// also stay in the post-filter chain as a deterministic, harmless re-check.
|
|
113
|
-
const
|
|
121
|
+
const siblings = conditions.slice(1);
|
|
122
|
+
const filters = mapConditionsToFilters(siblings, true, firstSearch.estimated_count);
|
|
114
123
|
const recordFilters = recordGuards ? filters.concat(recordGuards) : filters;
|
|
124
|
+
// The sibling conditions a secondary-index range scan answers exactly can seed an allow-set
|
|
125
|
+
// the index admits from, instead of loading and decoding a record at every visited node.
|
|
126
|
+
// The post-filter chain below stays whole, so the set only has to be a SUPERSET of the true
|
|
127
|
+
// matches — the index decides whether building it beats evaluating the predicate.
|
|
128
|
+
const candidateKeys = pushdownIndex.candidateKeyFilter && siblings.length > 0
|
|
129
|
+
? planCandidateKeys(siblings, table, txn, !recordGuards)
|
|
130
|
+
: undefined;
|
|
115
131
|
// Execute a COPY carrying the pushed-down predicate, never mutating the caller's condition
|
|
116
132
|
// object (which may be reused across requests with different users / guards).
|
|
117
133
|
const lead = recordFilters.length > 0
|
|
118
|
-
? { ...firstSearch, recordFilter: composeRecordFilter(recordFilters, table, context) }
|
|
134
|
+
? { ...firstSearch, recordFilter: composeRecordFilter(recordFilters, table, context), candidateKeys }
|
|
119
135
|
: firstSearch;
|
|
120
136
|
const results = executeCondition(lead);
|
|
121
137
|
return recordFilters.length > 0 ? transformToEntries(results, recordFilters) : results;
|
|
@@ -137,6 +153,7 @@ function executeConditions(conditions, operator, table, txn, request, context, t
|
|
|
137
153
|
filtered,
|
|
138
154
|
context,
|
|
139
155
|
minResults: request.limit === 0 ? 0 : request.limit !== undefined ? (request.offset || 0) + request.limit : undefined,
|
|
156
|
+
resultOffset: request.offset || 0,
|
|
140
157
|
});
|
|
141
158
|
}
|
|
142
159
|
function mapConditionsToFilters(conditions, intersection, estimatedIncomingCount) {
|
|
@@ -181,13 +198,141 @@ function buildRecordGuards(recordAccess) {
|
|
|
181
198
|
guards.push(recordAccess.rowFilter);
|
|
182
199
|
return guards.length > 0 ? guards : undefined;
|
|
183
200
|
}
|
|
184
|
-
|
|
185
|
-
|
|
201
|
+
function conditionIndex(table, attributeName, preferFullText = false) {
|
|
202
|
+
if (Array.isArray(attributeName) && attributeName.length === 1)
|
|
203
|
+
attributeName = attributeName[0];
|
|
204
|
+
if (typeof attributeName !== 'string')
|
|
205
|
+
return;
|
|
206
|
+
return preferFullText ? table.fullTextQueryIndexes?.[attributeName] : table.indices?.[attributeName];
|
|
207
|
+
}
|
|
208
|
+
function combineFullTextConditions(conditions, operator, table) {
|
|
209
|
+
if (table.hasFullTextQueryIndexes === false || !containsFullTextCondition(conditions, table))
|
|
210
|
+
return conditions;
|
|
211
|
+
let indexName;
|
|
212
|
+
let positive = false;
|
|
213
|
+
const expressions = [];
|
|
214
|
+
const leaves = [];
|
|
215
|
+
const remaining = [];
|
|
216
|
+
let includeHighlights = false;
|
|
217
|
+
let maxIndexLagMilliseconds;
|
|
218
|
+
let waitForIndexMilliseconds;
|
|
219
|
+
for (const condition of conditions) {
|
|
220
|
+
const compiled = compileFullTextExpression(condition, table);
|
|
221
|
+
if (!compiled) {
|
|
222
|
+
remaining.push(condition);
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (operator === 'or' && compiled.remaining?.length)
|
|
226
|
+
throw new hdbError_ts_1.ClientError('A full-text OR group cannot mix full-text and record conditions', 400);
|
|
227
|
+
if (indexName && indexName !== compiled.indexName)
|
|
228
|
+
throw new hdbError_ts_1.ClientError('One query cannot combine conditions from different full-text indexes', 400);
|
|
229
|
+
indexName = compiled.indexName;
|
|
230
|
+
expressions.push(compiled.expression);
|
|
231
|
+
leaves.push(...compiled.leaves);
|
|
232
|
+
positive ||= compiled.positive;
|
|
233
|
+
includeHighlights ||= compiled.includeHighlights;
|
|
234
|
+
maxIndexLagMilliseconds = mergeFullTextOption('maxIndexLagMilliseconds', maxIndexLagMilliseconds, compiled.maxIndexLagMilliseconds);
|
|
235
|
+
waitForIndexMilliseconds = mergeFullTextOption('waitForIndexMilliseconds', waitForIndexMilliseconds, compiled.waitForIndexMilliseconds);
|
|
236
|
+
if (compiled.remaining)
|
|
237
|
+
remaining.push(...compiled.remaining);
|
|
238
|
+
}
|
|
239
|
+
if (!indexName)
|
|
240
|
+
return conditions;
|
|
241
|
+
if (operator === 'or' && remaining.length > 0)
|
|
242
|
+
throw new hdbError_ts_1.ClientError('A full-text OR group can only contain conditions on the same full-text index', 400);
|
|
243
|
+
if (!positive)
|
|
244
|
+
throw new hdbError_ts_1.ClientError('A full-text query requires at least one non-negated condition', 400);
|
|
245
|
+
const expression = expressions.length === 1 ? expressions[0] : { operator: operator === 'or' ? 'or' : 'and', clauses: expressions };
|
|
246
|
+
const combined = {
|
|
247
|
+
attribute: indexName,
|
|
248
|
+
comparator: 'matches',
|
|
249
|
+
value: '',
|
|
250
|
+
fullTextQuery: expression,
|
|
251
|
+
fullTextLeaves: leaves,
|
|
252
|
+
includeHighlights,
|
|
253
|
+
...(maxIndexLagMilliseconds === undefined ? null : { maxIndexLagMilliseconds }),
|
|
254
|
+
...(waitForIndexMilliseconds === undefined ? null : { waitForIndexMilliseconds }),
|
|
255
|
+
};
|
|
256
|
+
return operator === 'or' ? [combined] : [combined, ...remaining];
|
|
257
|
+
}
|
|
258
|
+
function containsFullTextCondition(conditions, table) {
|
|
259
|
+
for (const condition of conditions) {
|
|
260
|
+
if (condition.conditions) {
|
|
261
|
+
if (containsFullTextCondition(condition.conditions, table))
|
|
262
|
+
return true;
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
const indexName = condition[0] ?? condition.attribute;
|
|
266
|
+
if (typeof indexName === 'string' &&
|
|
267
|
+
table.fullTextQueryIndexes?.[indexName] &&
|
|
268
|
+
(0, fullTextQueryProtocol_ts_1.fullTextComparatorMode)(condition.comparator))
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
function compileFullTextExpression(condition, table) {
|
|
274
|
+
if (condition.conditions) {
|
|
275
|
+
const children = condition.conditions.map((child) => compileFullTextExpression(child, table));
|
|
276
|
+
const fullTextChildren = children.filter(Boolean);
|
|
277
|
+
if (fullTextChildren.length === 0)
|
|
278
|
+
return;
|
|
279
|
+
const groupOperator = condition.operator === 'or' ? 'or' : 'and';
|
|
280
|
+
const remaining = condition.conditions.filter((_child, index) => !children[index]);
|
|
281
|
+
for (const child of fullTextChildren)
|
|
282
|
+
if (child.remaining)
|
|
283
|
+
remaining.push(...child.remaining);
|
|
284
|
+
if (groupOperator === 'or' && remaining.length > 0)
|
|
285
|
+
throw new hdbError_ts_1.ClientError('A full-text OR group cannot mix full-text and record conditions', 400);
|
|
286
|
+
const indexName = fullTextChildren[0].indexName;
|
|
287
|
+
if (fullTextChildren.some((child) => child.indexName !== indexName))
|
|
288
|
+
throw new hdbError_ts_1.ClientError('One query cannot combine conditions from different full-text indexes', 400);
|
|
289
|
+
return {
|
|
290
|
+
indexName,
|
|
291
|
+
expression: { operator: groupOperator, clauses: fullTextChildren.map((child) => child.expression) },
|
|
292
|
+
leaves: fullTextChildren.flatMap((child) => child.leaves),
|
|
293
|
+
positive: fullTextChildren.some((child) => child.positive),
|
|
294
|
+
includeHighlights: fullTextChildren.some((child) => child.includeHighlights),
|
|
295
|
+
maxIndexLagMilliseconds: fullTextChildren.reduce((value, child) => mergeFullTextOption('maxIndexLagMilliseconds', value, child.maxIndexLagMilliseconds), undefined),
|
|
296
|
+
waitForIndexMilliseconds: fullTextChildren.reduce((value, child) => mergeFullTextOption('waitForIndexMilliseconds', value, child.waitForIndexMilliseconds), undefined),
|
|
297
|
+
...(remaining.length > 0 ? { remaining } : null),
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
const indexName = condition[0] ?? condition.attribute;
|
|
301
|
+
if (typeof indexName !== 'string' || !table.fullTextQueryIndexes?.[indexName])
|
|
302
|
+
return;
|
|
303
|
+
const mode = (0, fullTextQueryProtocol_ts_1.fullTextComparatorMode)(condition.comparator);
|
|
304
|
+
if (!mode)
|
|
305
|
+
return;
|
|
306
|
+
if (typeof (condition[1] ?? condition.value) !== 'string')
|
|
307
|
+
throw new hdbError_ts_1.ClientError(`Full-text index '${indexName}' requires a full-text comparator and string value`, 400);
|
|
308
|
+
const text = condition[1] ?? condition.value;
|
|
309
|
+
const leaf = { text, mode, ...(condition.fields ? { fields: condition.fields } : null) };
|
|
310
|
+
return {
|
|
311
|
+
indexName,
|
|
312
|
+
expression: condition.negated ? { operator: 'not', clause: leaf } : leaf,
|
|
313
|
+
leaves: condition.negated ? [] : [leaf],
|
|
314
|
+
positive: !condition.negated,
|
|
315
|
+
includeHighlights: condition.includeHighlights === true,
|
|
316
|
+
maxIndexLagMilliseconds: condition.maxIndexLagMilliseconds,
|
|
317
|
+
waitForIndexMilliseconds: condition.waitForIndexMilliseconds,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
function mergeFullTextOption(name, current, next) {
|
|
321
|
+
if (next === undefined)
|
|
322
|
+
return current;
|
|
323
|
+
if (current !== undefined && current !== next)
|
|
324
|
+
throw new hdbError_ts_1.ClientError(`Combined full-text conditions must use the same ${name}`, 400);
|
|
325
|
+
return next;
|
|
326
|
+
}
|
|
327
|
+
function filterablePushdownIndex(condition, table) {
|
|
186
328
|
const attributeName = condition?.attribute ?? condition?.[0];
|
|
187
329
|
if (attributeName == null || table == null)
|
|
188
|
-
return
|
|
189
|
-
const
|
|
190
|
-
|
|
330
|
+
return undefined;
|
|
331
|
+
const fullTextMode = Boolean((0, fullTextQueryProtocol_ts_1.fullTextComparatorMode)(condition?.comparator));
|
|
332
|
+
const index = attributeName === table.primaryKey && !fullTextMode
|
|
333
|
+
? table.primaryStore
|
|
334
|
+
: conditionIndex(table, attributeName, fullTextMode);
|
|
335
|
+
return index?.customIndex?.filteredSearch ? index.customIndex : undefined;
|
|
191
336
|
}
|
|
192
337
|
/**
|
|
193
338
|
* Compose a set of record predicates into the single `(primaryKey) => boolean` the custom index calls
|
|
@@ -198,12 +343,12 @@ function isFilterablePushdown(condition, table) {
|
|
|
198
343
|
function composeRecordFilter(recordFilters, table, context) {
|
|
199
344
|
const memo = new Map();
|
|
200
345
|
const transaction = context && table._readTxnForContext ? table._readTxnForContext(context) : undefined;
|
|
201
|
-
return (primaryKey) => {
|
|
346
|
+
return (primaryKey, suppliedEntry) => {
|
|
202
347
|
const cached = memo.get(primaryKey);
|
|
203
348
|
if (cached !== undefined)
|
|
204
349
|
return cached;
|
|
205
350
|
let verdict = false;
|
|
206
|
-
const entry = table.primaryStore.getEntry(primaryKey, { transaction });
|
|
351
|
+
const entry = suppliedEntry ?? table.primaryStore.getEntry(primaryKey, { transaction });
|
|
207
352
|
const record = entry?.value;
|
|
208
353
|
if (record != null) {
|
|
209
354
|
(0, Table_ts_1.freezeRecord)(record);
|
|
@@ -219,6 +364,263 @@ function composeRecordFilter(recordFilters, table, context) {
|
|
|
219
364
|
return verdict;
|
|
220
365
|
};
|
|
221
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* Primary keys by storage identity. Strings and non-zero numbers are their own identity, which spares
|
|
369
|
+
* encoding the common key shapes, and the stores encode -0 and 0 apart where Set membership does not.
|
|
370
|
+
* The rest go by writeKeyId, the encoder the stores index by — flattenKey joins composite keys with
|
|
371
|
+
* NUL and collapses ['a', 'b\0c'] and ['a\0b', 'c'] — kept apart from the values because an encoded
|
|
372
|
+
* identity is a string that can equal a string key: writeKeyId(['a', 'b']) is 'a\0b'. An index scan
|
|
373
|
+
* decodes an int64 as the number it encodes as when that is exact, while a traversal may report the
|
|
374
|
+
* BigInt, so the two are the same key.
|
|
375
|
+
*/
|
|
376
|
+
class CandidateKeySet {
|
|
377
|
+
#byValue = new Set();
|
|
378
|
+
#encoded = new Set();
|
|
379
|
+
#zero = false;
|
|
380
|
+
#negativeZero = false;
|
|
381
|
+
#holdsBigInt = false;
|
|
382
|
+
add(primaryKey) {
|
|
383
|
+
if (typeof primaryKey === 'string' || (typeof primaryKey === 'number' && primaryKey !== 0))
|
|
384
|
+
this.#byValue.add(primaryKey);
|
|
385
|
+
else if (primaryKey === 0) {
|
|
386
|
+
if (Object.is(primaryKey, -0))
|
|
387
|
+
this.#negativeZero = true;
|
|
388
|
+
else
|
|
389
|
+
this.#zero = true;
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
if (typeof primaryKey === 'bigint')
|
|
393
|
+
this.#holdsBigInt = true;
|
|
394
|
+
this.#encoded.add((0, DatabaseTransaction_ts_1.writeKeyId)(primaryKey));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
has(primaryKey) {
|
|
398
|
+
if (typeof primaryKey === 'string')
|
|
399
|
+
return this.#byValue.has(primaryKey);
|
|
400
|
+
if (typeof primaryKey === 'number') {
|
|
401
|
+
if (primaryKey === 0)
|
|
402
|
+
return Object.is(primaryKey, -0) ? this.#negativeZero : this.#zero;
|
|
403
|
+
return this.#byValue.has(primaryKey) || (this.#holdsBigInt && this.#encoded.has((0, DatabaseTransaction_ts_1.writeKeyId)(primaryKey)));
|
|
404
|
+
}
|
|
405
|
+
if (typeof primaryKey === 'bigint') {
|
|
406
|
+
const number = Number(primaryKey);
|
|
407
|
+
if (BigInt(number) === primaryKey && (number === 0 ? this.#zero : this.#byValue.has(number)))
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
return this.#encoded.has((0, DatabaseTransaction_ts_1.writeKeyId)(primaryKey));
|
|
411
|
+
}
|
|
412
|
+
get size() {
|
|
413
|
+
return this.#byValue.size + this.#encoded.size + (this.#zero ? 1 : 0) + (this.#negativeZero ? 1 : 0);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
exports.CandidateKeySet = CandidateKeySet;
|
|
417
|
+
// Comparators whose secondary-index range is EXACTLY the condition over the values an index holds,
|
|
418
|
+
// so scanning that range yields every indexed match and nothing else. `ne`, `in`, `contains`,
|
|
419
|
+
// `ends_with` and any negated condition are excluded because searchByIndex answers them with a scan
|
|
420
|
+
// plus a record filter.
|
|
421
|
+
const CANDIDATE_KEY_COMPARATORS = new Set([
|
|
422
|
+
'equals',
|
|
423
|
+
'lt',
|
|
424
|
+
'le',
|
|
425
|
+
'gt',
|
|
426
|
+
'ge',
|
|
427
|
+
'between',
|
|
428
|
+
'gele',
|
|
429
|
+
'gelt',
|
|
430
|
+
'gtlt',
|
|
431
|
+
'gtle',
|
|
432
|
+
]);
|
|
433
|
+
/**
|
|
434
|
+
* Plan one leaf condition as an index range scan, or undefined when its matches cannot be read off
|
|
435
|
+
* an index exactly, or when the scan's size cannot be estimated.
|
|
436
|
+
*/
|
|
437
|
+
function planCandidateKeyScan(condition, table) {
|
|
438
|
+
if (condition.negated)
|
|
439
|
+
return undefined;
|
|
440
|
+
const attributeName = condition.attribute ?? condition[0];
|
|
441
|
+
if (typeof attributeName !== 'string' || attributeName === table.primaryKey)
|
|
442
|
+
return undefined;
|
|
443
|
+
const index = usableIndex(table, attributeName);
|
|
444
|
+
if (!index || index.customIndex)
|
|
445
|
+
return undefined;
|
|
446
|
+
const comparator = ALTERNATE_COMPARATOR_NAMES[condition.comparator] ?? condition.comparator ?? 'equals';
|
|
447
|
+
if (!CANDIDATE_KEY_COMPARATORS.has(comparator))
|
|
448
|
+
return undefined;
|
|
449
|
+
// The scan has to hold every key the record predicate admits, or results would depend on whether
|
|
450
|
+
// the plan was built. The index cannot supply a record lacking the attribute, a null one unless it
|
|
451
|
+
// indexes nulls, or one written as -0: ordered-binary encodes -0 past every number, and that entry's
|
|
452
|
+
// primary key decodes wrong. compareKeys ranks undefined below every scalar, so `rank < 100` admits
|
|
453
|
+
// a record with no rank at all; a condition admitting any of these is left to the predicate.
|
|
454
|
+
const predicate = filterByType(condition, table, undefined, null);
|
|
455
|
+
if (predicate({}) || predicate({ [attributeName]: 0 }) || (!index.indexNulls && predicate({ [attributeName]: null })))
|
|
456
|
+
return undefined;
|
|
457
|
+
let value = condition[1] ?? condition.value;
|
|
458
|
+
if (value instanceof Date)
|
|
459
|
+
value = value.getTime();
|
|
460
|
+
let start;
|
|
461
|
+
let end;
|
|
462
|
+
let inclusiveEnd = false;
|
|
463
|
+
let exclusiveStart = false;
|
|
464
|
+
switch (comparator) {
|
|
465
|
+
case 'lt':
|
|
466
|
+
// null, not searchByIndex's `true`: `true` sorts ABOVE null and false, so starting there
|
|
467
|
+
// would drop indexed rows the record predicate's compareKeys admits — an omission the
|
|
468
|
+
// post-filter cannot undo.
|
|
469
|
+
start = null;
|
|
470
|
+
end = value;
|
|
471
|
+
break;
|
|
472
|
+
case 'le':
|
|
473
|
+
start = null;
|
|
474
|
+
end = value;
|
|
475
|
+
inclusiveEnd = true;
|
|
476
|
+
break;
|
|
477
|
+
case 'gt':
|
|
478
|
+
start = value;
|
|
479
|
+
exclusiveStart = true;
|
|
480
|
+
break;
|
|
481
|
+
case 'ge':
|
|
482
|
+
start = value;
|
|
483
|
+
break;
|
|
484
|
+
case 'between':
|
|
485
|
+
case 'gele':
|
|
486
|
+
case 'gelt':
|
|
487
|
+
case 'gtlt':
|
|
488
|
+
case 'gtle':
|
|
489
|
+
if (!Array.isArray(value))
|
|
490
|
+
return undefined;
|
|
491
|
+
start = value[0] instanceof Date ? value[0].getTime() : value[0];
|
|
492
|
+
end = value[1] instanceof Date ? value[1].getTime() : value[1];
|
|
493
|
+
inclusiveEnd = comparator === 'gele' || comparator === 'gtle' || comparator === 'between';
|
|
494
|
+
exclusiveStart = comparator === 'gtlt' || comparator === 'gtle';
|
|
495
|
+
break;
|
|
496
|
+
default:
|
|
497
|
+
start = value;
|
|
498
|
+
end = value;
|
|
499
|
+
inclusiveEnd = true;
|
|
500
|
+
}
|
|
501
|
+
// ordered-binary encodes -0 above every number, where the predicate's compareKeys calls it 0.
|
|
502
|
+
if (start === 0)
|
|
503
|
+
start = 0;
|
|
504
|
+
if (end === 0)
|
|
505
|
+
end = 0;
|
|
506
|
+
// An over-length string key is stored truncated behind an overflow marker, which makes the range
|
|
507
|
+
// wider than the condition and needs a record filter to narrow again.
|
|
508
|
+
if ((typeof start === 'string' && start.length > terms_ts_1.MAX_SEARCH_KEY_LENGTH) ||
|
|
509
|
+
(typeof end === 'string' && end.length > terms_ts_1.MAX_SEARCH_KEY_LENGTH))
|
|
510
|
+
return undefined;
|
|
511
|
+
if (condition.estimated_count === undefined)
|
|
512
|
+
estimateCondition(table)(condition);
|
|
513
|
+
let estimatedCount = condition.estimated_count;
|
|
514
|
+
if (!(estimatedCount >= 0) || !Number.isFinite(estimatedCount))
|
|
515
|
+
return undefined;
|
|
516
|
+
// estimateRangeCondition measures the range searchByIndex would run, which starts at `true`. Only
|
|
517
|
+
// lt/le widen below that, and the block they add is read FIRST and can dwarf the rest of the
|
|
518
|
+
// range, so it has to be counted or the scan overruns a budget sized without it. A statistical
|
|
519
|
+
// estimate, never getValuesCount: that one steps the whole block, which is the unbounded
|
|
520
|
+
// per-query cost this is here to avoid. The other comparators' own estimates already cover
|
|
521
|
+
// whatever they start at.
|
|
522
|
+
if (comparator === 'lt' || comparator === 'le') {
|
|
523
|
+
let belowTrue;
|
|
524
|
+
try {
|
|
525
|
+
belowTrue = index.estimateCount?.({ start: null, end: true })?.count;
|
|
526
|
+
}
|
|
527
|
+
catch {
|
|
528
|
+
return undefined;
|
|
529
|
+
}
|
|
530
|
+
if (!(belowTrue >= 0))
|
|
531
|
+
return undefined;
|
|
532
|
+
estimatedCount += belowTrue;
|
|
533
|
+
}
|
|
534
|
+
return { index, range: { start, end, inclusiveEnd, exclusiveStart }, estimatedCount };
|
|
535
|
+
}
|
|
536
|
+
function planCandidateKeys(conditions, table, transaction, guardFree) {
|
|
537
|
+
const terms = [];
|
|
538
|
+
const planned = collectCandidateKeyTerms(conditions, table, terms) && guardFree;
|
|
539
|
+
if (terms.length === 0)
|
|
540
|
+
return undefined;
|
|
541
|
+
const estimatedCount = terms.reduce((narrowest, term) => Math.min(narrowest, term.estimatedCount), Infinity);
|
|
542
|
+
return {
|
|
543
|
+
estimatedCount,
|
|
544
|
+
collect: (maxKeys) => collectCandidateKeys(terms, transaction, maxKeys, planned),
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
/** Returns whether every condition was covered. */
|
|
548
|
+
function collectCandidateKeyTerms(conditions, table, terms) {
|
|
549
|
+
let complete = true;
|
|
550
|
+
for (const condition of conditions) {
|
|
551
|
+
if (condition.conditions) {
|
|
552
|
+
if (condition.operator === 'or') {
|
|
553
|
+
// A union is only sound in full: dropping one branch would exclude the records only
|
|
554
|
+
// that branch matches, and admission may over-admit but never omit.
|
|
555
|
+
const scans = [];
|
|
556
|
+
for (const child of condition.conditions) {
|
|
557
|
+
const scan = child.conditions ? undefined : planCandidateKeyScan(child, table);
|
|
558
|
+
if (!scan) {
|
|
559
|
+
complete = false;
|
|
560
|
+
break;
|
|
561
|
+
}
|
|
562
|
+
scans.push(scan);
|
|
563
|
+
}
|
|
564
|
+
if (scans.length === condition.conditions.length) {
|
|
565
|
+
terms.push({ scans, estimatedCount: scans.reduce((total, scan) => total + scan.estimatedCount, 0) });
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
else if (!collectCandidateKeyTerms(condition.conditions, table, terms))
|
|
569
|
+
complete = false;
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
const scan = planCandidateKeyScan(condition, table);
|
|
573
|
+
if (scan)
|
|
574
|
+
terms.push({ scans: [scan], estimatedCount: scan.estimatedCount });
|
|
575
|
+
else
|
|
576
|
+
complete = false;
|
|
577
|
+
}
|
|
578
|
+
return complete;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Run the planned scans narrowest-estimate first, intersecting on storage key identity. `maxKeys`
|
|
582
|
+
* bounds index entries READ, not keys retained — a wide later scan whose intersection is tiny still
|
|
583
|
+
* reads its whole range, and that is the cost the caller budgeted against. A term whose estimate
|
|
584
|
+
* exceeds what is left of that budget is dropped instead: fewer AND terms is a wider superset, which
|
|
585
|
+
* the predicate then narrows, and spending the budget on it only to abandon the set is pure loss.
|
|
586
|
+
*/
|
|
587
|
+
function collectCandidateKeys(terms, transaction, maxKeys, planned) {
|
|
588
|
+
const ordered = terms.length > 1 ? terms.slice().sort((a, b) => a.estimatedCount - b.estimatedCount) : terms;
|
|
589
|
+
let matched;
|
|
590
|
+
let complete = planned;
|
|
591
|
+
let scanned = 0;
|
|
592
|
+
for (const term of ordered) {
|
|
593
|
+
if (matched && term.estimatedCount > maxKeys - scanned) {
|
|
594
|
+
complete = false;
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
const next = new CandidateKeySet();
|
|
598
|
+
let overran = false;
|
|
599
|
+
for (const scan of term.scans) {
|
|
600
|
+
for (const { value: primaryKey } of scan.index.getRange({ ...scan.range, values: true, transaction })) {
|
|
601
|
+
if (++scanned > maxKeys) {
|
|
602
|
+
overran = true;
|
|
603
|
+
break;
|
|
604
|
+
}
|
|
605
|
+
if (!matched || matched.has(primaryKey))
|
|
606
|
+
next.add(primaryKey);
|
|
607
|
+
}
|
|
608
|
+
if (overran)
|
|
609
|
+
break;
|
|
610
|
+
}
|
|
611
|
+
// A half-scanned term is not a superset of its own matches, so it is dropped whole.
|
|
612
|
+
if (overran) {
|
|
613
|
+
if (!matched)
|
|
614
|
+
return null;
|
|
615
|
+
complete = false;
|
|
616
|
+
break;
|
|
617
|
+
}
|
|
618
|
+
matched = next;
|
|
619
|
+
if (matched.size === 0)
|
|
620
|
+
break;
|
|
621
|
+
}
|
|
622
|
+
return { keys: matched, complete };
|
|
623
|
+
}
|
|
222
624
|
/**
|
|
223
625
|
* A record's index entries are not adjacent — the composite `[indexedValue, primaryKey]` key sorts
|
|
224
626
|
* on the indexed value first — so comparing neighbours cannot collapse them and the scan has to
|
|
@@ -269,10 +671,15 @@ function searchByIndex(searchCondition, transaction, reverse, Table, options = {
|
|
|
269
671
|
// fail loud rather than silently reading every option as undefined.
|
|
270
672
|
if (typeof options !== 'object' || options === null)
|
|
271
673
|
throw new TypeError('searchByIndex: the 5th argument is an options object (#2165), not a positional value');
|
|
272
|
-
const { allowFullScan, filtered, context, minResults } = options;
|
|
674
|
+
const { allowFullScan, filtered, context, minResults, resultOffset } = options;
|
|
273
675
|
let attribute_name = searchCondition[0] ?? searchCondition.attribute;
|
|
274
676
|
let value = searchCondition[1] ?? searchCondition.value;
|
|
275
677
|
const comparator = searchCondition.comparator;
|
|
678
|
+
const fullTextMode = (0, fullTextQueryProtocol_ts_1.fullTextComparatorMode)(comparator);
|
|
679
|
+
if (fullTextMode && typeof attribute_name !== 'string')
|
|
680
|
+
throw new hdbError_ts_1.ClientError('Full-text comparator requires an index directly on the queried table', 400);
|
|
681
|
+
if (!fullTextMode)
|
|
682
|
+
Table.assertFullTextRecordField?.(attribute_name);
|
|
276
683
|
if (value === undefined && comparator !== 'sort') {
|
|
277
684
|
throw new hdbError_ts_1.ClientError(`Search condition for ${attribute_name} must have a value`);
|
|
278
685
|
}
|
|
@@ -332,8 +739,14 @@ function searchByIndex(searchCondition, transaction, reverse, Table, options = {
|
|
|
332
739
|
needFullScan = true;
|
|
333
740
|
}
|
|
334
741
|
}
|
|
335
|
-
|
|
336
|
-
|
|
742
|
+
if (fullTextMode) {
|
|
743
|
+
if (typeof attribute_name !== 'string' || !Table.fullTextIndexes?.some(({ name }) => name === attribute_name))
|
|
744
|
+
throw new hdbError_ts_1.ClientError('Full-text comparator requires a declared @fullText index', 400);
|
|
745
|
+
if (!Table.fullTextQueryIndexes?.[attribute_name]?.customIndex)
|
|
746
|
+
throw new hdbError_ts_1.IndexRebuildingError(`Full-text index '${attribute_name}' is not ready`);
|
|
747
|
+
}
|
|
748
|
+
const isPrimaryKey = !fullTextMode && (attribute_name === Table.primaryKey || attribute_name == null);
|
|
749
|
+
const index = isPrimaryKey ? Table.primaryStore : conditionIndex(Table, attribute_name, Boolean(fullTextMode));
|
|
337
750
|
let start;
|
|
338
751
|
let end, inclusiveEnd, exclusiveStart, stringPrefix;
|
|
339
752
|
if (value instanceof Date)
|
|
@@ -345,6 +758,9 @@ function searchByIndex(searchCondition, transaction, reverse, Table, options = {
|
|
|
345
758
|
start = true;
|
|
346
759
|
needFullScan = true;
|
|
347
760
|
}
|
|
761
|
+
else if (fullTextMode) {
|
|
762
|
+
start = true;
|
|
763
|
+
}
|
|
348
764
|
else
|
|
349
765
|
switch (ALTERNATE_COMPARATOR_NAMES[comparator] || comparator) {
|
|
350
766
|
case 'lt':
|
|
@@ -521,6 +937,7 @@ function searchByIndex(searchCondition, transaction, reverse, Table, options = {
|
|
|
521
937
|
// exploring until it has enough MATCHING results, rather than post-filtering an under-filled
|
|
522
938
|
// candidate set. Only indexes that opt in (filteredSearch) receive it; others post-filter as before.
|
|
523
939
|
const recordFilter = index.customIndex.filteredSearch ? searchCondition.recordFilter : undefined;
|
|
940
|
+
const candidateKeys = index.customIndex.candidateKeyFilter ? searchCondition.candidateKeys : undefined;
|
|
524
941
|
const waiting = index.customIndex.filePrimary && searchCondition.waitForIndexMilliseconds > 0;
|
|
525
942
|
const start = waiting ? Promise.withResolvers() : undefined;
|
|
526
943
|
const controller = waiting ? new AbortController() : undefined;
|
|
@@ -530,22 +947,27 @@ function searchByIndex(searchCondition, transaction, reverse, Table, options = {
|
|
|
530
947
|
? AbortSignal.any([context.signal, controller.signal])
|
|
531
948
|
: controller.signal
|
|
532
949
|
: undefined;
|
|
950
|
+
const indexSearchStart = waiting ? start.promise.then(() => checkActive?.()) : undefined;
|
|
951
|
+
indexSearchStart?.catch(() => { });
|
|
533
952
|
const searchContext = waiting
|
|
534
953
|
? Object.create(context, {
|
|
535
954
|
signal: { value: signal },
|
|
536
|
-
indexSearchStart: { value:
|
|
955
|
+
indexSearchStart: { value: indexSearchStart },
|
|
537
956
|
})
|
|
538
957
|
: context;
|
|
539
958
|
const searched = index.customIndex.search(searchCondition, searchContext, {
|
|
540
959
|
filter: waiting && recordFilter
|
|
541
|
-
? (id) => {
|
|
960
|
+
? (id, entry) => {
|
|
542
961
|
if (signal.aborted)
|
|
543
962
|
return false;
|
|
544
963
|
checkActive?.();
|
|
545
|
-
return recordFilter(id);
|
|
964
|
+
return recordFilter(id, entry);
|
|
546
965
|
}
|
|
547
966
|
: recordFilter,
|
|
548
967
|
minResults,
|
|
968
|
+
resultOffset,
|
|
969
|
+
assertTransactionActive: checkActive,
|
|
970
|
+
candidateKeys,
|
|
549
971
|
});
|
|
550
972
|
const coverage = searched.indexCoverage;
|
|
551
973
|
if (!waiting && coverage && context?.responseHeaders) {
|
|
@@ -557,12 +979,13 @@ function searchByIndex(searchCondition, transaction, reverse, Table, options = {
|
|
|
557
979
|
.map((entry) => {
|
|
558
980
|
// if the custom index returns an entry with metadata, merge it with the loaded entry
|
|
559
981
|
if (typeof entry === 'object' && entry) {
|
|
560
|
-
const { key, ...otherProps } = entry;
|
|
982
|
+
const { key, loadedEntry: suppliedEntry, ...otherProps } = entry;
|
|
561
983
|
if (key == null)
|
|
562
984
|
return extended_iterable_1.SKIP; // primaryKey missing from HNSW node — skip rather than crash
|
|
563
|
-
const loadedEntry =
|
|
564
|
-
|
|
565
|
-
|
|
985
|
+
const loadedEntry = suppliedEntry ??
|
|
986
|
+
Table.primaryStore.getEntry(key, {
|
|
987
|
+
transaction: waiting ? transaction : context && Table._readTxnForContext(context),
|
|
988
|
+
});
|
|
566
989
|
if (!loadedEntry)
|
|
567
990
|
return extended_iterable_1.SKIP; // record was deleted/expired or not yet visible
|
|
568
991
|
(0, Table_ts_1.freezeRecord)(loadedEntry?.value);
|
|
@@ -620,8 +1043,10 @@ function searchByIndex(searchCondition, transaction, reverse, Table, options = {
|
|
|
620
1043
|
},
|
|
621
1044
|
return(value) {
|
|
622
1045
|
closed = true;
|
|
623
|
-
if (!settled)
|
|
1046
|
+
if (!settled) {
|
|
1047
|
+
start?.resolve();
|
|
624
1048
|
controller?.abort();
|
|
1049
|
+
}
|
|
625
1050
|
iteratorPromise.then((inner) => inner.return?.(value), () => { });
|
|
626
1051
|
return Promise.resolve({ done: true, value });
|
|
627
1052
|
},
|
|
@@ -894,7 +1319,15 @@ const ALTERNATE_COMPARATOR_NAMES = {
|
|
|
894
1319
|
const LIST_VALUE_COMPARATORS = new Set(['in', 'between']);
|
|
895
1320
|
// Base comparators that accept the `not_` prefix to produce a negated form.
|
|
896
1321
|
// `not_equal` is an existing alias for `ne` and keeps its existing semantics.
|
|
897
|
-
const NEGATABLE_BASE_COMPARATORS = new Set([
|
|
1322
|
+
const NEGATABLE_BASE_COMPARATORS = new Set([
|
|
1323
|
+
'in',
|
|
1324
|
+
'between',
|
|
1325
|
+
'starts_with',
|
|
1326
|
+
'ends_with',
|
|
1327
|
+
'contains',
|
|
1328
|
+
'equals',
|
|
1329
|
+
...fullTextQueryProtocol_ts_1.FULL_TEXT_POSITIVE_COMPARATORS,
|
|
1330
|
+
]);
|
|
898
1331
|
/**
|
|
899
1332
|
* Resolve a comparator name to a (possibly stripped) base comparator and a
|
|
900
1333
|
* `negated` flag. Existing aliases are preserved as-is — the execution layer
|
|
@@ -1311,7 +1744,7 @@ function estimateRangeCondition(table, condition, searchType, fraction) {
|
|
|
1311
1744
|
}
|
|
1312
1745
|
/** The index a condition can be driven by; searchByIndex refuses a rebuilding one, so it reads as absent. */
|
|
1313
1746
|
function usableIndex(table, attributeName) {
|
|
1314
|
-
const index = attributeName == null ? undefined : table
|
|
1747
|
+
const index = attributeName == null ? undefined : conditionIndex(table, attributeName);
|
|
1315
1748
|
return index?.isIndexing ? undefined : index;
|
|
1316
1749
|
}
|
|
1317
1750
|
/**
|
|
@@ -1320,7 +1753,7 @@ function usableIndex(table, attributeName) {
|
|
|
1320
1753
|
*/
|
|
1321
1754
|
function drivesRebuildingIndex(table, attributeName, relationshipOffset = 0) {
|
|
1322
1755
|
if (!Array.isArray(attributeName))
|
|
1323
|
-
return attributeName != null && attributeName !== table.primaryKey && !!table
|
|
1756
|
+
return (attributeName != null && attributeName !== table.primaryKey && !!conditionIndex(table, attributeName)?.isIndexing);
|
|
1324
1757
|
if (relationshipOffset >= attributeName.length - 1)
|
|
1325
1758
|
return drivesRebuildingIndex(table, attributeName[relationshipOffset]);
|
|
1326
1759
|
const attribute = findAttribute(table.attributes, attributeName[relationshipOffset]);
|
|
@@ -1406,6 +1839,13 @@ function estimateCondition(table) {
|
|
|
1406
1839
|
condition.estimated_count = index ? index.getValuesCount(condition[1] ?? condition.value) : Infinity;
|
|
1407
1840
|
}
|
|
1408
1841
|
}
|
|
1842
|
+
else if ((0, fullTextQueryProtocol_ts_1.fullTextComparatorMode)(searchType)) {
|
|
1843
|
+
const attributeName = condition[0] ?? condition.attribute;
|
|
1844
|
+
const index = conditionIndex(table, attributeName, true);
|
|
1845
|
+
condition.estimated_count = index?.customIndex?.estimateCount
|
|
1846
|
+
? index.customIndex.estimateCount(condition.value)
|
|
1847
|
+
: Infinity;
|
|
1848
|
+
}
|
|
1409
1849
|
else if (searchType === 'contains' || searchType === 'ends_with' || searchType === 'ne') {
|
|
1410
1850
|
const attribute_name = condition[0] ?? condition.attribute;
|
|
1411
1851
|
const index = usableIndex(table, attribute_name);
|
|
@@ -1554,7 +1994,9 @@ function parseBlock(query, expectedEnd) {
|
|
|
1554
1994
|
comparator = value;
|
|
1555
1995
|
else
|
|
1556
1996
|
recordError(`invalid FIQL operator ${value}`);
|
|
1557
|
-
valueDecoder =
|
|
1997
|
+
valueDecoder = (0, fullTextQueryProtocol_ts_1.fullTextComparatorMode)(resolveComparator(comparator).comparator)
|
|
1998
|
+
? decodeURIComponent
|
|
1999
|
+
: typedDecoding;
|
|
1558
2000
|
}
|
|
1559
2001
|
else {
|
|
1560
2002
|
// standard equal comparison
|