8. 向实体贡献字段
15m

概述

虽然我们已经定义了Listing 在我们的 reviews 中,并贡献了 到它,但我们还没有告诉它如何解析这些字段。

在本课中,我们将

  • 创建参考
  • 填充 reviewsoverallRating 中的数据
  • 了解 @DgsEntityFetcher 注释

参考解析器

当我们 特定的列表及其评论数据时,我们需要一种方法来告诉 reviews 哪个 列表是我们要谈论的。然后 reviews 就会理解要提供哪些数据!

为了做到这一点, 会传递我们在上一课中讨论的 表示: reviews 将其用于了解要获取哪些列表数据的最小信息。

示例列表实体表示
{
"__typename": "Listing",
"id": "listing-3"
}

但是,在 reviews 中,这个 表示实际上 哪里了呢?

这正是我们缺少的部分:参考 。参考解析器是一个特殊的方法,它可以接收 表示,从表示数据中创建一个新的 Listing 实例,并返回它。

让我们看看它在代码中是什么样子的,并逐步进行这个过程。

添加 resolveListingReference

让我们在 reviews 中,在 com.example.reviews/datafetchers/ReviewsDataFetcher 下打开我们的数据获取器类。

首先,让我们在文件开头添加一些导入。我们很快就会用到它们。

import java.util.Map;
import com.example.reviews.generated.types.Listing;

注意: 在你的 generated 文件夹中没有看到 Listing 类型吗?尝试重启服务器!

在类的主体中为我们的新方法留出一些空间。为了更清楚地说明它的作用,我们将它命名为 resolveListingReference。(但你可以根据自己的需要给这个方法命名!)

datafetchers/ReviewsDataFetcher
public void resolveListingReference() {
// TODO
}

这个方法将接收 表示,它看起来类似于以下代码片段:只有 __typename 和我们设置为 主键的 id

{__typename=Listing, id=listing-1}

让我们给我们的方法一个参数来保存这个表示。它被认为是 Map<String, Object> 类型,我们将它称为 entityRepresentation。让我们打印出 entityRepresentation.toString() 这样我们就能看到这个方法接收了什么。

public void resolveListingReference(Map<String, Object> entityRepresentation) {
System.out.println(entityRepresentation.toString());
}

@DgsEntityFetcher 注释

为了让我们的 DGS 服务器知道 这个 方法是应该接收 的方法,我们需要用一个特定的注释标记它: @DgsEntityFetcher

这个注释有一个名为 name 的属性,我们将用它来定义它解析的 的名称: Listing

让我们将这个注释应用到我们的方法。

@DgsEntityFetcher(name = "Listing")
public void resolveListingReference(Map<String, Object> entityRepresentation) {
System.out.println(entityRepresentation.toString());
}

现在很清楚,当 将一个 Listing 表示传递到我们的 reviews 中时, 这个 方法是我们将用来解析 哪个 列表需要提供数据的!

返回你的终端,重启 reviews 服务器。我们的 rover dev 进程应该还在端口 4000 上运行,所以让我们回到那里,再次尝试我们的

query GetListingAndReviews {
listing(id: "listing-1") {
title
description
numOfBeds
amenities {
name
category
}
overallRating
reviews {
id
text
}
}
}

当我们运行 时,我们仍然不会得到任何与评论相关的数据;但是当我们检查 reviews 的终端时,我们会看到我们的 表示已经到达并被打印出来了!

{__typename=Listing, id=listing-1}

我们的 reviews 正从 成功接收列表表示。现在,我们只需要确保我们的方法返回一个新的 Listing 实例,以便其他数据获取器方法可以访问它并附加数据。

删除打印语句,并用实例化一个新的 Listing 实例替换它。我们还可以将方法的返回值类型更新为 Listing

@DgsEntityFetcher(name = "Listing")
public Listing resolveListingReference(Map<String, Object> entityRepresentation) {
Listing listing = new Listing();
}

接下来,让我们从我们的 "id" 值中提取 entityRepresentation。我们可以使用以下行将其转换为 String 类型。

String id = (String) entityRepresentation.get("id");

最后,我们将它设置为 id 在我们的 listing 上,并返回它。

listing.setId(id);
return listing;

很好!我们可以认为我们对列表的引用已经 "解析" 了。我们准备提供那些与评论相关的 - reviewsoverallRating - 使用它们自己的数据获取器方法。

添加 Listing.reviews

让我们先处理 reviews 方法。

ReviewsDataFetcher 类中,我们定义一个名为 reviews 的新方法。这是一个常规的数据提取器,所以我们将使用 @DgsData 注解,并将我们的 parentType 设置为 Listing

