GunDB: графическая база данных на JavaScript
Часть 1: Основы оружия
Первоначально опубликовано на Середина
Недавно я играл с GunDB и я хотел поделиться с вами тем, что я узнал до сих пор. GunDB, также известный как Gun, также известный как Gun.js, — это больше, чем просто база данных графов. Это группа проектов, направленных на упрощение масштабирования, повышение безопасности данных, снижение затрат и расширение возможностей разработчиков приложений.
Однако в этой статье я буду рассматривать Gun исключительно как базу данных. Сосредоточившись только на аспекте базы данных, я могу сделать статью менее перегруженной и исследовать другие аспекты в следующих статьях по мере того, как я продвигаюсь вперед и узнаю больше.
Все примеры кода для этой статьи доступны на Гитлаб
Введение
Вообще говоря, база данных — это часть программного обеспечения, которое вы устанавливаете на свой компьютер или на удаленный сервер для хранения данных. Данные могут храниться как на диске, так и в памяти. Базы данных бывают разных видов: реляционные, ориентированные на документы, ключ-значение или основанные на графах. Вот некоторые примеры:
- Реляционные: MySql, PostgreSQL, SQL Server.
- Документоориентированные: MongoDB, CouchDB
- Ключ-значение: Redis, LevelDB
- На основе графа: Neo4j, OrientDB
Gun, в отличие от других баз данных, не требует установки бинарного файла. Пистолет написан на JavaScript, что означает, что вы можете использовать его везде, где работает JavaScript. Начать работу с Gun так же просто, как загрузить файл JavaScrip.
Начиная
Самый простой способ начать работу с Gun — загрузить его в виде одного файла JavaScript. Вы можете загрузить последнюю уменьшенную версию по следующему адресу:
Затем вы можете загрузить его в HTML-файл:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="gun.min.js"></script>
<title>Gun Basics</title>
</head>
<body>
</body>
</html>
Затем вы можете открыть HTML-файл в браузере и использовать консоль для взаимодействия с Gun. В большинстве браузеров вы можете щелкнуть правой кнопкой мыши на странице и выбрать «Проверить элемент», чтобы открыть консоль. Ниже приведен пример, который вы можете использовать для создания записи автомобиля и печати его марки:
const db = window.Gun();
const car = db.get("123").put({
make: "Toyota",
model: "Camry",
});
car.once(v => console.log(v.make));
В приведенном выше фрагменте мы сначала создаем экземпляр Gun. Затем мы ссылаемся на пустой узел, используя get
метод с 123
ключ. Затем мы добавляем некоторые данные, используя put
метод и простой объект JavaScript. И, наконец, мы используем тот же ключ, чтобы получить запись (узел) и, используя once
методом мы считываем значение. Обратите внимание, что простой объект автоматически преобразуется в узел Gun. Я объясню каждый метод более подробно позже в разделе «Основы». Ниже приведена диграмма, которая поможет вам лучше понять, что происходит:
Создание записи карты
Теперь давайте поэкспериментируем с Gun, используя Node. Сначала создайте каталог и установите Gun, используя npm
:
Если у вас не установлены Node и npm, инструкции можно найти в Приложении 1.
cd ~/Desktop && mkdir gun-demo && cd $_
npm init -y
npm i gun -S
Затем создайте файл и назовите его main.js
и добавьте следующее (очень похоже на приведенное выше):
main.js
const db = require('gun')();
const car = db.get("123").put({
make: "Toyota",
model: "Camry",
});
car.once(v => console.log(v.make));
Затем выполните файл с помощью node main
и вы должны быть в состоянии видеть Toyota
залогинился в консоли. Кроме того, вы можете использовать Gun в Node’s REPL, сначала вызвав node
в терминале, а затем выполните следующее:
const db = require('gun')();
Затем вы можете взаимодействовать с Gun, используя db
объект. Чтобы закрыть REPL, используйте ctrl + c
дважды.
Основы оружия
В следующих разделах я собираюсь показать вам основы Gun и изучить его основные методы создания, чтения, обновления и удаления записей. Я также покажу вам, как создавать наборы и отношения. Я также кратко упомяну о различных способах, доступных для подписки на обновления записей. Обратите внимание, что я буду использовать термины «запись» и «узел» взаимозаменяемо, поскольку Gun — это графовая база данных, а записи представлены в виде узлов.
CRUD
Для создания записи можно использовать get
метод в сочетании с put
:
const entry = db.get('8899').put({
uuid: '8899',
some_prop: 'some value',
});
В приведенном выше фрагменте мы используем get
метод для создания ссылки на узел с помощью 8899
ключ. Затем мы используем put
для добавления данных в узел с помощью простого объекта JavaScript. Простой объект автоматически преобразуется в узел Gun. Обратите внимание, что если данный ключ уже существует, добавленные данные могут переопределить существующие данные. Более подробно я расскажу об обновлениях в разделе «Обновление». На приведенной ниже диаграмме показано, как данный ключ в базе данных указывает на узел:
Создание узла
Небольшое примечание о ключах: вы всегда должны использовать уникальные ключи. Вы можете использовать uuid для общих узлов и хешированные строки в сочетании с читаемыми строками для целей индексации. Пространство имен очень важно при работе с Gun, потому что все данные существуют в глобальном пространстве. Вы можете посмотреть на это расширение чтобы помочь вам создать пространство имен для ваших ключей.
Мы можем использовать get
метод поиска узла по ключу. Затем мы можем подписаться на него, используя on
или же once
. С использованием on
вы можете получать обновления по мере их появления, но once
выдает текущее значение только один раз:
const node = db.get("1122").once(v => console.log(v));
Вы можете продолжать цепочку get
звонки. Если ссылки не существуют, они создаются. В противном случае возвращается значение по указанному пути. Давайте посмотрим на пример. Ниже мы создаем узел с именем node1
с некоторыми свойствами:
const node1 = db.get("3344").put({
name: "node1"
});
node1.get("doc1").put({
name: "doc1",
});
node1.get("doc1").get("sub_doc").put({
name: 'sub_doc',
});
Например, чтобы получить доступ к значению node1.doc1.sub_doc
мы можем использовать get
цепочку и прочитайте значение, используя once
:
node1.get('doc1').get('sub_doc').once(v => console.log(v));
На приведенной ниже диаграмме показано, как выглядят узлы и отношения:
Связь между тремя узлами
Несколько замечаний по поводу приведенных выше фрагментов:
- Когда вы используете
put
, если ключ не указан явно, ключ (также известный как душа) генерируется автоматически. В дополнениеdb
объект также сохранит ссылку на этот ключ. Например, когда мы сделалиnode1.get("doc1").put
, за кулисами был сгенерирован новый узел с уникальным ключом. Мы можем видеть, что если мы зарегистрируем значениеnode1.doc1
и посмотри на внутр._
имущество:
Внутреннее значение, показывающее автоматически сгенерированный ключ
Теперь, если вы знаете этот уникальный ключ (душу) узла, вы можете напрямую получить доступ к узлу, на который он указывает, из db
объект:
db.get('unique_key')...
- То же самое верно и для
node1.doc1.sub_doc
:
Внутреннее значение, показывающее автоматически сгенерированный ключ
- Также обратите внимание, что
node1.doc1
имеет свойство, называемоеsub_doc
который содержит только ссылку наsub_doc
узел:
Сохранение ссылки на другой узел
Учитывая приведенные выше пояснения, ниже представлена более точная картина узлов и их взаимосвязей:
Узлы и отношения
Обратите внимание, как два других автоматически сгенерированных уникальных ключа (души) указывают на вновь созданные узлы непосредственно из db
.
Чтобы обновить запись, вы можете использовать put
метод:
db.get('9871').put({ name: 'Tom',});
Обратите внимание, что все обновления являются частичными обновлениями. В приведенном выше фрагменте обновляется только поле имени. Пока у вас есть ссылка на узел, вы можете просто использовать put
для обновления значений. Давайте посмотрим на другой пример. Ниже приведена настройка:
const n1 = db.get('5416')
.put({
name: 'n1',
prop: '...',
doc1: {
prop: '...',
},
});
const n2 = db.get('8899')
.put({
name: 'n2',
doc2: {
prop: '...',
}
});
n1.get('related_to').put(n2);
В приведенном выше фрагменте мы создаем два узла: n1
а также n2
. n1
узел имеет некоторые свойства name
, prop
а также doc1
. doc1
Свойство определяет вложенный документ, который автоматически превращается в узел и на который ссылается автоматически сгенерированный ключ.
Затем мы создаем n2
узел, который имеет два свойства name
а также doc2
похожий на n1
. И, наконец, мы создаем свойство на n1
называется related_to
это указывает на n2
. На приведенной ниже диаграмме показаны отношения:
Настройка отношений для демонстрации обновлений
Теперь давайте посмотрим, как мы можем выполнить следующие обновления:
- Обновлять
n1.doc1.prop
к другому значению:
n1.get('doc1').put({ prop: 'other value'});
- Обновить что
n1
относится к
n1.get('related_to').put(db.get('9185').put({ new_prop: 'some value', }));
В приведенном выше фрагменте мы полностью меняем то, что n1
указывает, создавая новый узел. Обратите внимание, что n2
ничего не изменилось, мы просто обновили related_to
указатель.
- Добавьте новые свойства в
n2
сначала ссылаясь наn1
:
n1.get('related_to').put({
new_stuff: 'some value',
other_stuff: 'some value',
})
Во фрагменте выше new_stuff
а также other_stuff
будет добавлено к тому, что уже существует на n2
. Если свойство уже существует, оно будет перезаписано, в противном случае будут созданы новые свойства.
Удаление работает немного по-другому в Gun. Вместо удаления записи мы можем сделать ее невозможной для обнаружения, установив указатель на null
:
db.get('8809').put(null);
В приведенном выше фрагменте мы используем get
найти ссылку на 8809
ключ. Затем мы устанавливаем его на null
. Пока у вас есть ссылка на узел или свойство, вы можете использовать put
установить их на null
. Подробнее об удалении можно прочитать по следующим ссылкам:
Вот краткое объяснение, взятое непосредственно из ответа StackOverflow:
Удаление в GUN работает как Mac OSX, Windows или Linux. Обнуление говорит каждой машине «Поместить эти данные в корзину». Причина, по которой это полезно, заключается в том, что это позволяет вам изменить свое мнение об удалении чего-либо, чтобы вы могли восстановить его позже, если хотите. (Восстановление удаленного контента/файлов происходит МНОГО, но большинство людей об этом не задумываются).
Наборы
Gun позволяет группировать несколько записей и добавлять их в набор. Набор оружия — это математический набор с уникальными неупорядоченными элементами. Допустим, у нас есть два узла, и мы хотим создать для них группу. Сначала мы создаем узел группы, а затем используем set
чтобы добавить к нему другие узлы или простые объекты. Обратите внимание, что простые объекты, как и операции обновления, будут автоматически преобразованы в узлы Gun:
const group = db.get('8871'); // create a group node
group.set(n1);
group.set(n2);
В настоящее время group
имеет две записи, n1
а также n2
. Вы также можете добавить в набор простые объекты:
const group = db.get('8871');
group.set({
title: 'hello'
});
group.set({
title: 'world'
});
В этом случае Gun автоматически создаст узлы из простых объектов. На диаграмме ниже показан набор и узлы, на которые он указывает:
Визуализация набора с двумя записями
Отношения
Моделирование реального мира сводится к выявлению взаимосвязей и реализации их в базе данных. Базы данных графов, естественно, хороши для выражения отношений. В этом разделе я собираюсь показать вам, как создавать отношения между узлами.
Самый простой способ создания отношения, как мы уже видели, — это использование следующего шаблона:
node1.get('related_to').put(node2)
или явное создание отношения при создании узла:
const node1 = db.get('8891').put({
uuid: '8891',
name: 'node1',
related_to: {
uuid: '9911',
name: 'node2',
},
});
Во фрагменте выше related_to
автоматически превращается в узел Gun, а ссылка сохраняется в node1
. Затем вы можете получить доступ к связанному узлу с помощью node.get('related_to')
.
Теперь, если вы хотите добавить свойства отношения, вы можете создать промежуточный узел и добавить свойства отношения и ссылку внутри промежуточного узла:
node1.get('related_to').put({
property: "value",
property2: "value",
});
node1.get('related_to').get('node').put(node2);
На приведенной ниже диаграмме показаны отношения:
Создание отношения с помощью узла свойства
Как вы можете видеть на диаграмме выше, related_to
узел указывает на node2
сквозь node
свойство промежуточного узла. Затем вы можете получить доступ node2
с node1.get('relate_to').get('node')
.
Подписка
Узлы Gun являются наблюдаемыми, что означает, что они излучают значения с течением времени. Вы можете подписаться на узлы Gun, используя on
или же once
. С использованием on
вы можете получать обновления по мере их появления, если только вы не отмените подписку. once
метод извлекает только текущее значение и не подписывается на будущие обновления.
Итерация по записям
Учитывая набор записей, вы можете перебирать их, используя map
:
myset.map().once(v => console.log(v));
Приведенный выше фрагмент будет регистрировать каждую запись в myset
однажды. Он также будет добавлять записи с течением времени, но только один раз.
Вот еще шаблоны, которые можно использовать (взять прямо из документации):
myset.map().on(cb)
: подписывается на изменения каждой записи и наmyset
по мере добавления новых записей в будущем.myset.map().once(cb)
: получает каждую запись один раз, включая те, которые добавляются с течением времени.myset.once().map().on(cb)
: получает список записей один раз, но подписывается на изменения в каждой из них.myset
а не добавленные позже записи.myset.once().map().once(cb)
: получает список записей один раз, получает каждую запись вmyset
только один раз, а не добавленные позже.
Вывод
GunDB меняет наше представление о базах данных и постепенно переводит нас к новой парадигме. Gun и связанные с ним проекты имеют множество аспектов, сильно отличающихся от классических централизованных моделей. Вы можете столкнуться с трудностями в изучении оружия, если вы только начинаете. Во-первых, потому что Gun — молодой проект, и вы должны ожидать изменения API. А во-вторых, вам может быть сложно разобраться в документации.
Я надеюсь, что эта серия статей поможет вам (и мне) лучше понять GunDB и послужит дополнительным руководством к ранее существовавшим руководствам. Вы можете получить доступ ко всей официальной документации и руководствам на
Приложение 1
Самый простой и наиболее последовательный способ установки Node — через диспетчер версий, например NVM. Сначала установите NVM, используя следующее:
curl -o- | bash
Затем проверьте свой файл «профиль», чтобы увидеть, были ли добавлены следующие записи:
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
Затем перезапустите терминал и убедитесь, что вы можете получить вывод для nvm --version
. После этого просто запустите nvm install 8
для установки последней версии Node 8. После этого запустите node -v
а также npm -v
чтобы убедиться, что и Node, и Npm доступны.