Introduction
Parse is a cloud service platform providing a backend storage service. It offers many kinds of application program interface (API) for such as .NET, JavaScript and PHP.
In our web application we will use its REST API, this lets us interact with data on Parse from applications written in any programming language that can send an HTTP request. The request HEADER should obtain `X-Parse-Application-Id` and `X-Parse-REST-API-Key`, but for pithiness of style the following codes will pass over these messages. Besides, the `objectId` which we used very offen is a Parse built-in standard ID for one object record.
Content List:
Background
Relation between objects
Based on the classical relational database there are three (four) kinds of relationships between objects:
- One-to-One relation: one object is associated with another object;
- One-to-Many (Many-to-One) relation: one object has many related objects;
- Many-to-Many relation: it represents complex relationships among many objects.
Relationships in Parse
Replying these kinds of relation, Parse provides four ways:
- Pointer: which is suitable for one-to-one and one-to-many relationships;
- Array: which is suitable for one-to-many and many-to-many relationships;
- Parse Relation: which is suitable for many-to-many relationship;
- Join Table: which is suitable for many-to-many relation.
Before going deep
Following words will discuss the usage of buiding relationships in Parse based on Public Library, In our web application we build a Publisher-Book-Author design model with two bidirectional associations as following:
In our case, there is no such relationship as One-to-One, and this situation should also be normally rare, so it would not be touched in this article.
Moreover, The official online document about Relations from Parse shows only the situation that how to solve Relations in Parse when developing an application for Android or iOS. There are many system-based functions that not fully suitable for REST API, however it tells us the logical solutions indeed.
One-to-Many Relationship
According to the Publisher-Book-Author design model, the One-to-Many relationship appears between Publisher and Book. One publisher may have no or many published books, one book may have no or only one publisher.
According to the introduction of relationships in Parse, we can use Pointer or Array to build the relation between them.
Using Pointer for One-to-Many Relationship
The starting point for building the relationship using Pointer between publisher and book objects is an association-free design model like following:
publisher
is a property of Book
, it will have no or a single value; publishedBook
is an inverse reference property of Publisher
, it will store no or multi values.
Create/Update Pointer
Suppose we got one Publisher
object and two Book
objects, we want to associate all Book
objects with this publisher:
<code>twoBooks.forEach( function( book) {
$http( {
method: 'PUT',
url: 'https://api.parse.com/1/classes/Book/' + book.objectId,
data: {
publisher: {
__type: 'Pointer',
className: 'Publisher',
objectId: publisher.objectId
}
}
});
});
</code>
The objectId
of this publisher will be saved as a property into each book object. In Pasre Core, the record of one book will be:
The record of this publisher keeps:
Retrieving Objects
If we want to get the information about the publisher of one known book:
<code>$http( {
method: 'GET',
url: 'https://api.parse.com/1/classes/Publisher/' + book.publisher.objectId
});</code>
Parse provides an operation include for returning multiple types of related objects in one query, when the type of this property is Pointer. In our case, the property publisher from Book is a Pointer, we can get all related publisher objects even during getting all books, without send a get request again for getting all corresponded publisher:
<code>
$http( {
method: 'GET',
url: 'http://api.parse.com/1/classes/Book',
params: { include: 'publisher'}
});</code>
If we want to get all published books from this publisher:
<code>$http( {
method: 'GET',
url: 'https://api.parse.com/1/classes/Book',
params: {
where: {
publisher: {
__type: 'Pointer',
className: 'Publisher',
objectId: publisher.objectId
}
}
}
});
</code>
Delete Pointer
Suppose we want to delete all relations:
<code>twoBooks.forEach( function ( book) {
$http( {
method: 'PUT',
url: 'https://api.parse.com/1/classes/Book/' + book.objectId,
data: { publisher: { __op: 'Delete'}}
});
});
</code>
This HTTP request will set the value of publisher Pointer<publisher>
field from ooTkye1JWh
to undefined
, so that this book will have no publisher in the record.
Furthermore, if we didn't delete the association but delete this publisher, the records of these two books won't be changed, which means the publisher's objectId
still exists in the publisher field, but this Pointer will point to none. For security reasons, it would be better delete the Pointer after deleted a publisher.
Using Array for One-to-Many Relationship
The starting point for building the relationship using Array between publisher and book objects is an association-free design model like following:
publisher
is an inverse reference property of Book
, it will have no or a single value; publishedBook
is a property of Publisher
, it will store no or multi values in array of objects.
Compare with Pointer, our focus has been changed from book object to publisher object.
Parse provides Add, AddUnique and Remove operations to process an Array and an method to query on Array values. After made some test I found these operations are only suitable for array of strings not for array of objects, cause these queries can not travell into each single object.
Furthermore, think about if we only save the objectId instead of the whole object as the item of array, this array will be structured as a list of Pointer, which means the way of using Array will obtain all the advantages just likes the way of using Pointer.
Create/Update Array
Suppose we know one publisher which has published two books. If we want to build this relation, we should insert these two books into this publisher's publishedBooks
:
Array of object
<code>$http( {
method: 'PUT',
url: 'https://api.parse.com/1/classes/Publisher/' + publisher.objectId,
data: { publishedBooks: [ bookObject1, bookObject2]}
});
</code>
This publisher record will be changed to:
Array of objectId
<code>$http( {
method: 'PUT',
url: 'https://api.parse.com/1/classes/Publisher/' + publisher.objectId,
data: {
publishedBooks: {
__op: 'AddUnique',
objects: [ bookObject1.objectId, bookObject2.objectId]
}
}
});
</code>
Retrieving Objects
Array of object
If we have got a publisher and want to get all infromation about this publisher including its published books, there will be nothing that need to do, cause this publisher record had these information about its published books already.
If we want to get the information about the publisher of one known book, we must first load all records of publisher, then manually find corresponding book from the array of publishedBooks:
<code>$http( {
method: 'GET',
url: 'http://api.parse.com/1/classes/Publisher'
})
.success( function ( data) {
var publishers = data.results;
publishers.forEach( function ( p) {
if ( p.publishedBooks.length > 0) {
p.publishedBooks.forEach( function ( pb) {
if ( pb.objectId == book.objectId) {
console.log("Found the publisher of this book finally. Could you try to use Pointer?");
}
});
}
});
});
</code>
Array of objectId
If we have got a publisher and want to get all infromation about this publisher including its published books:
<code>publisher.publishedBooks.forEach( function ( pb) {
$http( {
method: 'GET',
url: 'https://api.parse.com/1/classes/Book/' + pb.objectId
});
});
</code>
If we want to get the information about the publisher of one known book, we can simply send a request contained where
, let Parse to work for us (Servers are always more powerful than my personal computer in my opinion):
<code>$http( {
method: 'GET',
url: 'https://api.parse.com/1/classes/Publisher',
params: { where: { publishedBooks: book.objectId}}
});
</code>
Delete Array
Suppose we want to delete all relations:
<code>$http( {
method: 'PUT',
url: 'https://api.parse.com/1/classes/Publisher/' + publisher.objectId,
data: { publishedBooks: { __op: 'Delete'}}
});
</code>
Many-to-Many Relationship
According to the Publisher-Book-Author design model, the Many-to-Many relationship appears between Book and Author. One book may have no or many authors books, one author may authored no or many books.
According to the introduction of relationships in Parse, we can use Parse Relation, Join Table or Array to build the relation between them.
Using Parse Relation for Many-to-Many Relationship
The starting point for building the relationship using Parse Relation between book and author objects is an association-free design model like following:
authors
in type of Parse Relation is a property of Book
, it will keep all the related authors; authoredBooks
in type of Parse Relation is a property of Author
, it will store all the related books.
Before using the method of Parse Relation we discuss a little about what the Parse Relation really is.
If we create some books and authors as the test data in Parse, the book records with Parse Relations will be:
The author records with Parse Relations will be:
After we click one View Relations
in book records, we will see that a kind of list showing on the left side, which keeps all information of the related authors:
So Parse Relations are very simily like an array of Pointer combined with its corresponding object.
Parse offers operations AddRelation and RemoveRelation for Parse Relation used to update objects. There are also some query operations, Parse has named them Relational Queries but these are only suitable for One-to-Many Relationships.
Create/Update Parse Relation
Suppose we got a book and want to relate to two authors:
<code>
$http( {
method: 'PUT',
url: 'https://api.parse.com/1/classes/Book/' + book.objectId,
data: {
authors: {
__op: 'AddRelation',
objects: [{
__type: 'Pointer',
className: 'Author',
objectId: author1.objectId
},
{
__type: 'Pointer',
className: 'Author',
objectId: author2.objectId
}]
}
}
})
</code>
If we got an author and want to related to two books:
<code>
$http( {
method: 'PUT',
url: 'https://api.parse.com/1/classes/Author/' + author.objectId,
data: {
authoredBooks: {
__op: 'AddRelation',
objects: [{
__type: 'Pointer',
className: 'Book',
objectId: book1.objectId
},
{
__type: 'Pointer',
className: 'Book',
objectId: Book2.objectId
}]
}
}
})
</code>
Notice that if we update/delete the basic information about an author (name, birthDate, deathDate) or a book (title, year, publisher) record, do not need to change the part of Parse Relations.
Retrieving Objects
Suppose we got a book want to find all authors of this book. There are two ways that can be used:
-
check the the authoredBooks field of author objects:
<code> $http({
method: 'GET',
url: 'https://api.parse.com/1/classes/Author',
params: {
where: {
authoredBooks: {
__type: 'Pointer',
className: 'Book',
objectId: book.objectId
}
}
}
})
</code>
-
check the Parse Relation using Parse built-in operation $relatedTo
:
<code> $http({
method: 'GET',
url: 'https://api.parse.com/1/classes/Author',
params: {
where: {
$relatedTo: {
object: {
__type: 'Pointer',
className: 'Book',
objectId: book.objectId
},
key: 'authors'
}
}
}
})
</code>
If we had an author and want to find his/her books, there are same two ways that can be used:
-
check the the authors field of book objects:
<code> $http({
method: 'GET',
url: 'https://api.parse.com/1/classes/Book',
params: {
where: {
authors: {
__type: 'Pointer',
className: 'Author',
objectId: author.objectId
}
}
}
})
</code>
-
check the Parse Relation using Parse built-in operation $relatedTo
:
<code> $http({
method: 'GET',
url: 'https://api.parse.com/1/classes/Book',
params: {
where: {
$relatedTo: {
object: {
__type: 'Pointer',
className: 'Author',
objectId: author.objectId
},
key: 'authoredBooks'
}
}
}
})
</code>
Delete Parse Relation
Parse didn't allow user directly to remove the field in type of Relation
, we can only use its RemoveRelation
operation and a loop of request to delete all related records.
Using Array for Many-to-Many Relationship
Compare with Using Array for One-to-Many Relationship, the idea of this method is mostly like building two One-to-Many Relationships:
- When we use array of object, we should build an array of author object then save it to
authors
in a book record; build an array of book object then save it into authoredBooks
in an author record. - When we use array of objectId, we should build an array of author objectId then save it to
authors
in a book record; build an array of book objectId then save it to authoredBooks
in an author record.
Using Join Table for Many-to-Many Relationship
Join Table is the idea from classical database. When there is a Many-to-Many relation, we combine every objectId
from both sides together to build a new separate table in which the relationship is tracked:
(bookObjectId, authorObjectId)
will be combined together as one key for a single record.
Create Join Table
Suppose we have two books and two authors, each author had authored one book:
<code>
$http( {
method: 'POST',
url: 'https://api.parse.com/1/classes/joinBookAuthor',
data: {
bookObjectId: book1.objectId,
authorObjectId: author1.objectId
}
});
$http( {
method: 'POST',
url: 'https://api.parse.com/1/classes/joinBookAuthor',
data: {
bookObjectId: book2.objectId,
authorObjectId: author2.objectId
}
});
</code>
Update Join Table
Suppose author1 had also authored book2:
<code>$http( {
method: 'POST',
url: 'https://api.parse.com/1/classes/joinBookAuthor',
data: {
bookObjectId: book2.objectId,
authorObjectId: author1.objectId
}
});
</code>
Then, suppose we want to change that author1 had authored no book:
<code>$http( {
method: 'GET',
url: 'https://api.parse.com/1/classes/joinBookAuthor',
params: { where: { authorObjectId: author1.objectId}}
})
.success( function( data) {
var joinBookAuthorRecords = data.results;
joinBookAuthorRecords.forEach( function ( jba) {
$http( {
method: 'DELETE',
url: 'https://api.parse.com/1/classes/joinBookAuthor/' + jba.objectId
});
});
});
</code>
Retrieving Objects
Suppose we got book1 and want to find its all authors:
<code>$http( {
method: 'GET',
url: 'https://api.parse.com/1/classes/joinBookAuthor',
params: { where: { bookObjectId: book1.objectId}}
})
.success( function( data) {
var joinBookAuthorRecords = data.results;
joinBookAuthorRecords.forEach( function ( jba) {
$http( {
method: 'GET',
url: 'https://api.parse.com/1/classes/Author/' + jba.authorObjectId
});
});
});
</code>
Delete Join Table
The Join Table in Parse was saved as a class, Parse did not offer a function for user to delete a class.
Before ending
Based on the Parse plattform using REST API, Pointer for One-to-Many Relation and Parse Relation for Many-to-Many Relation are good choices.
We had discussed the way of using Array in Parse to set up the Publisher-Book and Book-Author relationships. Array can be used flexible, but these relations exist only logically in our application not in the database.
Join Table is the direct way for Many-to-Many Relation, but in our case, it will need to send more requests than other methods.