Статьи

Начало работы с клиентской базой данных JavaScript PouchDB

Эта статья была рецензирована Себастьяном Зейтцем и Таулантом Спахиу . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!

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

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

Что такое PouchDB?

PouchDB — это база данных JavaScript с открытым исходным кодом, созданная на основе Apache CouchDB, которая хорошо работает в браузере.

Что такое база данных JavaScript?

Проще говоря, база данных JavaScript — это объект JavaScript, который предоставляет методы (или API) для размещения, получения и поиска данных. На самом деле, простой старый объект JavaScript — это самый простой вид базы данных JavaScript. Если вы знакомы с Meteor , возможно, вы слышали о Minimongo, который является еще одной клиентской базой данных JavaScript, имитирующей API MongoDB.

PouchDB — это реализация CouchDB на JavaScript. Его цель — эмулировать API CouchDB с почти идеальной точностью при работе в браузере или в Node.js.

Отличие PouchDB от таких баз данных, как Minimongo, заключается в том, что по умолчанию он не просто находится в оперативной памяти, он использует IndexedDB для скрытого хранения. IndexedDB — это низкоуровневый API-интерфейс для хранения на стороне клиента значительных объемов структурированных данных, включая файлы и большие двоичные объекты. Это означает, что данные PouchDB хранятся на диске и будут доступны даже после обновления страницы (однако данные, сохраненные одним браузером, не будут доступны другим браузерам).

Различные адаптеры позволяют изменять базовый уровень хранения данных.

Отношение к CouchDB

PouchDB — это реализация CouchDB на JavaScript, которая максимально близко имитирует API.

В CouchDB вы получите все документы, используя этот вызов API

/db/_all_docs?include_docs=true 

В PouchDB это становится

 db.allDocs({include_docs: true}) 

PouchDB позволяет приложениям хранить данные локально в автономном режиме, а затем синхронизировать их с CouchDB, когда приложение снова подключено к сети.

Теперь давайте посмотрим, как вы можете использовать PouchDB в своих приложениях.

Установка

Чтобы начать использовать PouchDB, вам просто нужно включить клиентскую библиотеку PouchDB. Вы можете использовать автономную сборку, которая делает конструктор PouchDB глобально доступным для объекта window

 <script src="https://cdn.jsdelivr.net/pouchdb/5.4.5/pouchdb.min.js"></script> 

или, если вы используете его в среде Node.js / browserify / webpack, вы можете установить его с помощью npm .

 $ npm install pouchdb --save 

Тогда в вашем JavaScript:

 var PouchDB = require('pouchdb'); 

( npm isntall pouchdb факт: npm isntall pouchdb также работает!)

Работа с PouchDB

Создание базы данных

Создать базу данных PouchDB так же просто, как вызвать конструктор PouchDB. Давайте создадим базу данных под названием «Фильмы».

 var movies = new PouchDB('Movies'); 

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

 movies .info() .then(function (info) { console.log(info); }) 

Код выше выводит следующее:

 {"doc_count":0,"update_seq":0,"idb_attachment_format":"binary","db_name":"Movies","auto_compaction":false,"adapter":"idb"} 

Поле adapter указывает, что под ним используется IndexedDB.

Работа с документами

PouchDB — это база данных на основе документов NoSQL, поэтому жесткой схемы нет, и вы можете просто вставлять документы JSON напрямую. Давайте посмотрим, как вы можете вставлять, обновлять, извлекать или удалять документы.

Создание документа

Вы можете создать новый документ, используя метод put

 // returns a promise db.put(doc, [docId], [docRev], [options]) 

Параметры в квадратных скобках являются необязательными. С каждым документом связано поле _id , которое работает как уникальный идентификатор.

Создайте новый документ в ранее созданной базе данных Movies , выполнив следующий код:

 movies .put({ _id: 'tdkr', title: 'The Dark Knight Rises', director: 'Christopher Nolan' }).then(function (response) { console.log("Success", response) }).then(function (err) { console.log("Error", err) }) 

Ответ, в случае успеха, будет что-то вроде:

 Success {ok: true, id: "tdkr", rev: "3-f8afdea539618c3e8dceb20ba1659d2b"} 

Вызов movies.info() теперь выдаст {doc_count: 1} вместе с другими данными, указывающими, что наш документ действительно был вставлен.

Поле rev в ответе указывает редакцию документа. Каждый документ имеет поле с именем _rev . Каждый раз, когда документ обновляется, поле _rev документа изменяется. Каждая ревизия указывает на свою предыдущую ревизию. PouchDB поддерживает историю каждого документа (очень похоже на git).

Читать документ

PouchDB предоставляет метод get API для извлечения документа по его идентификатору. Бег:

 movies .get('tdkr') .then(function(doc) { console.log(doc) }) .catch(function (err) { console.log(err) }) 

даст ответ как

 {title: "The Dark Knight Rises", director: "Christopher Nolan", _id: "tdkr", _rev: "3-f8afdea539618c3e8dceb20ba1659d2b"} 

Обновить документ

