65.9K
CodeProject 正在变化。 阅读更多。
Home

Parse.com 中的关系使用 REST API

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2015年5月19日

CPOL

9分钟阅读

viewsIcon

29514

使用 Angular `$http` 和 Parse REST API 处理关系

引言

Parse 是一个云服务平台,提供后端存储服务。它提供了多种应用程序编程接口(API),例如 .NET、JavaScript 和 PHP。

在我们的 Web 应用程序中,我们将使用它的 REST API。这使我们能够从任何能够发送 HTTP 请求的编程语言编写的应用程序与 Parse 上的数据进行交互。请求头应包含 `X-Parse-Application-Id` 和 `X-Parse-REST-API-Key`,但为了简洁起见,以下代码将省略这些消息。此外,我们经常使用的 `objectId` 是 Parse 内置的标准 ID,用于标识单个对象记录。

内容列表:

背景

对象之间的关系

基于经典的数据库关系模型,对象之间存在三种(或四种)关系:

  • 一对一关系:一个对象与另一个对象关联;
  • 一对多(多对一)关系:一个对象拥有多个相关对象;
  • 多对多关系:它表示多个对象之间复杂的关联。

Parse 中的关系

为了应对这些关系,Parse 提供了四种方式:

  • Pointer:适用于一对一和一对多关系;
  • Array:适用于一对多和多对多关系;
  • Parse Relation:适用于多对多关系;
  • Join Table:适用于多对多关系。

深入了解之前

接下来的内容将基于“公共图书馆”项目,讨论在 Parse 中构建关系的使用。在我们的 Web 应用程序中,我们构建了一个具有以下两个双向关联的“出版商-图书-作者”设计模型。

在我们的例子中,不存在“一对一”关系,这种情况通常也很少见,因此本文将不作讨论。

此外,Parse 官方在线文档关于 关系 的内容仅展示了在开发 Android 或 iOS 应用程序时如何解决 Parse 中的关系。其中存在许多不完全适用于 REST API 的系统级函数,但它们确实提供了逻辑解决方案。

一对多关系

根据“出版商-图书-作者”设计模型,“出版商”和“图书”之间出现一对多关系。一个出版商可能没有或出版了许多图书,一本图书可能没有或只有一个出版商。

根据 Parse 中关系的介绍,我们可以使用 **Pointer** 或 **Array** 来建立它们之间的关系。

使用 Pointer 实现一对多关系

使用 Pointer 在出版商和图书对象之间建立关系,其起点是一个不包含关联的设计模型,如下所示:

  • `publisher` 是 `Book` 的一个属性,它将没有值或只有一个值;
  • `publishedBook` 是 `Publisher` 的一个反向引用属性,它将不存储值或存储多个值。

创建/更新 Pointer

假设我们有一个 `Publisher` 对象和两个 `Book` 对象,我们想将所有 `Book` 对象与此出版商关联。

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
      }
    }
  });
});

`objectId` 将作为属性保存在每个图书对象中。在 Parse Core 中,一本图书的记录将是:

此出版商的记录将保留:

检索对象

如果我们想获取一本已知图书的出版商信息:

$http( {
  method: 'GET',
  url: 'https://api.parse.com/1/classes/Publisher/' + book.publisher.objectId
});

当属性类型为 Pointer 时,Parse 提供了一个 `include` 操作,用于在一次查询中返回多种类型的相关对象。在我们的例子中,`Book` 的 `publisher` 属性是 Pointer,即使在获取所有图书的同时,我们也可以获取所有相关的出版商对象,而无需再次发送 GET 请求来获取所有对应的出版商。

// Get all books
$http( {
  method: 'GET',
  url: 'http://api.parse.com/1/classes/Book',
  params: { include: 'publisher'}
});

如果我们想获取此出版商出版的所有图书:

$http( {
  method: 'GET',
  url: 'https://api.parse.com/1/classes/Book',
  params: {
    where: {
      publisher: {
        __type: 'Pointer',
        className: 'Publisher',
        objectId: publisher.objectId
      }
    }
  }
});

删除 Pointer

假设我们要删除所有关系:

twoBooks.forEach( function ( book) {
  $http( {
    method: 'PUT',
    url: 'https://api.parse.com/1/classes/Book/' + book.objectId,
    data: { publisher: { __op: 'Delete'}}
  });
});

此 HTTP 请求会将 `ooTkye1JWh` 的 `publisher Pointer<publisher>` 字段值设置为 `undefined`,从而使该图书在记录中没有出版商。

此外,如果我们没有删除关联而是删除了出版商,这两本书的记录不会改变,这意味着出版商的 `objectId` 仍然存在于 `publisher` 字段中,但此 Pointer 将指向空。出于安全原因,最好在删除出版商后删除 Pointer。

使用 Array 实现一对多关系

