Google is committed to advancing racial equity for Black communities. See how.
Эта страница переведена с помощью Cloud Translation API.
Switch to English

Cloud Firestore Web Codelab

Цели

В этой кодовой лаборатории вы создадите веб-приложение для рекомендации ресторанов на базе Cloud Firestore .

img5.png

Что ты узнаешь

  • Чтение и запись данных в Cloud Firestore из веб-приложения
  • Слушайте изменения в данных Cloud Firestore в режиме реального времени
  • Используйте Firebase Authentication и правила безопасности для защиты данных Cloud Firestore
  • Написание сложных запросов Cloud Firestore

Что вам понадобится

Перед запуском этой кодовой лаборатории убедитесь, что вы установили:

  • npm, который обычно поставляется с Node.js - рекомендуется Node v8
  • IDE / текстовый редактор по вашему выбору, например WebStorm , Atom , VS Code или Sublime

Создать проект Firebase

  1. В консоли Firebase нажмите Добавить проект , затем назовите проект Firebase FriendlyEats .

Запомните идентификатор проекта для вашего проекта Firebase.

  1. Щелкните Создать проект .

Приложение, которое мы собираемся создать, использует несколько сервисов Firebase, доступных в Интернете:

  • Проверка подлинности Firebase для простой идентификации ваших пользователей
  • Cloud Firestore для сохранения структурированных данных в облаке и получения мгновенных уведомлений при обновлении данных
  • Хостинг Firebase для размещения и обслуживания ваших статических ресурсов

Для этой конкретной кодовой лаборатории мы уже настроили хостинг Firebase. Однако для Firebase Auth и Cloud Firestore мы проведем вас через настройку и включение служб с помощью консоли Firebase.

Включить анонимную аутентификацию

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

Вам нужно будет включить анонимный вход.

  1. В консоли Firebase найдите раздел « Разработка » на левой панели навигации.
  2. Щелкните Проверка подлинности , затем щелкните вкладку Метод входа (или щелкните здесь, чтобы перейти прямо туда).
  3. Включите поставщика анонимного входа, затем нажмите Сохранить .

img7.png

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

Включить Cloud Firestore

Приложение использует Cloud Firestore для сохранения и получения информации о ресторанах и оценок.

Вам нужно будет включить Cloud Firestore. В разделе « Разработка» консоли Firebase нажмите Firestore . Щелкните Создать базу данных на панели Cloud Firestore.

Доступ к данным в Cloud Firestore контролируется Правилами безопасности. Мы поговорим о правилах позже в этой лаборатории кода, но сначала нам нужно установить некоторые базовые правила для наших данных, чтобы начать работу. На вкладке «Правила» консоли Firebase добавьте следующие правила и нажмите « Опубликовать» .

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      //
      // WARNING: These rules are insecure! We will replace them with
      // more secure rules later in the codelab
      //
      allow read, write: if request.auth != null;
    }
  }
}

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

Клонируйте репозиторий GitHub из командной строки:

git clone https://github.com/firebase/friendlyeats-web

Образец кода должен быть клонирован в каталог 📁 friendlyeats-web , убедитесь, что с этого момента ваша командная строка запускается из этого каталога:

cd friendlyeats-web

Импортируйте стартовое приложение

Используя вашу IDE (WebStorm, Atom, Sublime, Visual Studio Code ...), откройте или импортируйте каталог 📁 friendlyeats-web . Этот каталог содержит начальный код для codelab, который состоит из еще не работающего приложения для рекомендации ресторанов. Мы сделаем его работоспособным на протяжении всей этой кодовой лаборатории, поэтому вам скоро нужно будет отредактировать код в этом каталоге.

Интерфейс командной строки Firebase (CLI) позволяет вам обслуживать ваше веб-приложение локально и развертывать ваше веб-приложение на хостинге Firebase.

  1. Установите CLI, выполнив следующую команду npm:
npm -g install firebase-tools
  1. Убедитесь, что интерфейс командной строки установлен правильно, выполнив следующую команду:
firebase --version

Убедитесь, что версия Firebase CLI - v7.4.0 или новее.

  1. Авторизуйте интерфейс командной строки Firebase, выполнив следующую команду:
firebase login

Мы настроили шаблон веб-приложения для извлечения конфигурации вашего приложения для хостинга Firebase из локального каталога и файлов вашего приложения. Но для этого нам нужно связать ваше приложение с вашим проектом Firebase.

  1. Убедитесь, что ваша командная строка обращается к локальному каталогу вашего приложения.
  2. Свяжите свое приложение с проектом Firebase, выполнив следующую команду:
firebase use --add
  1. Когда будет предложено, выберите свой идентификатор проекта , а затем присвойте проекту Firebase псевдоним.