Допустим, мы хотим добавить поле «год» в наш документ. Вы обновите созданный выше документ, выполнив:

 movies .get('tdkr') .then(function(doc) { doc.year = "2012" // new field console.log(doc._rev) // doc has a '_rev' field return db.put(doc) // put updated doc, will create new revision }).then(function (res) { console.log(res) }) 

При обновлении документа необходимо _rev поле _rev .

Вы должны увидеть похожий вывод в консоли:

 {ok: true, id: "tdkr", rev: "4-7a34189fb8f2e28fe08b666e699755b8"} 

с указанием новой редакции документа.

Удаление документов

Удаление документа в PouchDB просто устанавливает для его свойства _deleted значение true . Вы можете вызвать .remove() чтобы сделать это:

 movies .get('tdkr') .then(function(doc) { return movies.remove(doc) // return the promise }).then(function(res) { console.log("Remove operation response", res) }) 

что эквивалентно выполнению

 movies .get('tdkr') .then(function (doc) { doc._deleted = true return db.put(doc) }) .then(...) 

Удаление базы данных

Вы можете удалить базу данных, вызвав destroy() для объекта db.

 // returns a promise movies.destroy() 

Массовые операции

До сих пор мы работали с отдельными документами в PouchDB. Однако он также предоставляет API-интерфейсы для работы с коллекцией документов. PouchDB предоставляет два метода для массовых операций — bulkDocs() для массовых операций записи и allDocs() для массовых allDocs() чтения.

Метод bulkDocs() довольно прост. Он просто берет массив документов, которые вы хотите вставить в базу данных.

Вставить несколько документов

 // Returns a promise movies.bulkDocs([ { _id: 'easy-a', title: "Easy A", // other attribues }, { _id: 'black-swan', title: 'Black Swan', // ... } ]) 

Образец ответа:

 [ { "ok": true, "id": "easy-a", "rev": "1-84abc2a942007bee7cf55007cba56198" }, { "ok": true, "id": "black-swan", "rev": "1-7b80fc50b6af7a905f368670429a757e" } ] 

Если вы хотите вставить несколько документов, использование массового API, как правило, лучше, чем выполнение нескольких запросов put() . Массовые операции, как правило, выполняются быстрее, чем отдельные операции, поскольку их можно объединить в одну транзакцию (для локального хранилища IndexedDB / WebSQL) или в один HTTP-запрос (для удаленного сервера CouchDB).

Получить несколько документов

Для чтения нескольких документов PouchDB предоставляет метод allDocs() .

 // without {include_docs: true}, only document ids are returned movies .allDocs({include_docs: true}) .then(function (docs) { console.log(docs) }) 

Это быстрый и очень полезный метод. Из документации PouchDB:

allDocs () — это незамеченная звезда мира PouchDB. Он не только возвращает документы по порядку — он также позволяет изменить порядок, отфильтровать по _id, срезы и кости, используя операции «больше» и «меньше» над _id, и многое другое.

По умолчанию документы возвращаются в порядке возрастания _id . Вы можете указать {descending: true} чтобы изменить порядок.

 movies .allDocs({ include_docs: true, descending: true }) .then(...) 

Вы также можете указать параметры startkey и endkey для получения документов в диапазоне. Например, чтобы получить все фильмы, _id начинается с «a» или «b», вы можете выполнить этот запрос:

 movies .allDocs({ include_docs: true, startkey: 'a', endkey: 'c' }) .then(console.log) .catch(console.log) 

Параметры startKey и endKey особенно полезны для разбитых на страницы API.

Go Realtime с ChangeFeeds

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

Тем не менее, следствием этого алгоритма репликации является то, что он позволяет вам просматривать историю базы данных, позволяя вам отвечать на такие вопросы, как

  • Какие изменения были внесены в базу данных с определенного времени?
  • Какие изменения были внесены в конкретный документ?

Это где API changes() входит.

Чтобы получить все изменения с начала времени:

 db.changes({ since: 0, include_docs: true }).then(function (changes) { console.log(changes) }).catch(...) 

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

 db .changes({ since: 'now', live: true, include_docs: true }) .on('change', function (change) { // This is where you can modify UI, based on database change. // change.id contains the doc id, change.doc contains the doc if (change.deleted) { // document was deleted } else { // document was added/modified } }) .on('error', function (err) { // handle errors }) 

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

Вы можете увидеть демонстрацию этого в действии .

Синхронизация: возьмите данные PouchDB за пределы браузера

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

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

 // local database, that lives in the browser's IndexedDB store var localDB = new PouchDB('mylocaldb') // remote CouchDB var remoteDB = new PouchDB('http://localhost:5984/myremotedb') 

Вы можете реплицировать локальные изменения в удаленную БД, написав

 localDB .replicate .to(remoteDB) .on('complete', function () { // local changes replicated to remote }).on('error', function (err) { // error while replicating }) 

Однако, поскольку многие пользователи могут иметь доступ к одной и той же базе данных, более полезно иметь возможность синхронизировать изменения с удаленной БД в браузер. PouchDB поможет вам в этом.

Двунаправленная синхронизация может быть достигнута с помощью одной строки JavaScript:

 // replicates once localDB.sync(remoteDB); 

Или для прямой синхронизации:

 // keeps syncing changes as they occur localDB.sync(remoteDB, {live: true}) 

Следующие шаги

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

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