JSData 中的多对多关系支持
Many-to-Many relational support in JSData
有没有办法在JSData中定义多对多关系?
例如,我有这 3 个表:
实体
实体文件
文件
在 "entity" 上,我想要一个名为 "files" 的关系,它通过 entityFile 进行连接。
好问题。一个典型的多对多关系就是两个一对多关系:
任何实现中一个更重要的细节是:哪里是
关系信息存储? 这个问题的答案决定了如何
可以访问实体的关系。让我们探讨几个选项。
前提:
A
有很多 B
B
有很多 A
选项 1
关系信息存储在 A
的实例上。
在这种情况下,一旦您拥有 A
的实例,您就可以找到它的关联
B
个实例,因为关联的 B
个实例的 ID 存储在
A
。这也意味着如果你只有一个 B
的实例,唯一的方法是
找到 B
实例相关的 A
的所有实例将是
在 A
的所有实例中搜索 b_ids
字段包含
B
实例的 id
。
一个例子
var Player = store.defineResource({
name: 'player',
relations: {
hasMany: {
team: {
// JSData will setup a "teams" property accessor on
// instances of player which searches the store for
// that player's teams
localField: 'teams',
localKeys: 'team_ids'
}
}
}
})
var Team = store.defineResource({
name: 'team',
relations: {
hasMany: {
player: {
localField: 'players',
// Since relationship information is stored
// on the player, in order to retrieve a
// team's players we have to do a O(n^2)
// search through all the player instances
foreignKeys: 'team_ids'
}
}
}
})
现在让我们看看实际效果:
var player = Player.inject({
id: 1,
team_ids: [3, 4]
})
// The player's teams aren't in the store yet
player.teams // [ ]
var player2 = Player.inject({
id: 2,
team_ids: [4, 5],
teams: [
{
id: 4
},
{
id: 5
}
]
})
// See the property accessor in action
player2.teams // [{ id: 4 }, { id: 5 }]
// One of player one's teams is in the store now
player.teams // [{ id: 4 }]
// Access the relation from the reverse direction
var team4 = Team.get(4) // { id: 4 }
// The property accessor makes a O(n^2) search of the store because
// the relationship information isn't stored on the team
team4.players // [{ id: 1, team_ids: [3, 4] }, { id: 2, team_ids: [4, 5] }]
让我们从持久层加载关系:
// To get an authoritative list of player one's
// teams we ask our persistence layer.
// Using the HTTP adapter, this might make a request like this:
// GET /team?where={"id":{"in":[3,4]}} (this would be url encoded)
//
// This method call makes this call internally:
// Team.findAll({ where: { id: { 'in': player.team_ids } } })
player.DSLoadRelations(['team']).then(function (player) {
// The adapter responded with an array of teams, which
// got injected into the datastore.
// The property accessor picks up the newly injected team3
player.teams // [{ id: 3 }, { id: 4 }]
var team3 = Team.get(3)
// Retrieve all of team3's players.
// Using the HTTP adapter, this might make a request like this:
// // GET /player?where={"team_ids":{"contains":3}} (this would be url encoded)
//
// This method call makes this call internally:
// Player.findAll({ where: { team_ids: { 'contains': team3.id } } })
return team3.DSLoadRelations(['player'])
})
如果您使用的是 HTTP 适配器,则由您的服务器来解析
querystring 并用正确的数据响应。如果您正在使用以下任何一种
其他适配器则适配器已经知道如何 return 正确的数据。
在前端 和 后端使用 JSData 使这变得太简单了。
选项 2
关系信息存储在 B
的实例上。
这只是选项 1 的逆运算。
选项 3
"A
hasMany B
" 关系信息存储在实例 A
上,并且
“B
hasMany A
”关系信息存储在 B
.
的实例上
这只是选项 1 除了它现在可以双向工作。
这种方法的一个优点是您可以从两者访问关系
指示而不需要使用 foreignKeys
选项。的缺点
这种方法是当关系发生变化时必须修改数据
多个地方。
选项 4
关系信息存储在枢轴(连接)中table。
A
hasMany C
and C
belongsTo A
,这里的实际关系
信息存储在 C
.
B
hasMany C
and C
belongsTo B
,这里的实际关系
信息存储在 C
.
一个例子:
var Player = store.defineResource({
name: 'player',
relations: {
hasMany: {
membership: {
localField: 'memberships',
// relationship information is stored on the membership
foreignKey: 'player_id'
}
}
}
})
var Team = store.defineResource({
name: 'team',
relations: {
hasMany: {
membership: {
localField: 'memberships',
// relationship information is stored on the membership
foreignKey: 'team_id'
}
}
}
})
和枢轴资源:
var Membership = store.defineResource({
name: 'membership',
relations: {
belongsTo: {
player: {
localField: 'player',
// relationship information is stored on the membership
localKey: 'player_id'
},
team: {
localField: 'team',
// relationship information is stored on the membership
localKey: 'team_id'
}
}
}
})
现在让我们看看实际效果:
var player = Player.inject({ id: 1 })
var player2 = Player.inject({ id: 2 })
var team3 = Team.inject({ id: 3 })
var team4 = Team.inject({ id: 4 })
var team4 = Team.inject({ id: 5 })
player.memberships // [ ]
player2.memberships // [ ]
team3.memberships // [ ]
team4.memberships // [ ]
team5.memberships // [ ]
注意此时我们还不能访问任何关系
// The relationships stored in our pivot table
var memberships = Membership.inject([
{
id: 997,
player_id: 1,
// player one is on team three
team_id: 3
},
{
id: 998,
player_id: 1,
// player one is also on team four
team_id: 4
},
{
id: 999,
player_id: 2,
// team four also has player 2
team_id: 4
},
{
id: 1000,
player_id: 2,
// player 2 is also on team 5
team_id: 5
}
])
现在我们有会员信息
player.memberships // [{ id: 997, ... }, { id: 998, ... }]
player2.memberships // [{ id: 998, ... }, { id: 999, ... }]
team3.memberships // [{ id: 997, ... }]
team4.memberships // [{ id: 998, ... }, { id: 999, ... }]
team5.memberships // [{ id: 1000, ... }]
现在,将数据透视表 table 数据发送到前端有点笨拙
需要你的 JavaScript 来整理关系。为此你会想要
一些辅助方法:
var Player = store.defineResource({
name: 'player',
relations: {...},
computed: {
teams: {
get: function () {
return store.filter('membership', {
player_id: this.id
}).map(function (membership) {
return store.get('team', membership.team_id)
})
}
}
},
// Instance methods
methods: {
getTeams: function () {
return Player.getTeams(this.id)
}
}
// Static Class Methods
getTeams: function (id) {
return this.loadRelations(id, ['membership']).then(function (memberships) {
return store.findAll('team', {
where: {
id: {
'in': memberships.map(function (membership) {
return membership.team_id
})
}
}
})
})
}
})
我会让您了解团队资源的类似方法。
如果您不想麻烦辅助方法,那么您可以
只需在后端实现它们,让你的枢轴 table 对
前端并使您的多对多关系看起来更像选项 1、2 或
3.
有用链接
有没有办法在JSData中定义多对多关系?
例如,我有这 3 个表:
实体 实体文件 文件
在 "entity" 上,我想要一个名为 "files" 的关系,它通过 entityFile 进行连接。
好问题。一个典型的多对多关系就是两个一对多关系:
任何实现中一个更重要的细节是:哪里是 关系信息存储? 这个问题的答案决定了如何 可以访问实体的关系。让我们探讨几个选项。
前提:
A
有很多 B
B
有很多 A
选项 1
关系信息存储在 A
的实例上。
在这种情况下,一旦您拥有 A
的实例,您就可以找到它的关联
B
个实例,因为关联的 B
个实例的 ID 存储在
A
。这也意味着如果你只有一个 B
的实例,唯一的方法是
找到 B
实例相关的 A
的所有实例将是
在 A
的所有实例中搜索 b_ids
字段包含
B
实例的 id
。
一个例子
var Player = store.defineResource({
name: 'player',
relations: {
hasMany: {
team: {
// JSData will setup a "teams" property accessor on
// instances of player which searches the store for
// that player's teams
localField: 'teams',
localKeys: 'team_ids'
}
}
}
})
var Team = store.defineResource({
name: 'team',
relations: {
hasMany: {
player: {
localField: 'players',
// Since relationship information is stored
// on the player, in order to retrieve a
// team's players we have to do a O(n^2)
// search through all the player instances
foreignKeys: 'team_ids'
}
}
}
})
现在让我们看看实际效果:
var player = Player.inject({
id: 1,
team_ids: [3, 4]
})
// The player's teams aren't in the store yet
player.teams // [ ]
var player2 = Player.inject({
id: 2,
team_ids: [4, 5],
teams: [
{
id: 4
},
{
id: 5
}
]
})
// See the property accessor in action
player2.teams // [{ id: 4 }, { id: 5 }]
// One of player one's teams is in the store now
player.teams // [{ id: 4 }]
// Access the relation from the reverse direction
var team4 = Team.get(4) // { id: 4 }
// The property accessor makes a O(n^2) search of the store because
// the relationship information isn't stored on the team
team4.players // [{ id: 1, team_ids: [3, 4] }, { id: 2, team_ids: [4, 5] }]
让我们从持久层加载关系:
// To get an authoritative list of player one's
// teams we ask our persistence layer.
// Using the HTTP adapter, this might make a request like this:
// GET /team?where={"id":{"in":[3,4]}} (this would be url encoded)
//
// This method call makes this call internally:
// Team.findAll({ where: { id: { 'in': player.team_ids } } })
player.DSLoadRelations(['team']).then(function (player) {
// The adapter responded with an array of teams, which
// got injected into the datastore.
// The property accessor picks up the newly injected team3
player.teams // [{ id: 3 }, { id: 4 }]
var team3 = Team.get(3)
// Retrieve all of team3's players.
// Using the HTTP adapter, this might make a request like this:
// // GET /player?where={"team_ids":{"contains":3}} (this would be url encoded)
//
// This method call makes this call internally:
// Player.findAll({ where: { team_ids: { 'contains': team3.id } } })
return team3.DSLoadRelations(['player'])
})
如果您使用的是 HTTP 适配器,则由您的服务器来解析 querystring 并用正确的数据响应。如果您正在使用以下任何一种 其他适配器则适配器已经知道如何 return 正确的数据。 在前端 和 后端使用 JSData 使这变得太简单了。
选项 2
关系信息存储在 B
的实例上。
这只是选项 1 的逆运算。
选项 3
"A
hasMany B
" 关系信息存储在实例 A
上,并且
“B
hasMany A
”关系信息存储在 B
.
这只是选项 1 除了它现在可以双向工作。
这种方法的一个优点是您可以从两者访问关系
指示而不需要使用 foreignKeys
选项。的缺点
这种方法是当关系发生变化时必须修改数据
多个地方。
选项 4
关系信息存储在枢轴(连接)中table。
A
hasMany C
and C
belongsTo A
,这里的实际关系
信息存储在 C
.
B
hasMany C
and C
belongsTo B
,这里的实际关系
信息存储在 C
.
一个例子:
var Player = store.defineResource({
name: 'player',
relations: {
hasMany: {
membership: {
localField: 'memberships',
// relationship information is stored on the membership
foreignKey: 'player_id'
}
}
}
})
var Team = store.defineResource({
name: 'team',
relations: {
hasMany: {
membership: {
localField: 'memberships',
// relationship information is stored on the membership
foreignKey: 'team_id'
}
}
}
})
和枢轴资源:
var Membership = store.defineResource({
name: 'membership',
relations: {
belongsTo: {
player: {
localField: 'player',
// relationship information is stored on the membership
localKey: 'player_id'
},
team: {
localField: 'team',
// relationship information is stored on the membership
localKey: 'team_id'
}
}
}
})
现在让我们看看实际效果:
var player = Player.inject({ id: 1 })
var player2 = Player.inject({ id: 2 })
var team3 = Team.inject({ id: 3 })
var team4 = Team.inject({ id: 4 })
var team4 = Team.inject({ id: 5 })
player.memberships // [ ]
player2.memberships // [ ]
team3.memberships // [ ]
team4.memberships // [ ]
team5.memberships // [ ]
注意此时我们还不能访问任何关系
// The relationships stored in our pivot table
var memberships = Membership.inject([
{
id: 997,
player_id: 1,
// player one is on team three
team_id: 3
},
{
id: 998,
player_id: 1,
// player one is also on team four
team_id: 4
},
{
id: 999,
player_id: 2,
// team four also has player 2
team_id: 4
},
{
id: 1000,
player_id: 2,
// player 2 is also on team 5
team_id: 5
}
])
现在我们有会员信息
player.memberships // [{ id: 997, ... }, { id: 998, ... }]
player2.memberships // [{ id: 998, ... }, { id: 999, ... }]
team3.memberships // [{ id: 997, ... }]
team4.memberships // [{ id: 998, ... }, { id: 999, ... }]
team5.memberships // [{ id: 1000, ... }]
现在,将数据透视表 table 数据发送到前端有点笨拙 需要你的 JavaScript 来整理关系。为此你会想要 一些辅助方法:
var Player = store.defineResource({
name: 'player',
relations: {...},
computed: {
teams: {
get: function () {
return store.filter('membership', {
player_id: this.id
}).map(function (membership) {
return store.get('team', membership.team_id)
})
}
}
},
// Instance methods
methods: {
getTeams: function () {
return Player.getTeams(this.id)
}
}
// Static Class Methods
getTeams: function (id) {
return this.loadRelations(id, ['membership']).then(function (memberships) {
return store.findAll('team', {
where: {
id: {
'in': memberships.map(function (membership) {
return membership.team_id
})
}
}
})
})
}
})
我会让您了解团队资源的类似方法。
如果您不想麻烦辅助方法,那么您可以 只需在后端实现它们,让你的枢轴 table 对 前端并使您的多对多关系看起来更像选项 1、2 或 3.
有用链接