使用 Array 在出版商和图书对象之间建立关系,其起点是一个不包含关联的设计模型,如下所示:

  • `publisher` 是 `Book` 的一个反向引用属性,它将没有值或只有一个值;
  • `publishedBook` 是 `Publisher` 的一个属性,它将以对象数组的形式存储零个或多个值。

与 Pointer 相比,我们的重点已从图书对象转移到出版商对象。

Parse 提供了 Add、AddUnique 和 Remove 操作来处理 Array 以及一个 对 Array 值进行查询 的方法。经过一些测试,我发现这些操作仅适用于字符串数组,而不适用于对象数组,因为这些查询无法深入到每个单独的对象。

此外,如果我们在数组中只存储 `objectId` 而不是整个对象,那么这个数组将构成一个 Pointer 列表,这意味着使用 Array 的方式将获得与使用 Pointer 相同的所有优点。

创建/更新 Array

假设我们知道一个出版商出版了两本书。如果我们想建立这种关系,应该将这两本书添加到此出版商的 `publishedBooks` 中。

对象数组

$http( {
  method: 'PUT',
  url: 'https://api.parse.com/1/classes/Publisher/' + publisher.objectId,
  data: { publishedBooks: [ bookObject1, bookObject2]}
});

此出版商的记录将更改为:

对象 ID 数组

$http( {
  method: 'PUT',
  url: 'https://api.parse.com/1/classes/Publisher/' + publisher.objectId,
  data: {
    publishedBooks: {
      __op: 'AddUnique',  // can be also 'Add' or 'Remove'
      objects: [ bookObject1.objectId, bookObject2.objectId]
    }
  }
});

检索对象

对象数组

如果我们有一个出版商并想获取关于该出版商的所有信息,包括其出版的书籍,那么将无需执行任何操作,因为该出版商的记录已经包含了其出版书籍的相关信息。

如果我们想获取一本已知图书的出版商信息,我们必须先加载所有出版商记录,然后手动从 `publishedBooks` 数组中查找对应的图书。

$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?");
          }
        });
      }
    });
  });

对象 ID 数组

如果我们有一个出版商并想获取关于该出版商的所有信息,包括其出版的书籍:

publisher.publishedBooks.forEach( function ( pb) {
  $http( {
    method: 'GET',
    url: 'https://api.parse.com/1/classes/Book/' + pb.objectId
  });
});

如果我们想获取一本已知图书的出版商信息,我们可以简单地发送一个包含 `where` 的请求,让 Parse 为我们处理(在我看来,服务器总是比我的个人电脑更强大)。

$http( {
  method: 'GET',
  url: 'https://api.parse.com/1/classes/Publisher',
  params: { where: { publishedBooks: book.objectId}}
});

删除 Array

假设我们要删除所有关系:

$http( {
  method: 'PUT',
  url: 'https://api.parse.com/1/classes/Publisher/' + publisher.objectId,
  data: { publishedBooks: { __op: 'Delete'}}
});

多对多关系

根据“出版商-图书-作者”设计模型,“图书”和“作者”之间出现多对多关系。一本书可能没有或有多位作者,一位作者可能写过零本或多本书。

根据 Parse 中关系的介绍,我们可以使用 **Parse Relation**、**Join Table** 或 **Array** 来建立它们之间的关系。

使用 Parse Relation 实现多对多关系

使用 Parse Relation 在图书和作者对象之间建立关系,其起点是一个不包含关联的设计模型,如下所示:

  • `authors`(类型为 Parse Relation)是 `Book` 的一个属性,它将保存所有相关的作者;
  • `authoredBooks`(类型为 Parse Relation)是 `Author` 的一个属性,它将存储所有相关的图书。

在使用 Parse Relation 方法之前,我们先简单讨论一下 Parse Relation 到底是什么。

如果我们创建一些图书和作者作为 Parse 中的测试数据,带有 Parse Relations 的图书记录将是:

带有 Parse Relations 的作者记录将是:

在图书记录中点击“View Relations”后,左侧会显示一个列表,其中包含所有相关作者的信息。

因此,Parse Relations 非常类似于 Pointer 数组与对应对象的组合。

Parse 提供了 AddRelation 和 RemoveRelation 操作来处理 Parse Relation,用于更新对象。还有一些查询操作,Parse 将它们命名为关系查询,但这些只适用于一对多关系。

创建/更新 Parse Relation

假设我们有一本书,想将其关联到两位作者。

// add relation from book to authors
$http( {
  method: 'PUT',
  url: 'https://api.parse.com/1/classes/Book/' + book.objectId,
  data: {
    authors: {
      __op: 'AddRelation',  // or 'RemoveRelation'
      objects: [{
        __type: 'Pointer',
        className: 'Author',
        objectId: author1.objectId
      },
      {
        __type: 'Pointer',
        className: 'Author',
        objectId: author2.objectId
      }]
    }
  }
})

如果我们有一个作者,想将其关联到两本书。

