对象关系

对象关系有三种。一对一关系,即一个对象关联另一个对象的关系;一对多关系,即多个对象关联一个对象的关系;多对多关系,即多个对象中复杂的关系。

有4种方法在Parse中建立对象关系:

一对多关系

如果你想使用指针或数组来实现一对多的关系,有几个因素需要考虑。首先要考虑,这种关系中涉及多少个对象,如果子对象数量多余100个,推荐使用一对多的关系,如果少于100个,使用数组可能更方便,尤其是你想根据父对象一次获取所有子对象的时候。

使用指针

假设我们有一个游戏应用,要跟踪玩家的分数和成就,在Parse中,我们可以把这些数据保存在Game对象中。如果这个游戏非常成功,所有玩家可能会存储几千个Game对象到系统中,对于这样的情况,子对象的数量可能任意大,指针就是最合适的选择。

要实现这样的游戏应用,我们要确保每个Game对象都关联一个用户对象,我们可以这样实现:

  1. var game = new Parse.Object("Game");
  2. game.set("createdBy", Parse.User.current());

我们可以通过匹配用户对象来查询用户的所有Game对象:

  1. var query = new Parse.Query("Game");
  2. query.equalTo("createdBy", Parse.User.current());

如果要获取Game对象的创建者,可以通过指针字段获取:

  1. // Game对象
  2. var game = ...
  3. // Game对象创建者
  4. var user = game.get("createdBy");

大多数情况下,实现一对多的关系,使用指针都是最好的选择。

使用数组

当我们已知子对象的数量很少时,使用数组是个好办法。includeKey参数提供一些便捷性,可以让你在获得父对象的同时获得所有的子对象。不过,如果子对象数量很大的话,会造成响应时间增长。

假设在我们的游戏中,要跟踪玩家在游戏中积累的武器,并且要限制武器的数量。我们已知武器的数量不大,并且需把用户的武器按顺序显示在屏幕中。在这个例子中,使用数组就是很好的方法。

现在让我们来为用户对象创建一个weaponsList(武器列表)字段,然后存储一些Weapon(武器)到weaponsList中:

  1. // 假设我们有4个武器对象
  2. var scimitar = ...
  3. var plasmaRifle = ...
  4. var grenade = ...
  5. var bunnyRabbit = ...
  6. //插入武器到对象
  7. var weapons = [scimitar, plasmaRifle, grenade, bunnyRabbit];
  8. // 为用户设置weaponsList
  9. var user = Parse.User.current();
  10. user.set("weaponsList", weapons);

如果以后想再获得Weapon对象,只需要一行代码即可:

  1. var weapons = Parse.User.current().get("weaponsList")

有时我们fetch一个对象,想要同时拉取父对象的子对象,只需要为Parse.Query添加includeKey即可:

  1. // 用户对象的查询对象
  2. var userQuery = new Parse.Query(Parse.User);
  3. // configure any constraints on your query...
  4. // for example, you may want users who are also playing with or against you
  5. // 告诉查询对象想要同时拉取到哪个子对象
  6. userQuery.include("weaponsList");
  7. // 执行查询
  8. userQuery.find({
  9. success: function(results){
  10. // results contains all of the User objects, and their associated Weapon objects, too
  11. }
  12. });

你也可以通过子对象获取父对象,比如,我们要查询哪个用户具有给定的子对象,我们可以为查询对象增加一个这样的条件:

  1. // 增加武器列表是否包含某武器的条件
  2. userQuery.equalTo("weaponsList", scimitar);
  3. // 或者传入武器数组查找包含多个武器的用户
  4. userQuery.containedIn("weaponsList", arrayOfWeapons);

多对多关系

现在我们来处理多对多关系。假设我们有一个阅读应用,我们需要构建Book对象和Author对象,一个作者可以写多本书,并且一本书可以有多个作者,这就是一个多对多关系的场景,你必须从数组、对象关系、指针三个方案中做出选择。

决策的关键点在于你是否有父子对象以外的信息要保存。

如果没有,使用关系对象或者使用数组是最简单的方案。通常,使用数组会产生更高的并发和较少的请求。

如果有,那么使用指针指向关联对象更合适。

使用Parse.Realtion

使用Parse.Relation,我们可以在Book对象和一些Author对象中建立关联。你可以在数据浏览器中,在Book对象上创建一个relation类型的authors字段。然后,我们可以为Book对象关联一些author:

  1. // 假设有这么几个author对象
  2. var authorOne = ...
  3. var authorTwo = ...
  4. var authorThree = ...
  5. // 构建book对象
  6. var book = new Parse.Object("Book");
  7. // 为book对象关联作者
  8. var relation = book.relation("authors");
  9. relation.add(authorOne);
  10. relation.add(authorTwo);
  11. relation.add(authorThree);
  12. // 保存
  13. book.save();

