Базы данных: введение, часть десятая

Илья Тетерин
2011-11-23

(use arrow keys or PgUp/PgDown to move slides)

HTML5: WebStorage

Базы у вас под рукой ...

До HTML5

Предыдущая версия HTML4.0 была сформирована в 1997-ом году.

Два способа хранения - база данных на сервере или набор COOKIE(короткий текст) на стороне клиента.

Изменение "состояния" - послать запрос на сервер, изменить состояние на сервере, получить ответ, изменить cookie.

Пример: обычная, традиционная "корзина" на веб-сайте ...

Сложности

Что произойдет, если пользователь откроет две сессии одновременно?

Зачем? Дабы сравнить вариант вылета в субботу и в воскресенье.

Что сломается? И почему?

HTML5:WebStorage

Спецификация Web Storage - находится в разработке.

Вводятся два новых способа хранения информации у клиента:

SessionStorage
key-value хранилище на уровне "окно браузера"-хост
LocalStorage
key-value устойчивое хранилище на уровне браузер-хост

Определение Storage

interface Storage {
  readonly attribute unsigned long length;
  DOMString? key(unsigned long index);
  getter DOMString getItem(DOMString key);
  setter creator void setItem(DOMString key, DOMString value);
  deleter void removeItem(DOMString key);
  void clear();
};

Элементы интерфейса Storage

length
количество пар в хранилище
key(n)
имя ключа в позиции n, null если n >= length
getItem(k)
возвращает значение пары для ключа k, или null, если ключ неизвестен
setItem(k,v)
добавить новую пару, если ключ неизвестен, иначе обновить значение, NB: может кидать QuotaExceededError
removeItem(k)
удаляет пару, если ключ известен
clear()
очищает все пары из хранилища

Спецификация

[NoInterfaceObject]
interface WindowSessionStorage {
  readonly attribute Storage sessionStorage;
};
Window implements WindowSessionStorage;

[NoInterfaceObject]
interface WindowLocalStorage {
  readonly attribute Storage localStorage;
};
Window implements WindowLocalStorage;

Как использовать

<script>
    window.sessionStorage.setItem("session.1", "33");
    window.localStorage.setItem("local.1", "38");
</script>

Событие storage

[Constructor(DOMString type, optional StorageEventInit eventInitDict)]
interface StorageEvent : Event {
  readonly attribute DOMString key;
  readonly attribute DOMString? oldValue;
  readonly attribute DOMString? newValue;
  readonly attribute DOMString url;
  readonly attribute Storage? storageArea;
};

dictionary StorageEventInit : EventInit {
  DOMString key;
  DOMString? oldValue;
  DOMString? newValue;
  DOMString url;
  Storage? storageArea;
};

Элементы события

key
сам ключ
oldValue
значение до события
newValue
новое значение
url
полный урл источника события
storageArea
к какому storage пришло событие (localStorage, sessionStorage)
var logged = "key:" + e.key + ", newValue:" + e.newValue
    + ", oldValue:" + e.oldValue + ", url:" + e.url
    + ", storageArea:" + e.storageArea;

Как этим пользоваться?

localStorage - устойчивое и переживет перезапуск браузера...

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


Попробуем пример: в хранилище посещаемость лекций, хочу видеть графики ...

Данные в виде ключ-значение: (data.X;N), где X - номер лекции, N - количество студентов

        ("data.01", "33")
        ("data.02", "34")
        ("data.03", "27")
        ...
    
getAllKeys = function() { // все ключи в массив
    var st = window.localStorage;
    var data = [];
    for (var i = 0; i < st.length; i++)
        data.push(st.key(i));
    return data;
};
isData = function(key) { // нас интересуют только data. ключи
    return key && key.startsWith('data.');
};
loader = function(key) { // по ключу - значение
    return window.localStorage.getItem(key);
};

loadData = function () {
    // взять ключи, отобрать нужные, отсортировать, взять значения
    return getAllKeys().filter(isData).sort().map(loader);
    // return getAllKeys().filter(isData)
    //   .sort().map(window.localStorage.getItem);
};
    <script src="./files/jquery.js"></script>
    <script src="./files/jquery.sparkline.js"></script>

    drawGraphs = function (items) {
        $('.dynamicsparkline').sparkline(items,
           {height:200, width:200, lineWidth: 3, chartRangeMin: 0});

        $('.dynamicbar').sparkline(items,
           {type: 'bar', barColor: 'green', height:100, barWidth:10, chartRangeMin: 0});

        var t = document.getElementById('plaindata');
        t.innerHTML = '';
        for (var i = 0; i < items.length; i++) {
            var d = document.createElement('div');
            d.innerHTML = '' + i + ': ' + items[i];
            t.appendChild(d);
        }
    };

    $(document).ready(function() {
        drawGraphs(loadData());
    });

    <table border="1"><tr>
        <td><span class="dynamicsparkline">Loading..</span></td>
        <td><span class="dynamicbar">Loading..</span></td>
        <td><span id="plaindata">Loading..</span></td>
    </tr></table>

Запустить пример ...

cd (folder with samples)
python -m SimpleHTTPServer
// это простой web server, который отдает файл из каталога
// по http://localhost:8000/

Потом открыть страницу примера

... или пример на fluffypulser.ru ...

... но у нас же есть событие storage ...

Слушатель событий

report = function(val) {
    var incomingRow = document.createElement('pre');
    incomingRow.innerHTML = val;
    document.getElementById("container").appendChild(incomingRow);
};