// add relation from author to books
$http( {
  method: 'PUT',
  url: 'https://api.parse.com/1/classes/Author/' + author.objectId,
  data: {
    authoredBooks: {
      __op: 'AddRelation',  // or 'RemoveRelation'
      objects: [{
        __type: 'Pointer',
        className: 'Book',
        objectId: book1.objectId
      },
      {
        __type: 'Pointer',
        className: 'Book',
        objectId: Book2.objectId
      }]
    }
  }
})

请注意,如果我们更新/删除作者(姓名、出生日期、去世日期)或图书(标题、年份、出版商)记录的基本信息,则无需更改 Parse Relations 部分。

检索对象

假设我们有一本书,想查找这本书的所有作者。有两种方法可以实现:

  1. 检查作者对象的 `authoredBooks` 字段。

     $http({
       method: 'GET',
       url: 'https://api.parse.com/1/classes/Author',
       params: {
         where: {
           authoredBooks: {
             __type: 'Pointer',
             className: 'Book',
             objectId: book.objectId
           }
         }
       }
     })
    
  2. 使用 Parse 内置的 `$relatedTo` 操作检查 Parse Relation。

     $http({
       method: 'GET',
       url: 'https://api.parse.com/1/classes/Author',
       params: {
         where: {
           $relatedTo: {
             object: {
               __type: 'Pointer',
               className: 'Book',
               objectId: book.objectId
             },
             key: 'authors'
           }
         }
       }
     })
    

如果我们有一个作者,想查找他/她的书籍,也有相同的两种方法可以实现:

  1. 检查图书对象的 `authors` 字段。

     $http({
       method: 'GET',
       url: 'https://api.parse.com/1/classes/Book',
       params: {
         where: {
           authors: {
             __type: 'Pointer',
             className: 'Author',
             objectId: author.objectId
           }
         }
       }
     })
    
  2. 使用 Parse 内置的 `$relatedTo` 操作检查 Parse Relation。

     $http({
       method: 'GET',
       url: 'https://api.parse.com/1/classes/Book',
       params: {
         where: {
           $relatedTo: {
             object: {
               __type: 'Pointer',
               className: 'Author',
               objectId: author.objectId
             },
             key: 'authoredBooks'
           }
         }
       }
     })
    

删除 Parse Relation

Parse 不允许用户直接删除 `Relation` 类型的字段,我们只能使用其 `RemoveRelation` 操作和请求循环来删除所有关联的记录。

使用 Array 实现多对多关系

使用 Array 实现一对多关系相比,此方法的基本思想更像是构建两个一对多关系。

  • 当我们使用对象数组时,应该构建一个作者对象数组,然后将其保存到图书记录的 `authors` 中;构建一个图书对象数组,然后将其保存到作者记录的 `authoredBooks` 中。
  • 当我们使用对象 ID 数组时,应该构建一个作者 ID 数组,然后将其保存到图书记录的 `authors` 中;构建一个图书 ID 数组,然后将其保存到作者记录的 `authoredBooks` 中。

使用 Join Table 实现多对多关系

Join Table 是源自经典数据库的思想。当存在多对多关系时,我们将两边的 `objectId` 组合起来,构建一个新的单独的表来跟踪关系。

`(bookObjectId, authorObjectId)` 将组合成一个键,用于单个记录。

创建 Join Table

假设我们有两个图书和两个作者,每个作者写了一本书。

// author1 had authored book1
$http( {
  method: 'POST',
  url: 'https://api.parse.com/1/classes/joinBookAuthor',
  data: {
    bookObjectId: book1.objectId,
    authorObjectId: author1.objectId
  }
});
// author2 had authored book2
$http( {
  method: 'POST',
  url: 'https://api.parse.com/1/classes/joinBookAuthor',
  data: {
    bookObjectId: book2.objectId,
    authorObjectId: author2.objectId
  }
});

更新 Join Table

假设 author1 还写了 book2。

$http( {
  method: 'POST',
  url: 'https://api.parse.com/1/classes/joinBookAuthor',
  data: {
    bookObjectId: book2.objectId,
    authorObjectId: author1.objectId
  }
});

然后,假设我们想改变 author1 不再写任何书。

$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
      });
    });
  });

检索对象

假设我们得到了 book1,想查找它所有的作者。

$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
      });
    });
  });

删除 Join Table

Parse 中的Join Table 被保存为一个类,Parse 不提供删除类的函数。

结束之前

基于使用 REST API 的 Parse 平台,**Pointer** 用于一对多关系,**Parse Relation** 用于多对多关系,是较好的选择。

我们已经讨论了在 Parse 中使用 Array 来设置 Publisher-Book 和 Book-Author 关系的方法。Array 可以灵活使用,但这些关系仅在我们的应用程序中逻辑存在,而不在数据库中。

Join Table 是多对多关系的直接方式,但在我们的例子中,它需要比其他方法发送更多的请求。

© . All rights reserved.