【问题标题】:Randomly select DynamoDB entry随机选择 DynamoDB 条目
【发布时间】:2020-12-01 06:37:16
【问题描述】:

我有一个名为 URLArray 的 DynamoDB 表,其中包含 URL 列表 (myURL) 和唯一视频名称 (myKey)。

我需要做两件事:

  1. 当用户点击下一个视频按钮时,需要从这个 URLArray 中选择一个随机条目。可能有数万行。

用户已登录应用程序。每次他们看完视频时,都会记录视频的唯一视频名称。所以....当用户观看视频时,会将其添加到用户信息行下名为 Users 的表中的列表中。

  1. Soo...当用户单击第 1 点中的下一个视频按钮时选择的这个随机条目必须与他们已经看过的视频列表进行比较。确保该特定用户不会再次随机出现。

到目前为止,我做了一些效率极低的事情,虽然可行,但效果并不好:

顺便说一下,我正在使用 AppSync + GraphQL 与 DynamoDB 表进行交互。我首先获得了 URLArray 的本地副本:

 //Gets a list of the Key/URL pairs in the UrlArrays table in GraphQL   ****IN CONSTRUCTOR, so we have this URLArray data when componentDidMount()****
  listUrlArrays = async () => {  
    try {

      URLData = await API.graphql(graphqlOperation(ListUrlArrays)); //GraphQL query
      //URLData[] is available in the entire class
     
      this.setState({urlArrayLength: apiData.data.listURLArrays.items.length}); //gets the length of URLArray (i.e. how many videos are in the database)
      }
   }

作为概述,当用户点击下一个视频时:

     //When clicking next video
      async nextVideo(){
        
        await this.logVideosSeen(); //add myKey to the list of videos in *Users* table the logged in user has now seen
    
        await this.getURL();  //get the NEXT upcoming video's details, for Video Player to play and make sure it's not been seen before
    
      }
    

      //This will update the 'listOfVideosSeen[]' in Users table with videos unique myKey, the logged in user has seen
      logVideosSeen = async () => {     
           .......
      }

    async getURL() {  
        var dbIndex = this.getUniqueRandomNumber(this.state.urlArrayLength);  //Choose a number between 0 and N number of videos in URLArray
        
        //the hasVideoBeenSeen() basically gets the list of videos a user has already seen from `Users` table with the GraphQL getusers command, and creates a local copy of this list (can get big). I use javascripts indexOf() to check whether myKey already exists in the list 
        while(await this.hasVideoBeenSeen(this.state.URLData[dbIndex].myKey))  //while true i.e. user has seen that video before
        {
          dbIndex = this.getUniqueRandomNumber(this.state.urlArrayLength);  //get another random number to fetch a new myKey
        }
        
        //If false, we'll exit the loop and know we've got a not seen before myKey, proceed to set to play...
        if(dbIndex != null){
          this.setState({ playURL: this.state.URLData[dbIndex].vidURL });   //Retrieve the URL from the local URLArray that we're going to play (i.e. the next video to come)
          
        }   
      }

如果需要,我可以分享更多代码,但基本上我想知道如何:

  1. 让 Lambda 函数根据当前 URLArray 的大小选择一个随机数(我可能需要保留 URLArray 的本地副本)。但我认为这里的第 2 点确实效率低下..

  2. 让 Lambda 函数根据用户表检查(while 循环)是否已看到 myKey。主要是将这种计算负担转移到云端,而不是应用运行的本地设备。

三思后......

感谢赛斯的建议。我已经考虑了一段时间,虽然随机性要求仍然适用,但我认为你的建议有些道理。我需要随机性的原因是,例如,2 个用户并排坐着,无法预测接下来会出现哪个视频。它不应该是可预测的视频序列。我不确定是否可以将 Scan 函数与 AWS Amplify/GraphQL 一起使用。所以请记住这里发生了两件事:(1) 视频上传,将其记录在 URLArray 中以供将来参考。 (2) 用户观看之前未看过的随机视频,然后移动到另一个未看过的随机视频

*(1) 我喜欢你使用数字来索引 URLArray 的想法,这有助于让生活更轻松一些。所以第一个 URL 在索引 0 处,下一个在 1 处……

我在这里的想法(为了避免我执行 ListUrlArrays() 并将整个数组本地带到手机),是为 URLArray 表创建一个名为 VideoNumber 的 GSI。这将是具有数字 0-N 的唯一 VideoNumber 列。所以想象上面的图表有另一个名为 VideoNumber 的列。第 1 行的 VideoNumber 设置为 0,第 2 行的 VideoNumber 设置为 1 等等……然后我需要做的就是在设备上本地生成一个介于 0-N 之间的随机数,调用特定于该 GSI 的 getURLArrayIdbyVideoNumber() 查询,使用我们刚刚生成的数字,它将从行中解锁我需要的信息。瞧!我认为现在大部分沉重的负担都转移了。

问题:在每个视频上传之前,如何轻松获取表格中当前的总行数N(或行数)?然后我会加一。

我可以做的另一件事是将此当前计数保存在另一个我用于持久数据的 DynamoDB 表中,在上传之前从那里读取数字,并在上传后写入 N+1 以增加它(每次 2 次 DynamoDB 操作上传)。这并不理想。