displayStorageEvent = function(e) {
    var logged = "key:" + e.key + ", newValue:" + e.newValue
    + ", oldValue:" + e.oldValue + ", url:" + e.url
    + ", storageArea:" + e.storageArea;

    report(logged);
    this.drawGraphs(this.loadData());
};

window.addEventListener("storage", displayStorageEvent, true);

Источник событий

newLine = function() {
  var k = document.getElementById("key").value;
  var v = document.getElementById("val").value;
  window.localStorage.setItem(k, v);
}

removeLine = function() {
  var k = document.getElementById("key").value;
  window.localStorage.removeItem(k);
}

key: <input type="text" name="key" id="key"/>
val: <input type="text" name="val" id="val"/>
<input type="button" value="new" onclick="newLine()"/>
<input type="button" value="remove" onclick="removeLine()"/>
<a href="st.insert.html">reload</a>

Запустить пример 2 ...

cd (folder with samples)
python -m SimpleHTTPServer
// это простой web server, который отдает файл из каталога
// по http://localhost:8000/

Потом открыть страницу слушателя (на fluffypulser.ru) ...

Потом открыть страницу источника (на fluffypulser.ru)

И начать добавлять / удалять данные ...

Полезно: на странице "источник" открыть View / Developer / Developer Tools и там Resources / Local Storage ...

Что получилось?

Данные "кешируются" у пользователя ...

Страница может отображать данные стартуя с кэша - еще до получения данных от сервера.

Обновления одной страницы разлетаются в несколько других страниц (Model-View-Controller).

Что можно сделать?

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

sessionStorage

Код и принципы работы - аналогичны.

Хранилище живет только в одной закладке браузера.

Пользователь может работать с разными наборами пар в разных окнах (what-if сценарии).

В пределах одной закладки пользователь может уходить и возвращаться на страницу.

Пример: session storage (на fluffypulser.ru)

Как хранить JSON объект в storage?
Сериализуй в строку и храни.

var obj = ...;
window.localStorage.setItem('myObj', JSON.stringify(obj));
...
var myObj = eval(window.localStorage.getItem('myObj'));

Поддержка в каких браузерах?

if (typeof(window.localStorage) === "undefined") {
  // "Your browser does not support HTML5 storage";

Key/Value - слишком просто, что дальше?

w3.org: Web SQL Database - набор APIs для клиент-сайд баз использую SQL...

db.readTransaction(function (t) {
  t.executeSql('SELECT title, author FROM docs WHERE id=?', [id],
    function (t, data) {
      report(data.rows[0].title, data.rows[0].author);
    }
  )}); 

Спецификация "заброшена" с комментарием: Все реализации используют Sqlite - этого мало для реализации стандарта ...


w3.org: Indexed Database API - хранить много объектов локально - поиск по ключам, множественные значения для ключа, порядок обхода ключей ... NoSQL ...

Статьи на habrahabr.ru по тэгам IndexedDB и web sql database ...

Литература

Книга: Pro HTML5 Programming By Peter Lubbers, Brian Albers, and Frank Salim.
site / перевод на ozon.ru / примеры кода

Html5Rocs: site / slides

Habrahabr.ru: поиск по HTML5

w3.org спецификации:
Web Storage,
Web SQL Database
и Indexed Database API

Итого

Начиналось все с одного компьютера

потом разнесли на терминал - сервер

потом сервер базы - сервер бизнес-логики - терминал

потом добавился вебсервер

... а теперь база прокидывается прямо в браузер.

Использование локальной базы - открывает новые возможности - перенос части данных и логики в клиента.

Но еще предстоит понять как правильно и с пользой это использовать.

Сложности - распределенная система будет требовать синхронизации тысяч клиентов с центральным хранилищем ...

Вопросы?

Домашка, пояснительная записка и Markdown

Markdown (маркдаун) - язык разметки текстовых документов. Происходит из разметки почтовых сообщений.

Пишете текст с простой разметкой, пропускаете через процессор, получаете XHTML документы.

Много реализаций - например GitHub форматирует им сообещиния и пользовательские комменты ..., stackoverflow.com - тоже.

Параграф текста. Из нескольких предложений.
А этот _текст_ будет наклонным(<em>...).
А этот **текст** будет выделенным (<strong>...).
1. нумерованный (<ol><li>...)
2. список строк

    А с четырех пробелов начинается кусок код (<pre><code>...)
База данных
===========

Цель работы
-----------

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

В работу входят задания на:

* создание _key-value_ хранилища
* реализация шардирования и партиционирования
* работа с **распределенным** состоянием
* сериализация объектных данных для стримового представления

Ожидаемые результаты
--------------------

1. Запускающийся код
2. Текстовое описание
3. Описание архитектуры
<!DOCTYPE html>
<html><head><meta charset="utf-8"></head><body>
<h1>База данных</h1>

<h2>Цель работы</h2>

<p>Цель данной работы познакомить студентов с архитектурой современных баз данных,
дать навыки создания и использования NoSQL хранилищ. </p>

<p>В работу входят задания на:</p>

<ul>
<li>создание <em>key-value</em> хранилища</li>
<li>реализация шардирования и партиционирования</li>
<li>работа с <strong>распределенным</strong> состоянием </li>
<li>сериализация объектных данных для стримового представления</li>
</ul>

<h2>Ожидаемые результаты</h2>

<ol>
<li>Запускающийся код</li>
<li>Текстовое описание </li>
<li>Описание архитектуры</li>
</ol>
</body></html>

База данных

Цель работы

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

В работу входят задания на:

...