Произвольный выбор записи DynamoDB

У меня есть таблица DynamoDB с именем URLArray, которая содержит список URL-адресов (myURL) и уникальное имя видео (myKey).

См. таблицу URLArray здесь

Мне нужно сделать две вещи:

  1. Когда пользователь нажимает кнопку следующего видео, из этого URLArray необходимо выбрать случайную запись. Потенциально могут быть десятки тысяч строк.

Пользователь авторизован в приложении. Каждый раз, когда они заканчивают просмотр видео, записывается уникальное название видео. Итак... когда пользователь просматривает видео, оно добавляется в список в таблице с именем Пользователи в строке информации о пользователе.

  1. Итак... Эта случайная запись, которая выбирается, когда пользователь нажимает кнопку следующего видео в точке 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) по таблице Users, был ли уже виден myKey. Главным образом для того, чтобы перенести эту вычислительную нагрузку на облако, а не на локальное устройство, на котором работает приложение.

ПОСЛЕ ПОДУМА...

Спасибо за предложение, Сет. Я думал об этом в течение некоторого времени, и хотя требование случайности все еще остается в силе, я думаю, что в том, что вы предложили, есть доля правды. Причина, по которой мне нужна случайность, заключается в том, чтобы, например, 2 пользователя сидели рядом и не могли предсказать, какое видео будет следующим. Это не должна быть предсказуемая последовательность видео. Я не уверен, что смогу использовать функцию Scan с AWS Amplify/GraphQL. Так что помните, что здесь происходит две вещи: (1) загрузка видео, разумная запись его в URLArray для дальнейшего использования. (2) пользователи просматривают ранее не просмотренное случайное видео, а затем переходят к другому непросмотренному случайному видео

*(1) Мне нравится ваша идея использовать число для индексации URLArray, и это помогло немного облегчить жизнь. Таким образом, первый URL имеет индекс 0, следующий — 1 и т. д.

Я думаю здесь (чтобы избежать выполнения ListUrlArrays() и переноса ВЕСЬ массива локально на телефон) создать GSI с именем VideoNumber для таблицы URLArray. Это будет уникальный столбец VideoNumber с номером 0-N. Итак, представьте, что на приведенной выше диаграмме есть еще один столбец с именем VideoNumber. В строке 1 VideoNumber установлен на 0, в строке 2 VideoNumber установлен на 1 и т. д. ТОГДА все, что мне нужно сделать, это локально на устройстве, сгенерировать случайное число между 0-N, вызвать getURLArrayIdbyVideoNumber() запрос, специфичный для этого GSI, с номер, который мы только что сгенерировали, и он разблокирует нужную мне информацию из строки. Вуаля! Я думаю, что теперь это снимает большую часть этого тяжелого бремени.

Вопрос. Как легко получить текущее общее количество строк 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?


person chai86    schedule 11.08.2020    source источник
comment
Какой атрибут служит вашим ключом раздела (поле идентификатора?). Вы используете ключ сортировки? Действительно ли следующее видео должно быть случайным или просто тем, чего пользователь еще не видел?   -  person Seth Geoghegan    schedule 12.08.2020
comment
@chai86 chai86 Я бы порекомендовал проверить эту ветку stackoverflow.com/questions/10666364/   -  person Traycho Ivanov    schedule 14.08.2020
comment
@SethGeoghegan Нет ключа сортировки. Основная причина случайного перемешивания, как вы говорите, заключается в том, что он должен попасть в запись, которую пользователь раньше не видел. У меня есть запись в списке элементов, которые уже видел вошедший в систему пользователь, для сравнения. Я до сих пор не нашел прямого решения для чего-то, казалось бы, простого для базы данных.   -  person chai86    schedule 29.09.2020


Ответы (1)


Я думаю, вам будет легче придумать решение этой проблемы, если вы откажетесь от требования случайности. Похоже, что более важным требованием является предоставление пользователю видео, которое он раньше не видел.

Если это так, похоже, что ваш шаблон доступа может быть указан как

Получить ранее не просмотренное видео для пользователя

что проще решить задачу.

В отличие от баз данных SQL, в DynamoDB часто существует множество способов реализации заданного шаблона доступа. Мой ответ здесь только один способ.

