【问题标题】:Adding pagination cursor and additional features to endpoint result将分页光标和附加功能添加到端点结果
【发布时间】:2015-12-25 13:54:35
【问题描述】:

Strongloop api 开箱即用,非常棒。但是,我目前正在尝试进行一些自定义。我正在尝试向category 模型添加一个远程方法,该方法与端点数据一起返回以下内容:perPagetotalpaging(详细信息如下所示)。对于pagingbeforeafter 是基于gameId 的参数。如果值为null 或大于5limit 将默认为5。附加到前面提到的结果的最佳方法是什么?

例如,http://localhost:3000/api/Categories/1004/games/mature?before=1000053 将返回所有小于1000053gameId 和所有before 大于1000053gameIds

common/models/category.js

  Category.findById(id, {}, function(err, category){
        if (err) return callback(err);
        //set limit
        if (limit && limit > 5){
          limit = 5;
        }else if(limit === undefined){
          limit = 5;
        }
        //set after cursor
        Games.find({
            "where": {
                categoryId: id,
                mature: true,
                gameId: {gt: after}
            },
            "limit": limit
        }, function(err, gameArr) {
            if (err) return callback(err);
            callback(null, gameArr);
        });
    });

/Categories/1004/games/mature 的端点结果

{
      “perPage”: 5, //value from limit
      “total”: 5, //total of return items from array
      "data": [
         ... Endpoint data is here
      ],
      "paging": {
        "cursors": {
          "after": 1000057, //last item in array
          "before": 1000053 //first item in array
        },
        "previous": "http://localhost:3000/api/Categories/1004/games/mature?before=1000053" //url for previous
        "next": "http://localhost:3000/api/Categories/1004/games/mature?after=1000057" //url for after
      }
  }

