学习

使用 JSON 管理文档数据

Simon Prickett
作者
Simon Prickett, Redis 首席开发者布道师

我们使用 Redis 内置的 Hash 数据类型来表示用户和位置实体。Hash 非常适合这项任务,但它们的局限性在于只能包含扁平的键/值对。对于我们的位置,我们希望以更结构化的方式存储更多详细信息。

以下是我们想要存储的位置附加数据示例

{
  "id": 121,
  "hours": [
    { "day": "Monday", "hours": "6-7" },
    { "day": "Tuesday", "hours": "6-7" },
    { "day": "Wednesday", "hours": "7-8" },
    { "day": "Thursday", "hours": "6-9" },
    { "day": "Friday", "hours": "8-5" },
    { "day": "Saturday", "hours": "9-6" },
    { "day": "Sunday", "hours": "6-4" }
  ],
  "socials": [
    {
      "instagram": "theginclub",
      "facebook": "theginclub",
      "twitter": "theginclub"
    }
  ],
  "website": "www.theginclub.com",
  "description": "Lorem ipsum...",
  "phone": "(318) 251-0608"
}

我们可以将这些数据作为序列化的 JSON 存储在 Redis String 中,但这样一来,我们的应用每次想要读取部分数据时,都必须检索和解析整个文档。更新时也必须这样做。此外,使用这种方法,更新操作不是原子的,当我们正在应用代码中修改某个键上存储的 JSON 时,第二个客户端可能会更新它。然后,当我们把我们修改过的 JSON 版本序列化回 Redis String 时,其他客户端的修改就会丢失。

Redis Stack 为 Redis 添加了一个新的 JSON 数据类型,以及用于在 Redis 服务器上原子地选择和更新 JSON 文档中单个元素的查询语法。这使得我们的应用代码更简单、更高效且更可靠。

编码练习#

在本练习中,您将完成一个 API 路由的代码,该路由获取表示给定位置某一天营业时间的特定对象。打开文件 src/routes/location_routes.js,找到 /location/:locationId/hours/:day 的路由。起始代码如下所示

// EXERCISE: Get opening hours for a given day.
router.get(
  '/location/:locationId/hours/:day',
  [
    param('locationId').isInt({ min: 1 }),
    param('day').isInt({ min: 0, max: 6 }),
    apiErrorReporter,
  ],
  async (req, res) => {
    /* eslint-disable no-unused-vars */
    const { locationId, day } = req.params;
    /* eslint-enable */
    const locationDetailsKey = redis.getKeyName('locationdetails', locationId);

    // TODO: Get the opening hours for a given day from
    // the JSON stored at the key held in locationDetailsKey.
    // You will need to provide the correct JSON path to the hours
    // array and return the element held in the position specified by
    // the day variable.  Make sure Redis JSON returns only the day
    // requested!
    const jsonPath = 'TODO';

    /* eslint-enable no-unused-vars */
    const hoursForDay = JSON.parse(
      await redisClient.call('JSON.GET', locationDetailsKey, jsonPath),
    );
    /* eslint-disable */

    // If null response, return empty object.
    res.status(200).json(hoursForDay || {});
  },
);

您需要更新代码以提供正确的 JSON 路径,用 JSON 路径表达式替换“TODO”值。

查看存储在键 ncc:locationdetails:121 的 JSON,我们看到营业时间存储在一个名为 hours 的字段中的对象数组中,其中第 0 天是星期一,第 6 天是星期日

因此,您需要一个 JSON 路径查询,根据变量 day 中存储的值,从 hours 数组中获取正确的元素。

如果您使用 redis-cli,可以使用以下命令查看 JSON 文档的结构

json.get ncc:locationdetails:121 .

确保您的查询只返回请求的那一天,这样您就不必编写 Node.js 代码来过滤从 Redis 返回的值。使用 JSON 路径语法页面 帮助您构建正确的查询。

要测试您的代码,请使用以下命令启动服务器

$ npm run dev

请记住,这将允许您在不重启服务器的情况下编辑代码并尝试您的更改。

如果您的代码中有正确的 JSON 路径,访问 https://:80801/api/location/121/hours/2 应该返回

{
  "day": "Wednesday",
  "hours": "7-8"
}

如果您有任何问题或需要帮助,请记住我们在 Discord 上随时为您服务。

外部资源#

了解更多关于 JSON 的信息,请访问 https://redis.ac.cn/docs/stack/json/

在这个视频中,Justin 使用一个有趣的墨西哥餐车例子介绍了 JSON!