import { query as q } from 'faunadb' ; import { FaunaIndexOptions , FaunaIndexTerm , FaunaIndexValue } from '~/types/fauna' ; import { FrameworkCollectionFieldOptions } from '~/types/framework/framework.collection' ; import { Biota } from '~/biota' ; import { BiotaIndexName , Index , NGramOnField } from '~/factory/constructors/index' ; import { index as indexFactory } from '~/factory/api/index' ; import * as helpers from '~/helpers' ; import { execute } from '~/tools/tasks' ; export function fieldDefinition ( fieldInput ) { const definition = { inputs : [ ] , outputs : [ ] , field : undefined , data : { } , reverse : false , binding : undefined , name : undefined , action : undefined , ngram : false , ngramMin : 3 , ngramMax : 10 , unique : false , serialized : undefined , permissions : undefined , } ; if ( typeof fieldInput === 'string' ) { definition . name = fieldInput ; definition . field = fieldInput ; } else if ( typeof fieldInput === 'object' ) { Object . assign ( definition , fieldInput ) ; } return definition ; } export function field ( this , collectionName ) { const self = this ; return async function fieldMethod ( fieldInput ) { const definition = fieldDefinition ( fieldInput ) ; const index = Index ( { source : { collection : q . Collection ( collectionName ) , fields : { } , } , unique : definition . unique , serialized : definition . serialized , data : definition . data , } ) ; const tasks = [ ] ; const buildTerm = ( { field : fieldInput , binding , reverse } , fields = { } ) => { if ( binding ) { const bindingName = helpers . stringPath ( fieldInput || 'cursor' ) ; fields [ bindingName ] = binding ; return { binding : bindingName , reverse } ; } else if ( fieldInput ) { return { field : helpers . path ( fieldInput ) , reverse , } ; } } ; if ( definition . action === 'index' ) { if ( definition . field ) { index . terms = [ buildTerm ( { field : definition . field , binding : definition . binding , reverse : definition . reverse } , index . source . fields ) , ] ; } else if ( definition . inputs . length > 0 ) { index . terms = ( definition . inputs as FaunaIndexTerm [ ] ) . map ( ( input ) => { if ( typeof input === 'string' ) { return buildTerm ( { field : input } , index . source . fields ) ; } else if ( input . binding ) { return buildTerm ( { field : input . field , binding : input . binding , reverse : input . reverse } , index . source . fields ) ; } else if ( input . field ) { return buildTerm ( { field : input . field } , index . source . fields ) ; } } ) ; } else { index . terms = [ { field : [ 'ref' ] , } , ] ; } if ( definition . outputs . length > 0 ) { index . values = ( definition . outputs as FaunaIndexValue [ ] ) . map ( ( input ) => { if ( typeof input === 'string' ) { return buildTerm ( { field : input } , index . source . fields ) ; } else if ( input . binding ) { const bindingName = helpers . stringPath ( input . field ) || 'cursor' ; index . source . fields [ helpers . stringPath ( bindingName ) ] = input . binding ; return buildTerm ( { field : input . field , binding : input . binding , reverse : input . reverse } , index . source . fields ) ; } else if ( input . field ) { return buildTerm ( { field : input . field } , index . source . fields ) ; } } ) ; } else { index . values = [ { field : [ 'ref' ] , } , ] ; } index . name = BiotaIndexName ( helpers . name ( [ collectionName , 'search' , 'on' , helpers . stringPath ( definition . name || definition . field ) ] ) ) ; if ( definition . ngram ) { if ( index . terms . length > 0 && index . terms . length > 1 ) { throw new Error ( `Can't produce an ngram index with ${ index . terms . length } terms` ) ; } let fieldName = '' ; try { fieldName = helpers . stringPath ( definition . field || index . terms [ 0 ] . field ) ; } catch ( error ) { throw new Error ( `Couldn't find a correct field name` ) ; } const ngramFieldName = 'ngram:' + fieldName ; const ngramIndex = { name : BiotaIndexName ( helpers . name ( [ collectionName , 'ngram' , 'on' , helpers . stringPath ( fieldName ) ] ) ) , source : { collection : q . Collection ( collectionName ) , fields : { [ ngramFieldName ] : q . Query ( q . Lambda ( 'instance' , q . Distinct ( NGramOnField ( definition . ngramMax , helpers . path ( definition . field ) ) ) ) ) , } , } , terms : [ { binding : ngramFieldName , } , ] , serialized : true , data : definition . data , } ; tasks . push ( { name : `Creating (ngram search) index: ${ ngramIndex . name }` , async task ( ) { return self . query ( indexFactory ( self . context ) ( ngramIndex . name ) . upsert ( ngramIndex ) ) ; } , fullError : true , } ) ; } else { tasks . push ( { name : `Creating (search) index: ${ index . name }` , async task ( ) { return self . query ( indexFactory ( self . context ) ( index . name ) . upsert ( index ) ) ; } , } ) ; } } else if ( definition . action === 'compute' ) { const fieldOrName = definition . field || definition . name ; index . source . fields [ fieldOrName ] = definition . binding ; const indexByBinding = { ... index , name : BiotaIndexName ( helpers . name ( [ collectionName , 'compute' , 'on' , helpers . stringPath ( fieldOrName ) ] ) ) , values : [ { binding : fieldOrName , } , ] , terms : [ { field : [ 'ref' ] , } , ] , } ; tasks . push ( { name : `Creating (search compute) index: ${ indexByBinding . name }` , async task ( ) { return self . query ( indexFactory ( self . context ) ( indexByBinding . name ) . upsert ( indexByBinding ) ) ; } , } ) ; } return execute ( tasks , { domain : 'Biota.collection.field' , singleResult : false , } ) ; } ; }