*(2) 当用户观看完视频后,我可以登录一个列表(在 DynamoDB 中的用户信息下),他们已经看过哪些视频。因此,例如,这现在可以是一个看过的列表:[3,12,73,108,57] 表示他们迄今为止看过的 5 个视频。当用户单击 nextVideo() 时,我们将生成一个随机的 newNumber,并立即将其与已看到列表中的任何数字进行比较。我使用seenlist.indexOf(newNumber),如果列表中不存在 newNumber,它会再次运行或停止。然后我可以通过 GSI 查询,并从 URLArray 中检索相关信息以显示视频。

我认为这个indexOf() 是设备上最大的计算负担,并且随着seenList 的增加显然会变慢一些。但是使用纯整数应该比我之前使用的字母数字 myKey 更快。欢迎任何其他建议:)

我还没有尝试过,但这只是一个想法,因为我需要保留随机元素。但首先,你知道我如何轻松找到 URLArray 的行数或表数吗?

【问题讨论】:

  • 哪个属性是你的分区键(ID 字段?)。你在使用排序键吗?下一个视频真的需要随机播放,还是只是用户从未看过的内容?
  • @chai86 我建议检查这个线程stackoverflow.com/questions/10666364/…
  • @SethGeoghegan 没有排序键。正如您所说,随机洗牌的主要原因是它必须点击用户以前从未见过的条目。我在列表中记录了登录用户已经看到的项目,以进行比较。对于数据库看似简单的事情,我仍然没有找到一个直接的解决方案

标签: react-native amazon-dynamodb dynamodb-queries


【解决方案1】:

我认为,如果您放弃随机性要求,您将更容易找到解决此问题的方法。听起来更重要的要求是向用户展示他们以前从未看过的视频。

如果正确,听起来您的访问模式可以表述为

为用户获取以前看不见的视频

这是一个更容易解决的问题。

与 SQL 数据库不同,通常有多种方法可以在 DynamoDB 中实现给定的访问模式。我在这里的回答只是一种方式。

将您的URLArray 表想象成一个巨大的数组。第一个 URL 在索引 0 处,下一个 URL 在索引 1 处,第二个 URL 在索引 2 处,依此类推。您的应用程序的每个用户都将从观看 URL 索引 0 处的视频开始,然后是 URL 索引 1,等等。这将确保用户不会看到相同的视频两次。您不需要存储他们看过的所有视频的列表。相反,您可以存储他们观看的最后视频的索引。

您的应用程序可以从表格中抓取前 n 个视频以呈现给您的用户。一旦该列表用完,它就可以获取 下一个 n 个视频。等等……

我在这里描述的基本上是pagination is implemented in DynamoDB。要将这种抽象带回 DynamoDB 的世界,您的算法可能如下所示:

  • 扫描URLArray 表以查找URL 的第一“页”(scan 操作,没有过滤条件)
  • 随着结果,DynamoDB 将回复 LastEvaluatedKey,这将允许您检索下一页结果从此位置开始
  • 向您的用户展示您从 scan 操作中拉回的每个视频,确保记录他们看到的最后一个视频 id(主键)。
  • 当您从第 1 步中耗尽 URL 时,执行另一个 scan 操作,并将 ExclusiveStartKey 设置为从第 2 步返回的 LastEvaluatedKey
  • 当用户返回您的应用程序时,从URLArray 表中查询下一页,并将ExclusiveStartKey 设置为他们观看的最后一个视频的id

这有效地使用scan 操作一次搜索一页您的URLArray 表。您的应用程序将有效地从上到下搜索表格,跟踪每个用户在任何给定时间的位置。当用户重新访问您的应用程序时,只需从他们离开的地方开始。

回应您的修改:

如果您的用例要求下一个视频不可预测(例如,没有 2 个用户可以预测下一个视频是什么),那么您需要同时解决几个问题:

  1. 以不可预测/随机的方式选择项目
  2. 跟踪用户已经看到的内容

将这两个要求放在一起会形成一个棘手的访问模式。假设您的表格中有 N 个视频,并且用户查看了其中的 N-1 个视频,而只有一个视频没有看到。如果您正在随机获取您的下一个视频并且需要确保它尚未被观看,您将如何找到最后一个未看过的视频?在遇到唯一未看过的视频之前,您需要猜测多少次?您可以在对 DDB 的单个请求中执行哪些查询/扫描操作?我并不是说这不可能,只是……很复杂。

我认为最好生成一个用户无法预测的策略,但在选择下一个看不见的视频时你可以预测。

例如,您可以提前从 1..N 预先计算索引的随机顺序,这将代表您为给定用户呈现视频的顺序。您可以按顺序浏览该列表,跟踪上次看到的索引。这样一来,您就总能知道下一个视频是哪个,并且该用户之前没有看过该视频。获取该视频将是对 DDB 的简单查询操作。

您还询问了如何查找 DynamoDB 中的项目数。不幸的是,没有与 SQL count 操作等效的 DynamoDB。这个问题的答案并不简单。为了社区的利益(并获得多样化的答案),我建议您在 Stackoverflow 上提出一个关于 DDB 表中项目数的单独问题。

【讨论】:

  • 嗨,Seth,刚刚更新了以“思考后”开头的原始帖子,其中包含我所在的位置 :)
猜你喜欢
  • 2012-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-25
  • 2020-04-04
  • 1970-01-01
  • 2021-05-04
  • 1970-01-01
相关资源
最近更新 更多