【问题讨论】:

    标签: node.js loopbackjs strongloop


    【解决方案1】:

    要将结果添加到响应中,首先更改 Category.mature 远程方法返回给对象的响应类型:

    Category.remoteMethod('mature', {
      accepts: [
        {arg: 'id', type: 'number', required: true},
        {arg: 'limit',type: 'number',required: false},
        {arg: 'after',type: 'number',required: false},
        {arg: 'before',type: 'number',required: false}   
      ],
      // mixing ':id' into the rest url allows $owner to be determined and used for access control
      http: {
        path: '/:id/games/mature',
        verb: 'get'
      },
      returns: {
        arg: 'games',
        type: 'object' // will be whatever you pass back as 2nd arg to callback()
      }
    });
    

    然后只需将您想要的值添加到新的游戏对象中,使用您现有的 gamesArr 作为 data 的值,并将为限制、之前和之后传入的值作为第二个参数传入响应对象到成功回调:

    callback(null, {
      "perPage": limit,
      "total": gameArray.length,
      "data": gameArray,
      "paging": {
        "cursors": {
          "after": gameArray[gameArray.length-1].game_id, // last game_id in result
          "before": gameArray[0].game_id // first game_id in result
        },
        "previous": "http://localhost:3000/api/Categories/1004/games/mature?before=" + gameArray[0].game_id,
        "next": "http://localhost:3000/api/Categories/1004/games/mature?after=" + gameArray[gameArray.length-1].game_id
      }
    })
    

    正如我提到的使代码过于复杂的多个 Games.find() 调用,您也不应该删除该对象的 3 或 4 个副本。您可以将对象的构造封装在逻辑中,然后调用一个 callback() 来简化远程方法的 I/O 管理并减少代码编写。减少到单个 Games.find() 调用将更容易发送此结果。

    还要注意花撇号字符和制表符与空格缩进(选择一个并坚持使用,不要混用),当您的代码构建和组织得当时,它会更容易提供帮助。

    【讨论】:

    • 添加了对前后光标 ID 变量的更正。
    • 这很完美,知道如何操作gameArray 中的项目。例如,将descriptionpublishedDate 放在一个额外的数组gameInfo 中。结果"gameInfo": { "description": "Published by Activision", "publishedDate": "2015-01-01T00:00:00.000Z" }
    • 你想用gameInfo实现什么?您的意思是在 gameArray 中操作结果对象并将 description 和 publishedDate 放入每个游戏对象中的 gameInfo 中吗?您已经可以访问这些字段,而无需添加额外的 gameInfo 层。但是您可以通过在 gameArray 上循环并修改对象来为每个游戏对象添加一个 gameInfo 对象。查看 lodash 库和 map 函数。
    • 是的,到时候去看看。
    • 关于通过passport-component进行社交登录身份验证的附加问题:stackoverflow.com/questions/34549717/…
    【解决方案2】:

    这里的问题是您使用 Category 模型作为入口点,而您似乎应该使用 Game 模型的内置查询方法以及过滤器、限制和偏移量,句号。这将允许您在不需要添加所有这些复杂性来支持您的之前/之后概念的情况下进行分页。这是不必要的。 如果你的关系设置正确,你只需要对游戏进行分页就是一个游戏模型查询!

    例如,您似乎需要一个在特定类别中成熟的游戏的分页列表,对吗?要获得符合这些条件的 5 场比赛的结果,您需要发送一个 json REST API 调用,如下所示:

    http://0.0.0.0:3000/api/Games?filter=%7B%20%22where%22%3A%20%7B%22mature%22%3Atrue%2C%20%22categoryId%22%3A%201004%7D%2C%20%22offset%22%3A%200%2C%20%22limit%22%3A%205%20%7D
    

    这很难阅读,因为它是编码的,但底层过滤器是这样构造的:

    { "where": {"mature":true, "categoryId": 1004}, "offset": 0, "limit": 5 }
    

    这将返回以下数据集,即前 5 款属于 1004 categoryId 且同样成熟的游戏

    [
      {
        "mature": true,
        "gameName": "CODBlacOps3",
        "categoryId": 1004,
        "gameId": 1000053,
        "description": "Published by Activision",
        "publishedDate": "2015-01-01T00:00:00.000Z"
      },
      {
        "mature": true,
        "gameName": "Evolve",
        "categoryId": 1004,
        "gameId": 1000054,
        "description": "Published by Turtle Rock Studios",
        "publishedDate": "2015-01-01T00:00:00.000Z"
      },
      {
        "mature": true,
        "gameName": "Battlefield4",
        "categoryId": 1004,
        "gameId": 1000055,
        "description": "Published by EA Digital Illusions",
        "publishedDate": "2013-01-01T00:00:00.000Z"
      },
      {
        "mature": true,
        "gameName": "Rainbow6",
        "categoryId": 1004,
        "gameId": 1000056,
        "description": "Published by EUbisoft",
        "publishedDate": "2015-01-01T00:00:00.000Z"
      },
      {
        "mature": true,
        "gameName": "Destiny",
        "categoryId": 1004,
        "gameId": 1000057,
        "description": "Published by Bungie",
        "publishedDate": "2014-01-01T00:00:00.000Z"
      }
    ]
    

    要获得 5 个游戏的下一页,我们只需将偏移量更改为 5,这会告诉数据库跳过前 5 个,因为我们现在在第 2 页:

    { "where": {"mature":true, "categoryId": 1004}, "offset": 5, "limit": 5 }
    

    这将返回以下结果:

    [
      {
        "mature": true,
        "gameName": "Wolfenstein",
        "categoryId": 1004,
        "gameId": 1000058,
        "description": "Published by Bethesda",
        "publishedDate": "2014-01-01T00:00:00.000Z"
      },
      {
        "mature": true,
        "gameName": "StarWarsBattleFront",
        "categoryId": 1004,
        "gameId": 1000059,
        "description": "Published by EA DICE",
        "publishedDate": "2015-01-01T00:00:00.000Z"
      },
      {
        "mature": true,
        "gameName": "Test1",
        "categoryId": 1004,
        "gameId": 1000060,
        "description": "Published by Test1",
        "publishedDate": "2015-12-19T00:00:00.000Z"
      },
      {
        "mature": true,
        "gameName": "Test2",
        "categoryId": 1004,
        "gameId": 1000061,
        "description": "Published by Test2",
        "publishedDate": "2015-12-19T00:00:00.000Z"
      },
      {
        "mature": true,
        "gameName": "Test3",
        "categoryId": 1004,
        "gameId": 1000062,
        "description": "Published by Test3",
        "publishedDate": "2015-12-19T00:00:00.000Z"
      }
    ]
    

    默认情况下,游戏按照其 game_id 的顺序排列。无需自定义远程方法和嵌套的 find()!

    如果您想按字母顺序排列列表,还可以添加order 过滤器,并将其设置为"order": "gameName ASC"

    { "where": {"mature":true, "categoryId": 1004}, "offset": 0, "limit": 5, "order": "gameName ASC" }
    

    这将返回一个新的(第 1 页,因为我将偏移量设置回 0)游戏数组,按字母顺序按 gameName 排序:

    [
      {
        "mature": true,
        "gameName": "Battlefield4",
        "categoryId": 1004,
        "gameId": 1000055,
        "description": "Published by EA Digital Illusions",
        "publishedDate": "2013-01-01T00:00:00.000Z"
      },
      {
        "mature": true,
        "gameName": "CODBlacOps3",
        "categoryId": 1004,
        "gameId": 1000053,
        "description": "Published by Activision",
        "publishedDate": "2015-01-01T00:00:00.000Z"
      },
      {
        "mature": true,
        "gameName": "Destiny",
        "categoryId": 1004,
        "gameId": 1000057,
        "description": "Published by Bungie",
        "publishedDate": "2014-01-01T00:00:00.000Z"
      },
      {
        "mature": true,
        "gameName": "Evolve",
        "categoryId": 1004,
        "gameId": 1000054,
        "description": "Published by Turtle Rock Studios",
        "publishedDate": "2015-01-01T00:00:00.000Z"
      },
      {
        "mature": true,
        "gameName": "Rainbow6",
        "categoryId": 1004,
        "gameId": 1000056,
        "description": "Published by EUbisoft",
        "publishedDate": "2015-01-01T00:00:00.000Z"
      }
    ]
    

    我之所以能够如此迅速地得到这个结果,是因为使用了 API Explorer。我分叉了您的项目,更改了数据库连接值以匹配我的本地服务器,启动它,然后转到 http://0.0.0.0/explorer/#!/Games/Games_find,然后在 Games 模型上使用不同的 filter 值:

    【讨论】:

    • 问题是我真的不想通过 url 完全公开所有这些 filter 选项。出于同样的原因,我对limit 进行了硬编码,但仍然可以选择设置不超过x 值的限制。我一直在跟进其他人(fb、instagram、youtube)如何设置他们的 api 的模式。我主要关心的是关于如何将这些附加值perPagetotal 等添加到最终结果中的一些建议
    • 不过,您可以为当前运行的自定义 Category.findById() 编写一些更简洁的代码。不需要有 3 个单独的 Games.find() 调用。逻辑应该专注于构建过滤器,然后运行单个 Games.find() 查询。至于你真正的问题,我将添加一个关于如何在响应中添加额外数据的答案。
    • 你说得对,我需要集中精力简化那部分。
    • 这里只是一个小练习,我进行了重构以消除游标的复杂性。您只需要询问每页的游戏数量和页码。我不知道您的全部要求,但我不认为游戏的每页项目(限制)和页码(限制 * 页码 = 偏移量)暴露了太多过滤器选项,它可以让您专注于分页,而不是游标概念。见github.com/notbrain/games-strongloop-api/blob/master/common/…
    • 你很有帮助。但是,如果有下一页,我似乎需要猜测页码。总的来说,我正在尝试复制类似于以下内容的内容:developers.facebook.com/docs/graph-api/using-graph-api(在网页搜索关键字内pagination
    猜你喜欢
    • 1970-01-01
    • 2016-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多