要获取一本书的作者列表,可以创建一个查询:

  1. // 假设有一个book对象
  2. var book = ...
  3. // 通过relation方法获取relation对象
  4. var relation = book.relation("authors");
  5. // 通过relation对象构建查询对象
  6. var query = relation.query();
  7. // 执行查询即可

假如你想获取一个作者参与编辑过的书籍列表, 只需要为查询对象增加条件即可:

  1. // 假设有一个作者对象
  2. var author = ...
  3. // 构建查询对象
  4. var query = new Parse.Query("Book");
  5. // 增加约束条件
  6. query.equalTo("authors", author);

使用指针

假设我们要在用户间构建一个following(我关注的)/follower(关注我的)关系,一个用户可以关注多个用户,就像社交平台一样。在我们的应用中,我们不只需要知道谁关注了谁,还需要知道是什么时候关注的。要有序的记录这些数据,你必须创建一个独立的class表,这个表我们将命名为Follow,它有form字段和to字段,都是指向一个用户的指针。除了这些,你还可以添加一个Date类型的字段date用来记录时间。

现在,如果你想保存两个用户的关注信息,只需要在Follow表中设置fromtodate的值即可:

  1. var otherUser = ...
  2. // 构建Follow对象
  3. var follow = new Parse.Object("Follow");
  4. follow.set("from", Parse.User.current());
  5. follow.set("to", otherUser);
  6. follow.set("date", Date());
  7. follow.save();

如果你想查找你关注的所有用户,可以创建一个Follow的查询对象:

  1. var query = new Parse.Query("Follow");
  2. query.equalTo("from", Parse.User.current());
  3. query.find({
  4. success: function(users){
  5. ...
  6. }
  7. });

要查找关注我的所有用户也同样简单,通过to字段查找即可:

  1. // 构建查询对象
  2. var query = new Parse.Query("Follow");
  3. query.equalTo("to", Parse.User.current());
  4. query.find({
  5. success: function(users){
  6. ...
  7. }
  8. });

使用数组

在多对多关系中使用数组和在一对多关系中使用数组很像,关系一方的对象用一个数组字段包含另一方的一些对象。

还是假设我们有一个阅读应有,有BookAuthor两个对象,Book对象有一个authors数组字段包含了Book的所有Author,数组在这里就非常合适,因为一本书的作者不会超过100个人。不过一个作者可能会写100本以上的书。

下面是我们保存BookAuthor关系的示例:

  1. // 假设有一个作者对象
  2. var author = ...
  3. // 假设有个Book对象
  4. var book = ...
  5. // 把author对象添加到authors数组中
  6. book.add("authors", author);

因为authors是一个数组,你应该在查询Book的时使用includeKey参数把author包含进来,以便服务器返回Book对象时同时包含作者:

  1. // 构建Book查询对象
  2. var bookQuery = new Parse.Query("Book");
  3. // 告诉查询对象包含authors字段
  4. bookQuery.include("authors");
  5. // execute the query
  6. bookQuery.find({
  7. success: function(books){
  8. ...
  9. }
  10. });

要根据拿到的Book对象获取authors数组可以直接使用get方法:

  1. var authorList = book.get("authors")

最后,如果你想根据一个用户对象,查询用户所有参与编辑过的书,直接使用include方法即可:

  1. // 构建查询对象
  2. var bookQuery = new Parse.Query("Book");
  3. // 增加约束条件
  4. bookQuery.equalTo("authors", author);
  5. // 告诉查询对象要包含作者
  6. bookQuery.include("authors");
  7. // 执行查询
  8. bookQuery.find({
  9. success: function(books){
  10. ...
  11. }
  12. });

一对一关系

在某些情况下,要将一个对象分成两个对象,一对一关系就很合适。这种例子可能很少,但是也有如下两个例子:

  • 限制某些用户数据的可见。在这个场景中,你要把对象一分为二,其中一部分数据对其他用户可见,关联的另一部分数据对其他用户不可见,并且被ACL保护。
  • 分割对象大小。在这个场景中,你的原始对象大小超过了允许的128K,所以你决定创建第二个对象存储额外的数据。所以设计好你的数据模型很重要,可以避免对象过大要分割对象。如果你无法避免这样做,你可以考虑将大文件存储到Parse.File中。

最后,感谢你阅读了这么多,我们为这复杂程度向你致歉。虽然对象关系是比较难的部分,但是也有好的一面:

它可比人际关系简单多了。