2016-06-23 75 views
2

我將首先介紹我的應用程序:簡單的投票應用程序,用戶可以在其中創建投票並進行投票。簡單。
目前我的graphql模式由用戶類型,投票類型和投票類型組成,其中用戶和投票使用中繼連接與其投票具有一對多關係。 投票類型包含引用其投票人和投票的時間戳和實際投票值。據我所知,在graphql列表上使用連接的優點之一是能夠在邊緣存儲數據(除了分頁和更多...)。 我怎麼能這樣做?React Relay:向邊緣添加數據

如果確實有可能,我的計劃是擺脫投票類型,通過連接直接連接用戶和他投票的投票,並將投票值和它的時間戳存儲在連接邊緣。

如果重要的是,選民和他的民意調查之間的聯繫應該是雙向的,即每個用戶都與他投票的民意調查相關聯,並且每個民意調查都與其選民相關聯。

+0

「如果這是重要的,選民和他的調查之間的關係應該是雙向的,即每個用戶連接到他的投票調查,每個調查連接到其選民「。 - 是的,這看起來很重要。邊緣是特定於連接的,並且您在這裏有兩個連接,因此每個連接都需要明確的邊緣。如果選民投票 - 投票關係存在兩個真相,那麼真相的來源在哪裏? – wincent

+0

我明白你在說什麼,我只是將投票類型看作額外的中介,因爲我已經有了連接,但你可能是對的。 –

回答

2

這是使用一個單獨的Vote實體中Michael Paris's answer描述,但是,爲解決「如何在邊緣定義附加字段?」更普遍的問題,注意,您可以通過edgeFieldsconnectionDefinitions可能是更好的建模功能in the graphql-relay module

有一個例子in the test suite

var {connectionType: friendConnection} = connectionDefinitions({ 
    name: 'Friend', 
    nodeType: userType, 
    resolveNode: edge => allUsers[edge.node], 
    edgeFields:() => ({ 
    friendshipTime: { 
     type: GraphQLString, 
     resolve:() => 'Yesterday' 
    } 
    }), 
    connectionFields:() => ({ 
    totalCount: { 
     type: GraphQLInt, 
     resolve:() => allUsers.length - 1 
    } 
    }), 
}); 
3

這聽起來像你真的已經接近你想要的東西了。我認爲使用投票類型作爲用戶和投票間的中間人是一個很好的解決方案。這樣做,這樣會讓你發出一個看起來像這樣的查詢:

// Direction 1: User -> Vote -> Poll 
 
query GetUser($id: "abc") { 
 
    getUser(id: $id) { 
 
    username 
 
    votes(first: 10) { 
 
     edges { 
 
     node { 
 
      value 
 
      poll { 
 
      name 
 
      } 
 
     } 
 
     cursor 
 
     } 
 
    } 
 
    } 
 
} 
 

 
// Direction 2: Poll -> Vote -> User 
 
query GetPoll($id: "xyz") { 
 
    getPoll(id: $id) { 
 
    name 
 
    votes(first: 10) { 
 
     edges { 
 
     node { 
 
      value 
 
      user { 
 
      username 
 
      } 
 
     } 
 
     cursor 
 
     } 
 
    } 
 
    } 
 
}

在這個例子中,您的投票類型是存儲沿着邊緣的信息的實體。 「列表上的連接」的一個優點是您可以沿邊存儲信息,這是正確的,但我認爲更大的好處是能夠通過大量對象進行分頁。

要在服務器上實現此功能,您必須爲User和Poll上的Connection字段(即上面示例中的'votes'字段)編寫自定義解析方法。取決於你如何存儲你的數據,這將改變,但這是一個想法的一些僞代碼。

type Vote { 
 
    value: String, 
 
    poll: Poll, // Both poll & user would have resolve functions to grab their respective object. 
 
    user: User 
 
} 
 

 
type VoteEdge { 
 
    node: Vote, 
 
    cursor: String // an opaque cursor used in the 'before' & 'after' pagination args 
 
} 
 

 
type PageInfo { 
 
    hasNextPage: Boolean, 
 
    hasPreviousPage: Boolean 
 
} 
 

 
type VotesConnectionPayload { 
 
    edges: [VoteEdge], 
 
    pageInfo: PageInfo 
 
} 
 

 
const UserType = new GraphQLObjectType({ 
 
    name: 'User', 
 
    fields:() => ({ 
 
    id: { 
 
     type: new GraphQLNonNull(GraphQLID), 
 
     description: "A unique identifier." 
 
    }, 
 
    username: { 
 
     type: new GraphQLNonNull(GraphQLString), 
 
     description: "A username", 
 
    }, 
 
    votes: { 
 
     type: VotesConnectionPayload, 
 
     description: "A paginated set of the user's votes", 
 
     args: { // pagination args 
 
     first: { 
 
      type: GraphQLInt 
 
     }, 
 
     after: { 
 
      type: GraphQLString 
 
     }, 
 
     last: { 
 
      type: GraphQLInt 
 
     }, 
 
     before: { 
 
      type: GraphQLString 
 
     } 
 
     } 
 
     resolve: (parent, paginationArgs, ctxt) => { 
 

 
     // You can pass a reference to your data source in the ctxt. 
 
     const db = ctxt.db; 
 

 
     // Go get the full set of votes for my user. Preferably this returns a cursor 
 
     // to the set so you don't pull everything over the network 
 
     return db.getVotesForUser(parent.id).then(votes => { 
 

 
      // Assume we have a pagination function that applies the pagination args 
 
      // See https://facebook.github.io/relay/graphql/connections.htm for more details 
 
      return paginate(votes, paginationArgs); 
 

 
     }).then((paginatedVotes, pageInfo) => { 
 

 
      // Format the votes as a connection payload. 
 
      const edges = paginatedVotes.map(vote => { 
 

 
      // There are many ways to handle cursors but lets assume 
 
      // we have a magic function that gets one. 
 
      return { 
 
       cursor: getCursor(vote), 
 
       node: vote 
 
      } 
 

 
      }); 
 

 
      return { 
 
      edges: edges, 
 
      pageInfo: pageInfo 
 
      } 
 
     }) 
 
     } 
 
    } 
 
    }) 
 
});

你將不得不在你的投票類型的反方向同樣做一些事情。要將對象添加到連接,您只需創建一個指向正確的用戶和帖子的Vote對象。 db.getVotesForUser()方法應該足夠聰明,以實現這是一對多連接,然後可以拉出正確的對象。

創建一個處理連接的標準方式可能是一項艱鉅的任務,但幸運的是,有些服務可以幫助您開始使用GraphQL,而無需自己實現所有後端邏輯。我爲其中一項服務https://scaphold.io工作,如果您有興趣,我會很樂意與您進一步討論此解決方案。

+0

謝謝你的詳細解答!但是由於邊緣已經扮演了中間人的角色,我認爲最好是擺脫投票類型,並且只是爲用戶分頁投票,投票值將存儲在連接邊緣,而不是無用的投票類型。你不認爲這應該會更好嗎? –

+0

我想再次感謝您的回答,我要標記wincent作爲答案,因爲這是我本質上要求的,但您的回答非常有幫助,非常感謝! –