Подписка на события AWS AppSync на основе местоположения

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

Однако что, если я хочу, чтобы мой клиент также подписывался на новые события, создаваемые в этом диапазоне расстояний?

  1. пользователь подписывается на место
  2. если событие создается рядом с этим местом, уведомить пользователя

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


person AJ Z.    schedule 12.07.2018    source источник


Ответы (1)


Это отличный вопрос, и я думаю, что есть несколько способов решить эту проблему. Сложность здесь заключается в том, что вам нужно найти способ задать вопрос «Какие подписки заинтересованы в событии в этом месте». Вот один из возможных путей вперед.

Далее предполагается, что эти части схемы:

// Whatever custom object has a location 
type Post {
  id: ID!
  title: String
  location: Location
}
input PublishPostInput {
  id: ID!
  title: String
  location: Location
  subscriptionID: ID
}
type PublishPostOutput {
  id: ID!
  title: String
  location: Location
  subscriptionID: ID
}

type Location {
  lat: Float,
  lon: Float
}
input LocationInput {
  lat: Float,
  lon: Float
}

# A custom type to hold custom tracked subscription information
# for location discover
type OpenSubscription {
  subscriptionID: ID!
  location: Location
  timestamp: String!
}
type OpenSubscriptionConnection {
  items: [OpenSubscription]
  nextToken: String
}

type Query {
  # Query elasticsearch index for relevant subscriptions
  openSubscriptionsNear(location: LocationInput, distance: String): OpenSubscriptionConnection
}
type Mutation {
  # This mutation uses a local resolver (e.g. a resolver with a None data source) and simply returns the input as is.
  publishPostToSubscription(input: PublishPostInput): PublishPostOutput
}
type Subscription {
  # Anytime someone passes an object with the same subscriptionID to the "publishPostToSubscription" mutation field, get updated.
  listenToSubscription(subscriptionID: ID!): PublishPostOutput
    @aws_subscribe(mutations:["publishPostToSubscription"])
}

Предполагая, что вы используете DynamoDB в качестве основного источника достоверной информации, настройте поток DynamoDB, который вызывает лямбда-функцию «PublishIfInRange». Эта функция "PublishIfInRange" будет выглядеть примерно так

// event - { location: { lat, lon }, id, title, ... }
function lambdaHandler(event) {

  const relevantSubscriptions = await callGraphql(`
    query GetSubscriptions($location: LocationInput) {
      openSubscriptionsNear(location:$location, distance: "10 miles") {
        subscriptionID
      }
    }
  `, { variables: { location: event.location }})

  for (const subscription of relevantSubscriptions) {
    callGraphql(`
      mutation PublishToSubscription($subID: ID!, $obj: PublishPostInput) {
        publishPostToSubscription(input: $obj) {
          id
          title
          location { lat lon }
          subscriptionID
        }
      }
    `, { variables: { input: { ...subscription, ...event }}})
  }
}

Вам нужно будет вести реестр подписок, проиндексированных по местоположению. Один из способов сделать это — заставить ваше клиентское приложение вызывать мутацию, которая создает объект подписки с местоположением и идентификатором подписки (например, mutation { makeSubscription(loc: $loc) { ... } } при условии, что вы используете $util.autoId() для создания идентификатора подписки в распознавателе). Получив идентификатор подписки, вы можете выполнить вызов подписки через graphql и передать идентификатор подписки в качестве аргумента (например, subscription { listenToSubscription(subscriptionID: "my-id") { id title location { lat lon } } }). Когда вы выполняете указанный выше вызов подписки, AppSync создает тему и разрешает текущему пользователю подписаться на эту тему. Тема уникальна для вызываемого поля подписки и набора аргументов, передаваемых в поле подписки. Другими словами, топик получает только объекты

Теперь всякий раз, когда создается объект, запись передается в лямбда-функцию через потоки DynamoDB. Лямбда-функция запрашивает elasticsearch для всех открытых подписок рядом с этим объектом, а затем публикует запись для каждой из этих открытых подписок.

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

person mparis    schedule 13.07.2018
comment
Чтобы масштабировать это, вы можете поумнеть в том, как вы разделяете идентификатор подписки. Например, вы можете разделить свою карту на N небольших фрагментов, а затем на вашем клиенте заставить их подписаться на Y ближайших фрагментов в сетке и выполнить любую мелкозернистую фильтрацию на клиенте. Таким образом, у вас будет не более N идентификаторов подписки, поэтому процедура разветвления будет намного меньше, чем если бы у каждого пользователя была собственная подписка. - person mparis; 13.07.2018
comment
учитывая проблему масштабирования и стоимость со стороны пользователя для поддержки многих каналов подписки, было бы более разумно просто запрашивать чаще, чтобы убедиться, что пользователь обновляется почти в режиме реального времени? - person AJ Z.; 13.07.2018
comment
Опрос — гораздо более простой и экономичный вариант, если не требуется настоящий режим реального времени. - person mparis; 13.07.2018