Псевдоним полезен, если у вас несколько сред (производственная, промежуточная и т. Д.). Однако для этой лаборатории давайте просто будем использовать псевдоним по default .

  1. Следуйте оставшимся инструкциям в командной строке.

Мы готовы приступить к работе над нашим приложением! Давайте запустим наше приложение локально!

  1. Выполните следующую команду Firebase CLI:
firebase emulators:start --only hosting
  1. В вашей командной строке должен отображаться следующий ответ:
hosting: Local server: http://localhost:5000

Мы используем эмулятор Firebase Hosting для локального обслуживания нашего приложения. Теперь веб-приложение должно быть доступно по адресу http: // localhost: 5000 .

  1. Откройте свое приложение по адресу http: // localhost: 5000 .

Вы должны увидеть свою копию FriendlyEats, которая подключена к вашему проекту Firebase.

Приложение автоматически подключилось к вашему проекту Firebase и автоматически выполнило вход в систему как анонимный пользователь.

img2.png

В этом разделе мы запишем некоторые данные в Cloud Firestore, чтобы мы могли заполнить пользовательский интерфейс приложения. Это можно сделать вручную через консоль Firebase , но мы сделаем это в самом приложении, чтобы продемонстрировать базовую запись в Cloud Firestore.

Модель данных

Данные Firestore разбиты на коллекции, документы, поля и вложенные коллекции. Мы будем хранить каждый ресторан как документ в коллекции верхнего уровня, называемой restaurants .

img3.png

Позже мы сохраним все отзывы в подколлекции, называемой ratings в каждом ресторане.

img4.png

Добавить рестораны в Firestore

Основной объект модели в нашем приложении - ресторан. Напишем код, который добавляет документ restaurants коллекцию restaurants .

  1. Из загруженных файлов откройте scripts/FriendlyEats.Data.js .
  2. Найдите функцию FriendlyEats.prototype.addRestaurant .
  3. Замените всю функцию следующим кодом.

FriendlyEats.Data.js

FriendlyEats.prototype.addRestaurant = function(data) {
  var collection = firebase.firestore().collection('restaurants');
  return collection.add(data);
};

Приведенный выше код добавляет новый документ в коллекцию restaurants . Данные документа поступают из простого объекта JavaScript. Мы делаем это, сначала получая ссылку на restaurants коллекции Cloud Firestore, а затем add данные.

Добавим рестораны!

  1. Вернитесь в приложение FriendlyEats в браузере и обновите его.
  2. Щелкните Добавить фиктивные данные .

Приложение автоматически сгенерирует случайный набор объектов ресторанов, а затем вызовет вашу функцию addRestaurant . Однако вы еще не увидите данные в своем реальном веб-приложении, потому что нам все еще нужно реализовать получение данных (следующий раздел кодовой таблицы).

Однако если вы перейдете на вкладку Cloud Firestore в консоли Firebase, то теперь должны увидеть новые документы в коллекции restaurants !

img6.png

Поздравляем, вы только что записали данные в Cloud Firestore из веб-приложения!

В следующем разделе вы узнаете, как получить данные из Cloud Firestore и отобразить их в своем приложении.

В этом разделе вы узнаете, как получать данные из Cloud Firestore и отображать их в своем приложении. Два ключевых шага - это создание запроса и добавление прослушивателя моментальных снимков. Этот слушатель будет уведомлен обо всех существующих данных, соответствующих запросу, и получит обновления в режиме реального времени.

Во-первых, давайте создадим запрос, который будет обслуживать нефильтрованный список ресторанов по умолчанию.

  1. Вернитесь к файлу scripts/FriendlyEats.Data.js .
  2. Найдите функцию FriendlyEats.prototype.getAllRestaurants .
  3. Замените всю функцию следующим кодом.

FriendlyEats.Data.js

FriendlyEats.prototype.getAllRestaurants = function(renderer) {
  var query = firebase.firestore()
      .collection('restaurants')
      .orderBy('avgRating', 'desc')
      .limit(50);

  this.getDocumentsInQuery(query, renderer);
};

В приведенном выше коде мы создаем запрос, который будет извлекать до 50 ресторанов из коллекции верхнего уровня с именем restaurants , которые упорядочены по средней оценке (в настоящее время все нулевые). После того, как мы объявили этот запрос, мы передаем его getDocumentsInQuery() который отвечает за загрузку и рендеринг данных.

Мы сделаем это, добавив прослушиватель снимков.

  1. Вернитесь к файлу scripts/FriendlyEats.Data.js .
  2. Найдите функцию FriendlyEats.prototype.getDocumentsInQuery .
  3. Замените всю функцию следующим кодом.

