import type { Fn2 , Predicate } from "@thi.ng/api" ; import { isArray } from "@thi.ng/checks/is-array" ; import { isFunction } from "@thi.ng/checks/is-function" ; import { isSet } from "@thi.ng/checks/is-set" ; import { defmulti } from "@thi.ng/defmulti/defmulti" ; import { equiv } from "@thi.ng/equiv" ; import type { FTerm , KeyQueryFn , KeyQueryOpts , OTerm , QueryFn , QueryImpl , QueryImpls , QueryObj , QueryOpts , QueryType , SPInputTerm , SPTerm , } from "./api.js" ; /**
 * Classifies a single S,P,O term. See  QueryType for explanations.
 *
 *  param x internal const classify = ( x ) => ( x != null ? ( isFunction ( x ) ? "f" : "l" ) : "n" ) ; internal const ensureArray = ( src ) => ( isArray ( src ) ? src : [ ... src ] ) ; internal const ensureSet = ( src ) => isArray ( src ) ? new Set ( src ) : src ; param src internal const intersect = ( src ) => { const a = ensureArray ( src ) ; const num = a . length ; return ( b ) => { const $b = ensureSet ( b ) ; for ( let i = num ; i -- > 0 ; ) { if ( ! $b . has ( a [ i ] ) ) return false ; } return true ; } ; } ; /**
 * Checks the type of a single S,P,O term and if an array or Set, returns a
 * predicate function to check if an element is included. Otherwise, returns
 * input. If `isec` is true, the returned predicate will be the result of
 *  intersect .
 *
 *  param x param isec internal const coerce = ( x , isec = false ) => isArray ( x ) ? isec ? intersect ( x ) : ( y ) => x . includes ( y ) : isSet ( x ) ? isec ? intersect ( x ) : ( y ) => x . has ( y ) : x ; /**
 * Similar to  coerce , but intended for S,P terms. Unless `x` is a
 * function or null, coerces `x` (or its elements) to strings first.
 *
 *  param x internal const coerceStr = ( x ) => isArray ( x ) ? coerce ( x . map ( ( y ) => String ( y ) ) ) : isSet ( x ) ? coerce ( new Set ( [ ... x ] . map ( ( y ) => String ( y ) ) ) ) : x == null || isFunction ( x ) ? x : String ( x ) ; const addTriple = ( acc , s , p , o ) => { const sval = acc [ s ] ; sval ? ( sval [ p ] = o ) : ( acc [ s ] = { [ p ] : o } ) ; } ; const match = ( o , val , opts ) => { if ( val != null ) { const pred = < Predicate < any > > ( ( isFunction ( o ) ? o : ( $ ) => opts . equiv ( o , $ ) ) ) ; return opts . cwise && isArray ( val ) ? val . some ( pred ) : pred ( val ) ; } return false ; } ; const collect = ( acc , s , p , o , val , opts ) => { if ( val != null ) { const pred = isFunction ( o ) ? o : ( $ ) => opts . equiv ( o , $ ) ; if ( opts . cwise && isArray ( val ) ) { val = val . filter ( pred ) ; val . length && addTriple ( acc , s , p , val ) ; } else if ( pred ( val ) ) { addTriple ( acc , s , p , val ) ; } } } ; const collectFull = ( res , s , val ) => ( res [ s ] = val ) ; const collectSP = ( res , sval , s , p , o , opts ) => { if ( opts . partial ) { for ( let $p in sval ) { ( < FTerm > p ) ( $p ) && collect ( res , s , $p , o , sval [ $p ] , opts ) ; } } else { for ( let $p in sval ) { if ( ( < FTerm > p ) ( $p ) && match ( o , sval [ $p ] , opts ) ) { collectFull ( res , s , sval ) ; return ; } } } } ; const collectSO = ( res , sval , s , o , opts ) => { if ( opts . partial ) { for ( let p in sval ) { collect ( res , s , p , o , sval [ p ] , opts ) ; } } else { for ( let p in sval ) { if ( match ( o , sval [ p ] , opts ) ) { collectFull ( res , s , sval ) ; return ; } } } } ; const queryLL = ( res , db , s , p , o , opts ) => { const sval = db [ < any > s ] ; const val = sval ?. [ < string > p ] ; if ( opts . partial ) { collect ( res , s , p , o , val , opts ) ; } else { match ( o , val , opts ) && collectFull ( res , s , sval ) ; } } ; const queryLF = ( res , db , s , p , o , opts ) => { const sval = db [ < string > s ] ; sval != null && collectSP ( res , sval , s , p , o , opts ) ; } ; const queryLN = ( res , db , s , _ , o , opts ) => { const sval = db [ < string > s ] ; sval != null && collectSO ( res , sval , s , o , opts ) ; } ; const queryFL = ( res , db , s , p , o , opts ) => { if ( opts . partial ) { for ( let $s in db ) { ( < FTerm > s ) ( $s ) && collect ( res , $s , p , o , db [ $s ] ?. [ < string > p ] , opts ) ; } } else { for ( let $s in db ) { const sval = db [ $s ] ; ( < FTerm > s ) ( $s ) && match ( o , sval ?. [ < string > p ] , opts ) && collectFull ( res , $s , sval ) ; } } } ; const queryFF = ( res , db , s , p , o , opts ) => { for ( let $s in db ) { ( < FTerm > s ) ( $s ) && collectSP ( res , db [ $s ] , $s , p , o , opts ) ; } } ; const queryFN = ( res , db , s , _ , o , opts ) => { for ( let $s in db ) { ( < FTerm > s ) ( $s ) && collectSO ( res , db [ $s ] , $s , o , opts ) ; } } ; const queryNL = ( res , db , _ , p , o , opts ) => { if ( opts . partial ) { for ( let s in db ) { collect ( res , s , p , o , db [ s ] [ < string > p ] , opts ) ; } } else { for ( let s in db ) { const sval = db [ s ] ; match ( o , sval [ < string > p ] , opts ) && collectFull ( res , s , sval ) ; } } } ; const queryNF = ( res , db , _ , p , o , opts ) => { for ( let s in db ) { collectSP ( res , db [ s ] , s , p , o , opts ) ; } } ; const queryNN = ( res , db , _ , __ , o , opts ) => { for ( let s in db ) { collectSO ( res , db [ s ] , s , o , opts ) ; } } ; const querySP = ( res , sval , s , p , _ , opts ) => { if ( opts . partial ) { for ( let q in sval ) { if ( ( < FTerm > p ) ( q ) ) { const val = sval [ q ] ; val != null && addTriple ( res , s , q , val ) ; } } } else { for ( let q in sval ) { if ( ( < FTerm > p ) ( q ) ) { collectFull ( res , s , sval ) ; return ; } } } } ; const queryO = ( res , db , s , p , _ , opts ) => { const sval = db [ < string > s ] ; const val = sval ?. [ < string > p ] ; val != null && ( opts . partial ? addTriple ( res , s , p , val ) : collectFull ( res , s , sval ) ) ; } ; const IMPLS = < QueryImpls > { lll : queryLL , llf : queryLL , lln : queryO , lfl : queryLF , lff : queryLF , lfn : ( res , db , s , p , _ , opts ) => { const sval = db [ < string > s ] ; sval != null && querySP ( res , sval , s , p , null , opts ) ; } , lnl : queryLN , lnf : queryLN , lnn : ( res , db , s ) => { const sval = db [ < string > s ] ; sval != null && collectFull ( res , s , sval ) ; } , fll : queryFL , flf : queryFL , fln : ( res , db , s , p , _ , opts ) => { for ( let $s in db ) { ( < FTerm > s ) ( $s ) && queryO ( res , db , $s , p , null , opts ) ; } } , ffl : queryFF , fff : queryFF , ffn : ( res , db , s , p , _ , opts ) => { if ( opts . partial ) { for ( let $s in db ) { if ( ( < FTerm > s ) ( $s ) ) { const sval = db [ $s ] ; for ( let $p in sval ) { ( < FTerm > p ) ( $p ) && addTriple ( res , $s , $p , sval [ $p ] ) ; } } } } else { for ( let $s in db ) { if ( ( < FTerm > s ) ( $s ) ) { const sval = db [ $s ] ; for ( let $p in sval ) { if ( ( < FTerm > p ) ( $p ) ) { collectFull ( res , $s , sval ) ; break ; } } } } } } , fnl : queryFN , fnf : queryFN , fnn : ( res , db , s ) => { for ( let $s in db ) { if ( ( < FTerm > s ) ( $s ) ) { const sval = db [ $s ] ; sval != null && collectFull ( res , $s , sval ) ; } } } , nll : queryNL , nlf : queryNL , nln : ( res , db , _ , p , __ , opts ) => { if ( opts . partial ) { for ( let s in db ) { const val = db [ s ] [ < string > p ] ; val != null && addTriple ( res , s , p , val ) ; } } else { for ( let s in db ) { const sval = db [ s ] ; const val = sval [ < string > p ] ; val != null && collectFull ( res , s , sval ) ; } } } , nfl : queryNF , nff : queryNF , nfn : ( res , db , _ , p , __ , opts ) => { for ( let s in db ) { querySP ( res , db [ s ] , s , p , null , opts ) ; } } , nnl : queryNN , nnf : queryNN , nnn : ( res , db ) => Object . assign ( res , db ) , } ; internal const impl = defmulti < QueryObj , QueryObj , SPTerm , SPTerm , OTerm , QueryOpts , void > ( ( _ , __ , s , p , o ) => classify ( s ) + classify ( p ) + classify ( o ) , { } , IMPLS ) ; const objQuery = ( src , opts , args ) => { const isIsec = opts . cwise && opts . intersect ; isIsec && ( opts . cwise = false ) ; let [ s , p , o , out ] = < [ SPInputTerm , SPInputTerm , OTerm , QueryObj ? ] > args ; out = out || { } ; impl ( out , src , coerceStr ( s ) , coerceStr ( p ) , coerce ( o , isIsec ) , < QueryOpts > opts ) ; return out ; } ; const arrayQuery = ( src , opts , p , o , collect ) => { const isIsec = opts . cwise && opts . intersect ; isIsec && ( opts . cwise = false ) ; const $p = coerceStr ( p ) ; const $o = coerce ( o , isIsec ) ; const impl = IMPLS [ < QueryType > ( "n" + classify ( $p ) + classify ( $o ) ) ] ; for ( let i = 0 , n = src . length ; i < n ; i ++ ) { const res = { } ; impl ( res , { _ : src [ i ] } , "_" , $p , $o , opts ) ; res . _ && collect ( res . _ , i ) ; } } ; const DEFAULT_OPTS = { partial : false , cwise : true , intersect : false , equiv , } ; remarks see QueryOpts see ObjQueryFn see ArrayQueryFn see defKeyQuery param opts export const defQuery = < T extends QueryObj | QueryObj [ ] = QueryObj > ( opts ? ) : QueryFn < T > => { const $opts = { ... DEFAULT_OPTS , ... opts } ; return < QueryFn < T > > ( ( src , ... args ) : any => { if ( isArray ( src ) ) { const out = args [ 2 ] || [ ] ; arrayQuery ( src , $opts , args [ 0 ] , args [ 1 ] , ( x ) => out . push ( x ) ) ; return out ; } else { return objQuery ( src , $opts , args ) ; } } ) ; } ; /**
 * Generic Higher-order function to return an actual query function based on
 * given behavior options. Unlike  defQuery , key query functions only
 * return sets of keys (or indices) of matching objects.
 *
 *  remarks see KeyQueryOpts see ObjKeyQueryFn see ArrayKeyQueryFn param opts export const defKeyQuery = < T extends QueryObj | QueryObj [ ] = QueryObj > ( opts ? ) => { const $opts = { ... DEFAULT_OPTS , ... opts } ; return < KeyQueryFn < T > > ( ( src , ... args ) : any => { if ( isArray ( src ) ) { const out = args [ 2 ] || new Set < number > ( ) ; arrayQuery ( src , $opts , args [ 0 ] , args [ 1 ] , ( _ , i ) => out . add ( i ) ) ; return out ; } else { const res = objQuery ( src , $opts , args . slice ( 0 , 3 ) ) ; const out = args [ 3 ] ; if ( ! out ) return new Set < string > ( Object . keys ( res ) ) ; for ( let k in res ) out . add ( k ) ; return out ; } } ) ; } ;