关系
有三种关系。一对一关系使一个对象能够与另一个对象相关联。一对多关系使一个对象具有许多相关对象。最后,多对多关系使许多对象之间的复杂关系成为可能。
在Parse中建立关系有四种方式:
一对多关系(One-to-Many)
当您考虑一对多关系以及是否实现指针或数组时,有几个要考虑的因素。首先,这种关系涉及多少对象?如果关系的“多”一方可能包含非常多(大于100个)的对象,则必须使用指针。如果对象的数量很少(少于100个),那么数组可能更方便,特别是作为父对象,您需要同时获取其所有相关对象(一对多关系中的“多”)时。
1.使用指针
假设我们有一个游戏app。游戏记录玩家每次玩的成绩和成就。在Parse中,我们可以将这些数据存储在一个Game对象中。如果app非常成功,每个玩家将在系统中存储数千个Game对象。对于这样的情况,如果关系的数量可以任意大,最好使用指针。
假设在这个游戏app中,每个Game对象都与一个Parse User相关联。我们可以这样实现:
var game = new Parse.Object("Game");
game.set("createdBy", Parse.User.current());
我们可以获取由Parse User创建的所有Game对象的查询:
var query = new Parse.Query("Game");
query.equalTo("createdBy", Parse.User.current());
而且,如果我们想要找到创建特定Game对象的Parse User,可以使用createdBy关键字查找:
// say we have a Game object
var game = ...
// getting the user who created the Game
var user = game.get("createdBy");
对于大多数情况,指针将是实现一对多关系的最佳选择。
2.使用数组
当我们知道一对多关系中涉及到的对象的数量将会很小时,数组是理想的。数组的includeKey参数具备一些优势。提供这个参数能够同时获得“一对多”关系中所有的“多”对象和“一”对象。然而,如果关系中涉及的对象数量很多,则响应时间会很慢。
假设在游戏中,玩家能够跟踪他们的角色在游戏中所积累的所有武器,而且只能有十几种武器。在这个例子中,我们知道武器的数量不会很大。我们还可以让玩家指定武器显示的顺序。此时,数组是理想的,一是因为这个数组较小,二是因为我们也希望保留用户每次玩游戏时所设定的顺序:
我们首先在Parse User对象上创建一个列weaponsList。
现在我们来储存一些Weapon对象到weaponsList中:
// let's say we have four weapons
var scimitar = ...
var plasmaRifle = ...
var grenade = ...
var bunnyRabbit = ...
// stick the objects in an array
var weapons = [scimitar, plasmaRifle, grenade, bunnyRabbit];
// store the weapons for the user
var user = Parse.User.current();
user.set("weaponsList", weapons);
后来,如果我们想要检索Weapon对象,只需要一行代码:
var weapons = Parse.User.current().get("weaponsList")
有时,我们希望在获取一对多关系中“一”对象的同时获取其中的“多”对象。一个技巧是使用includeKey(Android中为include)参数,每当我们使用Parse Queries在获取Parse User对象的同时,也可以获取Weapon对象数组(存储在weaponsList列中):
// set up our query for a User object
var userQuery = new Parse.Query(Parse.User);
// configure any constraints on your query...
// for example, you may want users who are also playing with or against you
// tell the query to fetch all of the Weapon objects along with the user
// get the "many" at the same time that you're getting the "one"
userQuery.include("weaponsList");
// execute the query
userQuery.find({
success: function(results){
// results contains all of the User objects, and their associated Weapon objects, too
}
});
你也可以通过“多”对象获得一对多关系中的“一”对象。例如,如果我们想要找到所有拥有给定Weapon的Parse User对象,查询约束条件可以这样写:
// add a constraint to query for whenever a specific Weapon is in an array
userQuery.equalTo("weaponsList", scimitar);
// or query using an array of Weapon objects...
userQuery.containedIn("weaponsList", arrayOfWeapons);
多对多关系(Many-to-Many)
假设我们有一个读书app,我们想对Book对象和Author对象进行建模。我们知道,一个给定的作者可以写很多书,一本书可以有多个作者。这是一个多对多关系场景,您必须在数组、Parse Relations或创建自己的Join Table之间进行选择。
这里的关键点在于您是否要附加元数据到两个实体之间的关系中。
如果没有,Parse Relations或使用数组将是最直接的选择。一般来说,使用数组将获得更高的性能、更少的查询量。如果多对多关系的任何一方可能超过100个对象,那么,跟指针在一对多关系中更好的原因一样,Parse Relations或Join Table将是更好的选择。
另一方面,如果要将元数据附加到关系中,则需要创建一个单独的表(“Join Table”)来存放关系的两端。记住,这是关系的信息,而不是关系的任何一方的对象。您可能对使用Join Table方法附加元数据的一些示例感兴趣,包括:
1.使用Parse Relations
使用Parse Relations,我们可以创建一个Book和几个Author对象之间的关系。在数据浏览器中,您可以在Book对象上创建一个类型关系列,并将其命名为authors。
之后,我们可以将一些作者与本书联系起来:
// let’s say we have a few objects representing Author objects
var authorOne = ...
var authorTwo = ...
var authorThree = ...
// now we create a book object
var book = new Parse.Object("Book");
// now let’s associate the authors with the book
// remember, we created a "authors" relation on Book
var relation = book.relation("authors");
relation.add(authorOne);
relation.add(authorTwo);
relation.add(authorThree);
// now save the book object
book.save();
要获取写书的作者列表,可以创建一个查询:
// suppose we have a book object
var book = ...
// create a relation based on the authors key
var relation = book.relation("authors");
// generate a query based on that relation
var query = relation.query();
// now execute the query
也许你还想得到某个作者撰写的所有书籍的清单。您可以创建一种略有不同的查询来获得这个反关系:
// suppose we have a author object, for which we want to get all books
var author = ...
// first we will create a query on the Book object
var query = new Parse.Query("Book");
// now we will query the authors relation to see if the author object we have
// is contained therein
query.equalTo("authors", author);
2.使用Join Tables
在某些情况下,我们想要获得关系上更多的信息。例如,假设我们正在建模用户之间的关注/被关注关系:给定的用户可以关注另一个用户,就像流行的社交网络一样。在我们的app中,我们不仅想知道用户A是否关注用户B,而且我们也想知道用户A何时开始关注用户B。此信息不能包含在Parse Relations中。为了跟踪这些数据,您必须创建一个单独的表来记录这个关系。我们建立表Follow,它有一from列和一to列,每一列都有一个指向Parse User的指针。除了关系之外,您还可以添加一个Date类型的列date。
现在,当你想保存两个用户之间的关系时,在Follow表中创建一行数据,相应的填写from,to和date字段:
var otherUser = ...
// create an entry in the Follow table
var follow = new Parse.Object("Follow");
follow.set("from", Parse.User.current());
follow.set("to", otherUser);
follow.set("date", Date());
follow.save();
如果想找到我们正在关注的所有人,我们可以在Follow表上执行查询:
var query = new Parse.Query("Follow");
query.equalTo("from", Parse.User.current());
query.find({
success: function(users){
...
}
});
通过to字段可以轻松地查找到关注当前用户的所有用户:
// create an entry in the Follow table
var query = new Parse.Query("Follow");
query.equalTo("to", Parse.User.current());
query.find({
success: function(users){
...
}
});
3.使用数组
数组在多对多关系中的使用方式与在一对多关系的方式大致相同。关系一侧的所有对象都将包含一个Array列,其中包含关系另一侧的若干对象。
假设我们有一个阅读app ,包含了Book和Author对象。该Book对象包含一个Author对象数组(键名authors)。数组非常适合这种情况,因为一本书不太可能有超过100个作者。正是基于此,我们将这个Author数组放在Book对象中。毕竟,一个作者可以写出100多本书。
我们这样保存Book和Author之间的关系:
// let's say we have an author
var author = ...
// and let's also say we have an book
var book = ...
// add the author to the authors list for the book
book.add("authors", author);
由于作者列表是一个数组,所以在获取Book对象时应该使用includeKey(Android上使用include)参数,以便Parse在返回book对象时同时返回其所有作者:
// set up our query for the Book object
var bookQuery = new Parse.Query("Book");
// configure any constraints on your query...
// tell the query to fetch all of the Author objects along with the Book
bookQuery.include("authors");
// execute the query
bookQuery.find({
success: function(books){
...
}
});
在此,获取给定Book对象的所有Author非常直观:
var authorList = book.get("authors")
最后,假如你想找到包含某个Author的所有Book对象,只需要在查询上带上约束条件:
// set up our query for the Book object
var bookQuery = new Parse.Query("Book");
// configure any constraints on your query...
bookQuery.equalTo("authors", author);
// tell the query to fetch all of the Author objects along with the Book
bookQuery.include("authors");
// execute the query
bookQuery.find({
success: function(books){
...
}
});
一对一关系(One-to-One)
在Parse中,一对一关系非常适用于需要将一个对象分割成两个对象的情况。这些情况应该很少,但也有两个例子:
1.限制某些用户数据的可见性。在这种情况下,可以将对象拆分为两个,其中对象的一部分数据对其他用户可见,而关联对象的部分数据则对本用户是私有的(并通过ACL保护)。
2.拆分对象的大小。在这种情况下,原始对象超过了对象允许的最大大小128KB,因此需要创建辅助对象来存放额外的数据。通常应该设计好数据模型以避免这样庞大的对象,而不是分开它们。如果不能避免这样做,还可以考虑将大数据存储在Parse文件中。
感谢您阅读至此。我们为复杂性道歉。一般而言,数据建模关系是一个难题。但要看到光明的一面:它比人与人的关系还是要简单一些。