FriendlyEats.Data.js

FriendlyEats.prototype.getDocumentsInQuery = function(query, renderer) {
  query.onSnapshot(function(snapshot) {
    if (!snapshot.size) return renderer.empty(); // Display "There are no restaurants".

    snapshot.docChanges().forEach(function(change) {
      if (change.type === 'removed') {
        renderer.remove(change.doc);
      } else {
        renderer.display(change.doc);
      }
    });
  });
};

В приведенном выше коде query.onSnapshot запускает свой обратный вызов каждый раз, когда изменяется результат запроса.

  • В первый раз обратный вызов запускается для всего набора результатов запроса, то есть для всей коллекции restaurants из Cloud Firestore. Затем он передает все отдельные документы в функцию renderer.display .
  • Когда документ удаляется, change.type равен removed . В этом случае мы вызовем функцию, которая удаляет ресторан из пользовательского интерфейса.

Теперь, когда мы реализовали оба метода, обновите приложение и убедитесь, что рестораны, которые мы видели ранее в консоли Firebase, теперь отображаются в приложении. Если вы успешно завершили этот раздел, ваше приложение теперь читает и записывает данные с помощью Cloud Firestore!

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

img5.png

До сих пор мы показали, как использовать onSnapshot для получения обновлений в реальном времени; однако это не всегда то, что мы хотим. Иногда имеет смысл получить данные только один раз.

Мы хотим реализовать метод, который запускается, когда пользователь нажимает на конкретный ресторан в вашем приложении.

  1. Вернитесь к своему файлу scripts/FriendlyEats.Data.js .
  2. Найдите функцию FriendlyEats.prototype.getRestaurant .
  3. Замените всю функцию следующим кодом.

FriendlyEats.Data.js

FriendlyEats.prototype.getRestaurant = function(id) {
  return firebase.firestore().collection('restaurants').doc(id).get();
};

После того, как вы внедрите этот метод, вы сможете просматривать страницы для каждого ресторана. Просто нажмите на ресторан в списке, и вы увидите страницу с описанием ресторана:

img1.png

На данный момент вы не можете добавлять рейтинги, поскольку нам все еще нужно реализовать добавление рейтингов позже в кодовой таблице.

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

Вот пример простого запроса для получения всех ресторанов Dim Sum :

var filteredQuery = query.where('category', '==', 'Dim Sum')

Как следует из названия, метод where() заставит наш запрос загружать только элементы коллекции, поля которых соответствуют установленным нами ограничениям. В этом случае будут загружены только рестораны с category Dim Sum .

В нашем приложении пользователь может объединить несколько фильтров в цепочку для создания конкретных запросов, например «Пицца в Сан-Франциско» или «Морепродукты в Лос-Анджелесе, заказанные по популярности».

Мы создадим метод, который создаст запрос, который будет фильтровать наши рестораны на основе нескольких критериев, выбранных нашими пользователями.

  1. Вернитесь к своему файлу scripts/FriendlyEats.Data.js .
  2. Найдите функцию FriendlyEats.prototype.getFilteredRestaurants .
  3. Замените всю функцию следующим кодом.

FriendlyEats.Data.js

FriendlyEats.prototype.getFilteredRestaurants = function(filters, renderer) {
  var query = firebase.firestore().collection('restaurants');

  if (filters.category !== 'Any') {
    query = query.where('category', '==', filters.category);
  }

  if (filters.city !== 'Any') {
    query = query.where('city', '==', filters.city);
  }

  if (filters.price !== 'Any') {
    query = query.where('price', '==', filters.price.length);
  }

  if (filters.sort === 'Rating') {
    query = query.orderBy('avgRating', 'desc');
  } else if (filters.sort === 'Reviews') {
    query = query.orderBy('numRatings', 'desc');
  }

  this.getDocumentsInQuery(query, renderer);
};

Приведенный выше код добавляет несколько фильтров where и одно предложение orderBy для создания составного запроса на основе ввода данных пользователем. Теперь наш запрос будет возвращать только те рестораны, которые соответствуют требованиям пользователя.

Обновите приложение FriendlyEats в браузере, затем убедитесь, что вы можете фильтровать по цене, городу и категории. Во время тестирования вы увидите ошибки в консоли JavaScript вашего браузера, которые выглядят следующим образом:

The query requires an index. You can create it here: https://console.firebase.google.com/project/.../database/firestore/indexes?create_index=...

Эти ошибки вызваны тем, что Cloud Firestore требует индексов для большинства составных запросов. Требование индексов для запросов позволяет Cloud Firestore быстро масштабироваться.

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