@DgsData(parentType="Listing")
public void reviews() {}

在我们的 中,Listing.reviews 返回 [Review!]! 类型。因此,我们将使用相应的 Java 返回类型 Flux<ReviewDto> 更新我们的方法。

public Flux<ReviewDto> reviews() {}

此方法应使用 id 表示中查找并返回数据库中所有相关的评论。因为此数据提取器解析了实体类型上的 ,所以我们知道链中的上一个数据提取器是我们刚刚定义的 resolveListingReference 方法。这意味着我们可以使用 DgsDataFetchingEnvironment 参数访问其返回值(Listing 实例)。

让我们将其作为名为 dfe 的参数添加到我们的方法中。

public Flux<ReviewDto> reviews(DgsDataFetchingEnvironment dfe) {}

我们可以在 dfe 上调用 getSource 方法来访问 Listing 实例;然后调用列表的 getId 方法来访问其 id 值。

public Flux<ReviewDto> reviews(DgsDataFetchingEnvironment dfe) {
Listing listing = dfe.getSource();
String id = listing.getId();
}

有了列表 id,我们就可以在数据库中查找与该列表关联的所有评论。我们使用的是已经实例化在类中的 ReviewController,以访问我们内存中的 。它为 reviewsForListing 提供映射,因此让我们在此处调用该方法并将我们的 id 传递给它。

return this.reviewController.reviewsForListing(id);

The overallRating method

现在进入 overallRating 数据提取器方法!让我们使用 @DgsData 注解设置初始结构。

@DgsData(parentType="Listing")
public void overallRating() {
// TODO
}

自己尝试一下,并查看 ReviewController 类以查找可用于检索此数据的有用方法。(提示:查看其返回类型,以获取有关我们的方法应返回内容的有用提示!)

准备就绪后,将您的代码与我们完成后的方法进行比较。

@DgsData(parentType="Listing")
public Mono<Float> overallRating(DgsDataFetchingEnvironment dfe) {
Listing listing = dfe.getSource();
String id = listing.getId();
return this.reviewController.averageRatingForListing(id);
}

注意: 因为我们在响应式风格中编码,所以我们到目前为止一直使用 Flux 类型来描述我们返回的数据流。但是,由于我们只返回一个值——列表评级的平均值(以 Float 返回),我们可以改用 Mono 类型,并将类型 传递给它 Float

运行我们的理想查询

是时候重新编译了!

任务!

rover dev 进程仍在运行的情况下,让我们在 https://127.0.0.1:4000 上尝试我们的

query GetListingAndReviews {
listing(id: "listing-1") {
title
description
numOfBeds
amenities {
name
category
}
overallRating
reviews {
id
text
}
}
}

提交查询,然后……我们有了数据!🎉 我们已将评论与列表关联起来,并使我们的理想查询成为现实!

查看查询计划

让我们看看 来了解这些数据是如何组合在一起的。我们会看到,首先, 将从 listings 中获取数据,然后使用这些数据构建它对 reviews 的请求。最后一步是将来自两个 的响应扁平化为单个 Listing 类型,用于我们查询的 listing

https://127.0.0.1:4000

The operation in Sandbox, with the Query Plan Preview opened, showing a linear path to retrieve data from both subgraphs

仍然看到 reviews: null?尝试重启你的 DGS 服务器!运行我们 rover dev 进程将在端口 4000 上自动刷新。

练习

实体的参考解析器方法应该在哪里定义?

关键要点

  • 任何为 贡献 都需要为该实体定义一个参考 方法。每当 需要在其他子图中访问实体的字段时,就会调用此方法。
  • 在 DGS 中,我们使用 @DgsEntityFetcher 注释将方法标记为参考 ,并传入它所解析的类型的 name。此方法从 中接收 表示。
  • 一个 表示是 用来表示实体的特定实例的对象。它包括实体的类型及其关键

接下来

太棒了!我们的 listingsreviews 服务现在正在合作同一个 Listing 。每个 都贡献自己的 ,而 的本地 rover dev 进程为我们打包了响应。重点是“本地”这个词;要使我们的更改真正“生效”(至少在 教程 的意义上),我们需要告诉 关于它们!

在下一课中,我们将探讨如何使用安全可靠地实施这些更改。

上一页

分享您关于本课的疑问和评论

本课程目前处于

测试版
.您的反馈帮助我们改进!如果您遇到困难或困惑,请告知我们,我们会帮助您解决问题。所有评论都是公开的,必须遵守 Apollo 行为准则。请注意,已解决或已处理的评论可能会被删除。

您需要一个 GitHub 帐户才能在下方发帖。没有帐户吗? 请改为在 Odyssey 论坛中发帖。