Представьте, что ваша таблица URLArray представляет собой гигантский массив. Первый URL-адрес имеет индекс 0, следующий URL-адрес имеет индекс 1, второй URL-адрес имеет индекс 2 и так далее. Каждый пользователь вашего приложения начнет просмотр видео с URL-индекса 0, затем URL-индекса 1 и т. д. Это гарантирует, что пользователь никогда не увидит одно и то же видео дважды. Вам не нужно хранить список всех видео, которые они видели. Вместо этого вы можете сохранить индекс последнего видео, которое они видели.

Ваше приложение может захватить первые n видео из таблицы и показать пользователям. Как только этот список будет исчерпан, он может перейти к следующим n видео. И так далее...

Здесь я описал, как разбиение на страницы реализовано в DynamoDB. . Чтобы вернуть эту абстракцию в мир DynamoDB, ваш алгоритм может выглядеть примерно так:

  • Просканируйте таблицу URLArray на наличие первой страницы URL-адресов (операция scan без критериев фильтрации).
  • Вместе с результатами DynamoDB ответит LastEvaluatedKey, что позволит вам получить следующую страницу результатов, начиная с этой позиции.
  • Покажите пользователю каждое видео, которое вы извлекли из операции scan, убедившись, что id (первичный ключ) записано последним видео, которое они видели.
  • Когда вы исчерпаете URL-адреса из шага 1, выполните еще одну операцию scan с ExclusiveStartKey, установленным на LastEvaluatedKey, возвращенный из шага 2.
  • Когда пользователи вернутся в ваше приложение, запросите следующую страницу из таблицы URLArray, установив ExclusiveStartKey на id последнего видео, которое они просмотрели.

Это эффективно использует операцию scan для поиска в таблице URLArray по одной странице за раз. Ваше приложение будет эффективно искать таблицу сверху вниз, отслеживая, где каждый пользователь находится в любой момент времени. Когда пользователь повторно посещает ваше приложение, просто начните с того места, на котором он остановился.

В ответ на ваше редактирование:

Если ваш вариант использования требует, чтобы следующее видео было непредсказуемым (например, никакие 2 пользователя не могут предсказать, какое видео будет следующим), вам нужно решить несколько проблем одновременно:

  1. Выбор элемента непредсказуемым/случайным образом
  2. Отслеживание того, что пользователь уже видел

Объединение этих двух требований приводит к сложному шаблону доступа. Допустим, у вас есть N видео в таблице, и пользователь просмотрел N-1 из этих видео, оставив непросмотренным только одно видео. Если вы загружаете следующее видео случайным образом и вам нужно убедиться, что оно еще не было просмотрено, как вы найдете последнее непросмотренное видео? Сколько раз вам нужно будет угадать, прежде чем вы наткнетесь на единственное непросмотренное видео? Какую операцию запроса/сканирования вы могли бы выполнить в одном запросе к DDB? Я не говорю, что это невозможно, это просто... сложно.

Я думаю, что лучше разработать стратегию, которая будет непредсказуемой для пользователя, но предсказуемой для вас, когда дело доходит до выбора следующего непросмотренного видео.

Например, вы можете заранее вычислить случайный порядок индексов от 1 до N, который будет представлять порядок, в котором вы представляете видео для данного пользователя. Вы можете просмотреть этот список последовательно, отслеживая последний просмотренный индекс. Таким образом, вы всегда будете знать, какое видео будет следующим, и что это видео ранее не просматривалось этим пользователем. Получение этого видео будет простой операцией запроса к DDB.

Вы также спрашивали, как узнать количество элементов в DynamoDB. К сожалению, в DynamoDB нет эквивалента операции SQL count. Ответ на этот вопрос не однозначен. В интересах сообщества (и чтобы получить разнообразные ответы) я предлагаю вам задать отдельный вопрос в Stackoverflow относительно количества элементов в таблице DDB.

person Seth Geoghegan    schedule 29.09.2020
comment
Привет, Сет, только что обновил исходный пост, начинающийся «ПОСЛЕ ПОДУМА» с того, где я нахожусь :) - person chai86; 07.10.2020