Если мы не хотим исследовать каждый путь в нашем приложении и следовать каждой из ссылок для создания индекса, мы можем легко развернуть сразу несколько индексов с помощью интерфейса командной строки Firebase.

  1. В загруженном локальном каталоге вашего приложения вы найдете файл firestore.indexes.json .

В этом файле описаны все индексы, необходимые для всех возможных комбинаций фильтров.

firestore.indexes.json

{
 "indexes": [
   {
     "collectionGroup": "restaurants",
     "queryScope": "COLLECTION",
     "fields": [
       { "fieldPath": "city", "order": "ASCENDING" },
       { "fieldPath": "avgRating", "order": "DESCENDING" }
     ]
   },

   ...

 ]
}
  1. Разверните эти индексы с помощью следующей команды:
firebase deploy --only firestore:indexes

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

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

В нашем приложении будет много пользователей, которые захотят поставить оценку ресторану, поэтому нам нужно будет координировать несколько операций чтения и записи. Сначала необходимо отправить сам отзыв, а затем обновить count оценок и average rating ресторана. Если один из них не работает, а другой нет, мы остаемся в несогласованном состоянии, когда данные в одной части нашей базы данных не соответствуют данным в другой.

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

  1. Вернитесь к своему файлу scripts/FriendlyEats.Data.js .
  2. Найдите функцию FriendlyEats.prototype.addRating .
  3. Замените всю функцию следующим кодом.

FriendlyEats.Data.js

FriendlyEats.prototype.addRating = function(restaurantID, rating) {
  var collection = firebase.firestore().collection('restaurants');
  var document = collection.doc(restaurantID);
  var newRatingDocument = document.collection('ratings').doc();

  return firebase.firestore().runTransaction(function(transaction) {
    return transaction.get(document).then(function(doc) {
      var data = doc.data();

      var newAverage =
          (data.numRatings * data.avgRating + rating.rating) /
          (data.numRatings + 1);

      transaction.update(document, {
        numRatings: data.numRatings + 1,
        avgRating: newAverage
      });
      return transaction.set(newRatingDocument, rating);
    });
  });
};

В блоке выше мы запускаем транзакцию для обновления числовых значений avgRating и numRatings в документе ресторана. В то же время мы добавляем новый rating в подколлекцию ratings .

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

  1. В разделе « Разработка» консоли Firebase нажмите « База данных» .
  2. Щелкните вкладку Правила в разделе Cloud Firestore (или щелкните здесь, чтобы перейти прямо туда).
  3. Замените значения по умолчанию следующими правилами, затем нажмите Опубликовать .

firestore.rules

rules_version = '2';
service cloud.firestore {

  // Determine if the value of the field "key" is the same
  // before and after the request.
  function unchanged(key) {
    return (key in resource.data) 
      && (key in request.resource.data) 
      && (resource.data[key] == request.resource.data[key]);
  }

  match /databases/{database}/documents {
    // Restaurants:
    //   - Authenticated user can read
    //   - Authenticated user can create/update (for demo purposes only)
    //   - Updates are allowed if no fields are added and name is unchanged
    //   - Deletes are not allowed (default)
    match /restaurants/{restaurantId} {
      allow read: if request.auth != null;
      allow create: if request.auth != null;
      allow update: if request.auth != null
                    && (request.resource.data.keys() == resource.data.keys()) 
                    && unchanged("name");
      
      // Ratings:
      //   - Authenticated user can read
      //   - Authenticated user can create if userId matches
      //   - Deletes and updates are not allowed (default)
      match /ratings/{ratingId} {
        allow read: if request.auth != null;
        allow create: if request.auth != null
                      && request.resource.data.userId == request.auth.uid;
      }
    }
  }
}

Эти правила ограничивают доступ, чтобы клиенты могли вносить только безопасные изменения. Например:

  • Обновления документа о ресторане могут изменять только рейтинги, но не имя или любые другие неизменяемые данные.
  • Рейтинги могут быть созданы только в том случае, если идентификатор пользователя соответствует зарегистрированному пользователю, что предотвращает спуфинг.

В качестве альтернативы использованию консоли Firebase вы можете использовать интерфейс командной строки Firebase для развертывания правил в своем проекте Firebase. Файл firestore.rules в вашем рабочем каталоге уже содержит правила, указанные выше. Чтобы развернуть эти правила из вашей локальной файловой системы (вместо использования консоли Firebase), вы должны выполнить следующую команду:

firebase deploy --only firestore:rules

В этой лаборатории вы узнали, как выполнять базовые и расширенные операции чтения и записи с помощью Cloud Firestore, а также как защитить доступ к данным с помощью правил безопасности. Вы можете найти полное решение в репозитории quickstarts-js .

Чтобы узнать больше о Cloud Firestore, посетите следующие ресурсы: