JSQL - небольшая библиотека для построения Базы Данных на основе Google-таблиц. Теперь и для Google APIv3 😋
🚀 Всего 4 кБ !!!
✅ Можно использовать как набор разнородных файлов из каталога lib/parts/.
✅ Можно использовать файлы каталога lib/all_in_one/ как (ВСЕ В ОДНОМ).
Для использования вам нужно:
-
Создать Google-таблицу,
1.1 Опубликовать ее для всех,
1.2 Создать несколько листов по нужде (лист - это таблица нашей БД)
1.3 Скопировать ее хэш
-
Создать два Google-скрипта
2.1 Всавить содежимое скриптов из папки google_script (https://github.com/AlbertSadykovOfficial/JSQL/blob/master/lib/google_script/) в свои скрипты ИЛИ отсюда: (https://script.google.com/home/projects/1Zuugnn0eKfMxsVGUjc3aXoLhRZAk6EHMPZ-7h2Cqn03RAL4FMkTmd5Ic/edit)
2.2 Вставить в переменную SHEET_KEY (в скриптах) - хэш своей таблицы
2.3 Сохранить изменения.
2.4 Опубликовать скрипт как веб-приложение, сделать его выполнение доступным всем.
2.5 Скопировать ХЭШи 2х таблиц
Параметр | Значение |
---|---|
grab_script_hash | Ключ ссылки на скрипт сбора данных (скрипт должен быть публичным) |
change_script_hash | Ключ ссылки на скрипт изменения данных(скрипт должен быть публичным) |
[table_name.., .. ] | Имя листа в таблице. |
Далле следует скачать библиотеку (к примеру callback min верию) и добавить ее:
<script src="my_project/js/JSQL_callback.min.js"></script>
После этого (после добавления библиотеки) нужно создать объект, необходимый для функционирования бибилиотеки.
Параметры объявления:
let JSQL = new JSQL_constructor(grab_script_hash, change_script_hash, [table_name_1, table_name_2, ...]);
Пример:
let JSQL = new JSQL_constructor('AKfycbx_aLxhvDyzScnCWIfX4jyl4dvLpwvJovqwxYZ2D8ybixzE-mhKulJ5vwBj1KsoFqzjAw',
'AKfycbyYYihG8l7QthbD8Pcu6M9jYtyv57Q9KWM15iIQhFKEJL06ed7GKo5SCaXzS1_pGxeaDg',
['main', 'members', 'sandbox']);
В терминологии библиотеки:
- Google-Таблица -> База данных.
- Отдельные Листы Google-Таблицы -> таблицы Базы данных Т.е. далее, к примеру, table_name - это не имя Google-таблиц, а имя листа Google-таблицы.
Стоит понимать, что это библиотека некий эмулятор взаимодействия с БД. Обычно БД находится на одном компьютере с серверным скриптом и время общения между ними-миимально. У нас же другой вариант - мы получаем данные с сервера, обрабатываем их у себя и отправляем данные обратно... Давольно немало времени тратится на передачу данных в одну сторону, а потом в другую. Это, конечно, порождает проблемы с синхронизацией данных. Если данные меняются каждые несколько секунд - не проблема. Но если период обновления около секунды - это проблема.
Отсюда первая рекомендация - не используйте библиотеку для высоконагруженных приложений, она скорее для простых приложений или для демострации возможности приложения без поднятия сервера (самое то для начинающих).
Вторая рекомендация (для async)- не вызывайте кучу команд сразу в одной функции, особенно это касается последовательности: SELECT -> INSERT или UPDATE или DELETE -> а потом опять SELECT. Пока данные вставляются вызов SELECT во 2й раз не успеет считать новые данные, он скорее всего считает старые. Лучше делать небольшие функции с 1м вызовом SELECT в начале, а затем нужной нам функцией -> Вставить, Обновить или Удалить. Тогда времени должно хватить. Также лучше пытайтесь изменять (доабвлять, удалять) данные одной командой вместо нескольких, для этого есть специлальный синтаксис.
Пример плохого применения:
INSERT('sandbox', [['username', user1],['level', level1],['say', says1]]);
INSERT('sandbox', [['username', user2],['level', level2],['say', says2]]);
INSERT('sandbox', [['username', user3],['level', level3],['say', says3]]);
Пример хорошего применения, лучше 1 команда, чем предыдущие 3.
INSERT('sandbox', [
['username', [user1, user2, user3]],
['level', [level1, level2, level3]],
['say', [says1, says2, says3]]
]);
Есть 2 варианта библиотеки, они оличаются функциям приема данных:
- Варинат callback (Promise)
- Вариант async/await (!!! ВНИМАНИЕ: НЕ РАБОТАЕТ С APIv3 (на 12.10.2021), Планируется устраниенеи этой проблемы !!!)
Функция извлечения данных из БД периодически по каким-то причинам не срабатывает. В таком случае я выбрасываю ошибку. Вы можете отловить эту ошибку и снова попробовать сделать запрос на получение данных. В callback-варианте я запрашиваю данные 1 раз целиком и потом с ними работаю. Все команды в callback-фунции оперируют данными, которые мы получили на вход функции. Напротив, в варианте async/await для любой операции кроме INSERT требуется получение данных от БД. Соответсвенно, при использовании async/await кол-во обращений к БД повышается, а значит, повышается и частота возникновния ошибок. Поэтому я расскажу о callback-варианте в первыую очередь, а что выбрать - реать вам.
Чтобы получить данные из google-таблицы, следует вызвать функцию queryJSQL()
. Функция выполнит асинхронный запрос к таблице и вернет результат в callback-функцию, которую мы указали. Результатом функции при успешном выполнении является массив коллекций. При ошибке - ошибка, поэтому, чтобы не упал код можно поставить try/catch.
Принимающая callback-функция должна иметь только 1 аргумент (data)!
Параметры:
Параметр | Значение |
---|---|
error_f | callback-функция при ошибке |
success_f | callback-функция при успешном получении данных |
table_name | имя таблицы (листа) из которой извлекаем данные |
Синтаксис:
queryJSQL(error_f, success_f, table_name);
Пример:
queryJSQL(console.log, select_data, 'main');
queryJSQL(custom_log, update_data, 'members');
queryJSQL(custom_alert, insert_data, 'sandbox');
Дальше массив данных приходит в указанную нами функцию, через некоторое время. ВАЖНЫЙ МОМЕНТ: Данные приходят как текст. Его необходимо преобразовать в JSON вначале функции для нормальной работы всех функций:
function select_data(значения_из_БД)
{
значения_из_БД_JSON = JSON.parse(значения_из_БД);
...наш код
...тут можно использовать функции, приведенные ниже
}
Синтаксис:
SELECT('значения из БД',
[имя_колонки_1, имя_колонки_2,..., имя_колонки_N],
[
[имя_колонки, ВЫРАЖЕНИЕ, [значение_1, значение_2, ..., значение_N]],
[имя_колонки, ВЫРАЖЕНИЕ, [значение_1, значение_2, ..., значение_N]],
...
]);
Пример:
function select_data(data)
{
data = JSON.parse(data);
user_mail = '[email protected]';
from_date = new Date('1980-01-01');
to_date = new Date('2099-01-01');
result = SELECT(data, ['mail', 'city', 'date', 'doctype'],
[
['mail','!=', user_mail],
['date', 'from', [from_date]],
['date', 'to', [to_date]],
]);
}
Синтаксис абсолютно такой же как и у SELECT:
COUNT('значения из БД',
[имя_колонки_1, имя_колонки_2,..., имя_колонки_N],
[
[имя_колонки, ВЫРАЖЕНИЕ, [значение_1, значение_2, ..., значение_N]],
[имя_колонки, ВЫРАЖЕНИЕ, [значение_1, значение_2, ..., значение_N]],
...
]);
Синтаксис:
INSERT('таблица', [
[ имя_колонки, [значение_1, значение_2, ..., значение_N]],
[ имя_колонки, [значение_1, значение_2, ..., значение_N]],
...
]);
Пример:
function insert_data(data)
{
block = document.getElementById('insert_example_block');
user = block.getElementsByClassName('UserName')[0].value;
level = block.getElementsByClassName('level')[0].value;
says = block.getElementsByClassName('say')[0].value;
INSERT('sandbox', [
['username', user],
['level', level],
['say', says]
]);
}
Синтаксис:
UPDATE('значения из БД','таблица', [
[ имя_колонки, новое_значение],
[ имя_колонки, новое_значение],
...
[,
[
[имя_колонки, ВЫРАЖЕНИЕ, [значение_1, значение_2, ..., значение_N]],
[имя_колонки, ВЫРАЖЕНИЕ, [значение_1, значение_2, ..., значение_N]],
...
[));_
Пример:
function update_data(data)
{
data = JSON.parse(data);
block = document.getElementById('update_example_block');
set_column = block.getElementsByClassName('column')[0].value;
set_value = block.getElementsByClassName('set_input')[0].value;
where_column = 'ID';
where_value = block.getElementsByClassName('where_input')[0].value;
UPDATE(data, 'sandbox', [
[set_column, set_value],
],
[
[where_column, '=', where_value],
]
);
}
Синтаксис:
DELETE('значения из БД', 'таблица', [
[имя_колонки, ВЫРАЖЕНИЕ, [значение_1, значение_2, ..., значение_N]],
[имя_колонки, ВЫРАЖЕНИЕ, [значение_1, значение_2, ..., значение_N]],
...
]);
Пример:
function delete_data(data)
{
data = JSON.parse(data);
block = document.getElementById('delete_example_block');
column = block.getElementsByClassName('column')[0].value;
value = block.getElementsByClassName('input_section')[0].value;
DELETE( data, 'sandbox', [
[column, 'LIKE', value]
]);
}
Примеры использования (для async/await-варианта) (!!! ВНИМАНИЕ: НЕ РАБОТАЕТ С APIv3 (на 12.10.2021), Планируется устраниенеи этой проблемы !!!)
Другой вариант использования - использование библиотеки с функционалом async/await. Напомню особенность таких функций:
- await может использоваться только внутри async-функций, это обязывает писать async перед всеми функциями, в которых намеривается использование библиотеки.
При работе с async/await использование очень напоминает стандартное поведение при программировании на серверных языках (к примеру, php). Мы так же получаем данные в переменную, потом их последовательно обрабатываем, а не как в варианте с callback получаем все и сразу, а потом уже разбираем полученные данные.
Почему тогда не использовать этот вариант, раз он так хорош? Причины:
- Он рабаотет не так стабильно как вариант с callback, периодически выдавал ошибку
CORS Missing Allow Origin
, возможно из-за очень частых запросов, так или иначе, варинат с callback не так часто ругался, но все же иногда бывает. - Требует ставить
async
перед всеми функциями, которые будут обращатсья к БД (хотя это не такая весомая причина) - Не успевает. В следующем примере идет SELECT, затем INSERT и UPDATE, а затем опять SELECT. Стоит ожидать, что данные при 1м вызове SELECT и 2м будут отличаться, но это не совсем так...
- Причина 1. В логике отправки данных. Отправка UPDATE и DELETE состоит из 2х этапов:
- 1й - Выбрать id строк через SELECT, которые удовлетворяют условию.
- 2й - Отправить данные через iframe Так вот, 1й этап хорошо отрабатывает, async/await срабатывают, но вот окончания 2го этапа функция await не дожидается, пока данные заносятся, мы уже снова можем вызвать SELECT и получить устаревшие данные.
- Причина 2. Google-таблице нужно немного времени, чтобы отобразить изменения, поэтому если мы в короткий промежуток времени (каким является время выполнения кода) вызовем несколько функций подряд, то таблица еще не отобразит изменения и мы получим старые данные. Поэтому между операциями считывания (SELECT) нужно делать паузы и, НАВЕРНОЕ, стоит использовать их больше одной за функцию.
- Причина 1. В логике отправки данных. Отправка UPDATE и DELETE состоит из 2х этапов:
В оправдание async:
- В callback варианте SELECT вообще не обращается к БД, а работает как фильтр над принятми данными, поэтому он тоже при вызове SELECT видит не новые данные скаолько бы мы INSERT и UPDATE не вызывали.
Изменения синтаксиса по сравнению с callback-вариантом:
- Теперь мы не передаем в функции массив данных, теперь параметры функций еще больше схожи с реальным синтаксисом SQL.
Синаксис :
SELECT('Имя_таблицы',
[имя_колонки_1, имя_колонки_2,..., имя_колонки_N],
[
[имя_колонки, ВЫРАЖЕНИЕ, [значение_1, значение_2, ..., значение_N]],
[имя_колонки, ВЫРАЖЕНИЕ, [значение_1, значение_2, ..., значение_N]],
...
]);
Пример функции:
async function async_example()
{
// Пример Извлечения данных и подсчета
let data = await SELECT('sandbox',
['UserName', 'level'],
[
['id', '!=', '10']
]);
let count= await COUNT('sandbox', [['id', '!=', '10']]);
console.log(data, count);
// Вносим данные
INSERT('sandbox', [
['username', 'Аль-Ка-Пони'],
['level', '35'],
['say', 'Макфа']
]);
// Обновляем данные
await UPDATE('sandbox', [
['level', 'oVER 999']
],
[
['Username', 'LIKE', 'Имба'],
]);
// Получим больше данных и выведем их в таблицу
data = await SELECT('sandbox',
['ID' ,'UserName', 'level', 'say'],
[
['id', '!=', '10']
]);
// Как видно, изменения не успели примениться
// 1й вызов равен 2му, несмотря на то что между ними мы изменяли данные.
console.log(data);
// Эта функция выводит данные в таблицу, ее нет в составе библиотеки
print_result_as_array(
['ID' ,'UserName', 'level', 'say'],
['ID', 'Имя', 'Уровень', 'Сказал'],
data
);
}
- Не надо вызывать специальную функцию
queryJSQL()
для вызова функции - Более удобный и интуитивный синтаксис, схожий с оригинальной работой с БД.
- Менее стабилен - главный минус.
- Требует перед всеми функциями, которые его используют, писать async