import { Collection , ComponentRequestTable , ConceptDb , ConceptServer , ConceptServerBuilder , Config , Context , getReturnFields } from '@deja-vu/concept-server' ; import { IResolvers } from 'graphql-tools' ; import * as _ from 'lodash' ; import { v4 as uuid } from 'uuid' ; import { CreateRankingInput , Ranking , RankingDoc , RankingsInput , TargetRank } from './schema' ; interface RankingConfig extends Config { oneToOneRanking ? : boolean ; } const componentRequestTable = { 'create-ranking' : ( extraInfo ) => `
    mutation CreateRanking($input: CreateRankingInput!) {
      createRanking(input: $input) ${ getReturnFields ( extraInfo ) }
    }
  ` , 'show-ranking' : ( extraInfo ) => `
    query ShowRanking($id: ID!, $sourceId: ID) {
      ranking(id: $id, sourceId: $sourceId) ${ getReturnFields ( extraInfo ) }
    }
  ` , 'show-rankings' : ( extraInfo ) => `
    query ShowRankings($input: RankingsInput!) {
      rankings(input: $input) ${ getReturnFields ( extraInfo ) }
    }
  ` , 'show-fractional-ranking' : ( extraInfo ) => `
    query ShowFractionRanking($targetIds: [ID!]) {
      fractionalRanking(targetIds: $targetIds) ${ getReturnFields ( extraInfo ) }
    }
  ` } ; function rankingDocsToRanking ( rankingDocs ) { if ( rankingDocs . length === 0 ) { throw new Error ( 'Could not create Ranking object without targets' ) ; } return { id : rankingDocs [ 0 ] . id , sourceId : rankingDocs [ 0 ] . sourceId , targets : rankingDocs . map ( ( rankingDoc ) : TargetRank => { return { id : rankingDoc . targetId , rank : rankingDoc . rank } ; } ) } ; } function resolvers ( db , _config ) { const rankings = db . collection ( 'rankings' ) ; return { Query : { ranking : async ( _root , { id , sourceId } ) => { const query = { } ; if ( id ) { query [ 'id' ] = id ; } if ( sourceId ) { query [ 'sourceId' ] = sourceId ; } const rankingDocs = await rankings . find ( query ) ; if ( rankingDocs . length === 0 ) { throw new Error ( `Ranking ${ id } not found` ) ; } return rankingDocsToRanking ( rankingDocs ) ; } , rankings : async ( _root , { input } ) => { const groupedRankingDocs = await rankings . aggregate ( [ { $match : input } , { $group : { _id : { id : '$id' , sourceId : '$sourceId' } , rankingDocs : { $push : '$$ROOT' } } } ] ) . toArray ( ) as any [ ] ; return groupedRankingDocs . map ( ( group ) => rankingDocsToRanking ( group . rankingDocs ) ) ; } , fractionalRanking : async ( _root , { targetIds } ) => { const targetRankings = await rankings . aggregate ( [ { $match : { targetId : { $in : targetIds } } } , { $group : { _id : '$targetId' , sumRank : { $sum : '$rank' } } } , { $project : { id : '$_id' , sumRank : 1 } } ] ) . sort ( { sumRank : 1 } ) . toArray ( ) ; const scores = _ . map ( targetRankings , 'sumRank' ) ; const reversedScores = scores . slice ( 0 ) . reverse ( ) ; return targetRankings . map ( ( target ) => { const score = target [ 'sumRank' ] ; target [ 'rank' ] = ( scores . indexOf ( score ) + 1 + reversedScores . length - reversedScores . indexOf ( score ) ) / 2 ; return target ; } ) ; } } , Ranking : { id : ( ranking ) => ranking . id , sourceId : ( ranking ) => ranking . sourceId , targets : ( ranking ) => ranking . targets } , TargetRank : { id : ( target ) => target . id , rank : ( target ) => target . rank } , Mutation : { createRanking : async ( _root , { input } , context ) => { const rankingId = input . id ? input . id : uuid ( ) ; const rankingDocs = _ . map ( input . targets , ( target ) => { return { id : rankingId , sourceId : input . sourceId , targetId : target . id , rank : target . rank } ; } ) ; await rankings . insertMany ( context , rankingDocs ) ; return rankingDocsToRanking ( rankingDocs ) ; } } } ; } const rankingConcept = new ConceptServerBuilder < RankingConfig > ( 'ranking' ) . initDb ( ( db , config ) : Promise < any > => { const rankings = db . collection ( 'rankings' ) ; const sourceTargetIndexOptions = config . oneToOneRanking ? { unique : true , sparse : true } : { } ; return Promise . all ( [ rankings . createIndex ( { id : 1 , sourceId : 1 , rank : 1 } , { unique : true , sparse : true } ) , rankings . createIndex ( { sourceId : 1 , targetId : 1 } , sourceTargetIndexOptions ) ] ) ; } ) . componentRequestTable ( componentRequestTable ) . resolvers ( resolvers ) . build ( ) ; rankingConcept . start ( ) ;