Представляем badcube.js - крошечную базу данных Node.js, о которой никто не просил
Никто не должен воспринимать это всерьез.
Репозиторий GitHub для этого проекта
Разговор с коллегой подсказал мне кое-что о программировании, о котором я не задумывался, пока он это не сказал: каждый файл - это просто набор строк. Вы должны пропустить его через лексер и синтаксический анализатор, чтобы заставить его делать что-нибудь, но в конечном итоге это всего лишь строка. В свете этого я начал думать о том, как база данных sqlite работает с локальными файлами в качестве базы данных. Почему нет эквивалента для noSQL? Как оказалось, их несколько, и один из моих любимых - TinyDB, написанный на Python. Когда я начал теряться в поисках Google, мне в голову пришла мысль, от которой я никак не мог избавиться: почему я не могу создать базу данных файловой системы с помощью Node.js? То, что началось как случайная мысль в пятницу вечером, превратилось в проект выходного дня, в результате которого был создан badcube. Я готов поспорить, что кто-то уже сделал нечто подобное, но для меня это отличный шанс попрактиковаться в работе с модулем файловой системы в Node.
Я должен был подумать, какие входы и выходы в базе данных, прежде чем что-либо писать. Естественно, как веб-разработчик, я склонен тяготеть к файлам JSON, так что я поступил именно так. Я также хотел иметь возможность брать выходные файлы из базы данных и легко импортировать их в любую базу данных на основе документов. В конечном итоге я остановился на массивах JSON, чтобы с файлами было легко работать, и они обладали всеми вышеупомянутыми функциями.
Но где же хранить эти файлы .json? Как поклонник MongoDB, я хотел бы иметь что-то аналогичное коллекциям ... тогда это папка с именем collections
it. Благодаря этому я могу хранить каждую из аналогичных коллекций в собственном автономном файле .json внутри папки и импортировать каждую из них как объекты в память при запуске кода.
Первые шаги в программировании были простыми: читать в файлах. За несколько минут я изучил основы программы чтения коллекций, показанной ниже.
Оглядываясь назад, когда я пишу эту статью, начинать с асинхронной версии readdir
было не самой блестящей идеей, но это было, по крайней мере, началом. Когда .forEach
завершился, я смог отключить глобальный объект collections
и увидеть все импортированные файлы. Написание этого научило меня двум действительно интересным вещам о require
, которых я раньше не знал об этой функции. Во-первых, require является синхронным, что имеет смысл, учитывая, что вы не хотите начинать работу с модулями, которые еще не добавили в файл. Во-вторых, что более интересно, требуемый файл не должен иметь module.exports
или exports
в нем, если вы передаете объект JSON или массив JSON. Это привело меня к необходимости немного глубже изучить, как это на самом деле реализовано в языке, и к отличной статье Фреда К. Шотта.
Это было довольно просто, и я почти совершил ошибку, остановившись здесь. Теперь, когда коллекции были загружены в объект collections
, я понял, что могу просто взаимодействовать с каждой коллекцией так же, как и с любым объектом JavaScript. Это прекрасно, пока мне не придется писать повторяющийся код для базовых операций CRUD. Любой, кто использовал Mongoose или Sequelize, может сказать вам, что работать с моделью - с одинаковыми функциями для каждой коллекции (таблица для всех поклонников SQL) - значительно проще. Я должен встроить ORM-подобную функциональность прямо в базу данных! Теперь все снова интересно.
На этом этапе я подсознательно начал брать на себя обязательство, что это больше не тот проект, над которым я должен работать, а потом откладывать в долгий ящик. Я собираюсь сделать законный Пакет NPM! Ой! Я также могу сделать это отличным способом облегчить моим ученикам концепции баз данных с открытым исходным кодом, конструкторов, ORM и nosql с невероятно короткой настройкой.
Небольшое отступление: оказывается, что Git-LFS и npm adduser
плохо работают. Если вы знаете отличный способ решить эту проблему, оставьте комментарий ниже!
Чтобы каждая коллекция могла иметь доступ к одним и тем же функциям, мне нужно предоставить им один и тот же прототип. Для этого мне нужно создать функцию-конструктор для Model
, которая создает экземпляр нового объекта. Сам конструктор должен содержать информацию о коллекции, местонахождении ее файла (чтобы мы могли читать / писать в него позже) и имя самой модели.
Когда создается экземпляр этого конструктора, я также хочу присоединить его к глобальному объекту. Я переписал readdir
функцию (обратите внимание на изменение readdirSync, чтобы сохранить рассудок), чтобы сделать это.
Обратите внимание также на изменение пути к переменной tmp
, которая представляет коллекцию, когда она переносится в память из файла. Расположение каталога является результатом того, что этот файл стал обязательным модулем npm.
Наконец, мы можем перейти к отдельным функциям для операций CRUD в модели. Во-первых, find
function. Если я передаю объект в эту функцию, я хочу найти первый объект в массиве, который представляет коллекцию, которая соответствует свойству. Поскольку это в настоящее время написано, оно работает только с одним свойством, но я надеюсь расширить его в более поздних версиях. Это будет верно для большинства функций, которые я здесь перечисляю. Эта функция была написана первой специально, потому что я могу повторно использовать ее как для функций обновления, так и для удаления. Метод ES6 Array.find
упростил написание этой функции.
Точно так же мы можем сделать то же самое, используя Array.filter
, чтобы создать метод findAll
:
Я счастлив, что эта функция при передаче пустого объекта ведет себя аналогично функции Mongoose / MongoDB findAll при передаче пустого объекта.
Здесь все становится интересно. Первоначально я написал insert
, а затем insertMany
функции с вызовом fs.writeFileSync()
в них. Приготовившись двигаться дальше, я понял, что все функции, которые создают, обновляют или удаляют данные, должны будут переписать файл. Чтобы СУШИТЬ код, прежде чем заходить слишком далеко, я решил абстрагироваться от перезаписи его собственным методом, показанным ниже.
Эта функция в том виде, в котором она написана сейчас, меня пугает, потому что я точно знаю, что она убьет производительность при операциях чтения / записи. Скорее всего, мне придется вернуться за рефакторингом для этого позже.
Функцию insert
написать не так уж сложно, учитывая, что мы просто добавляем еще один элемент в массив. После помещения объекта в массив мы просто перезаписываем файл коллекции и возвращаем вставленный объект.
Впоследствии писать insertMany
становится глупо просто после написания insert
- this.insert
ALL THE THINGS!
Для последних двух операций CRUD я написал код, который работал, но не обязательно работал хорошо:
Хотя splice позволяет просто модифицировать образец массива, особенно с использованием this.find
, я должен был заметить, что что-то упустил при обновлении. Несмотря на то, что он выполняется, обновление в настоящее время перезаписывает объект на месте, а не обновляет определенные значения. На следующем проходе мне нужно будет переписать функцию, чтобы включить в нее свойства, которые уже есть в объекте, и изменить значение тех, которые передаются в качестве аргумента. Здесь мы также видим возврат метода this.rewrite()
, который сохранил код невероятно чистым.
В общем, это была забавная кроличья нора, в которую можно было окунуться на выходных, и я счастлив, как это обернулось. Я с нетерпением жду возможности использовать этот код в качестве примера для обучения моих студентов и надеюсь, что некоторые из них проявят инициативу и внесут свой вклад в исправление некоторых ошибок, которые я оставил позади!
Я думаю, что этот проект будет хорош для демонстрации примеров того, как конструкторы могут использоваться для создания функциональных возможностей объектов, и я рад реализовать схемы, чтобы я мог также обсудить цепочку прототипов. Даже в полуфункциональном состоянии badcube может быть отличным примером того, как создавать объекты JS, которые представляют данные в «базе данных», в данном случае файлы .json в папке коллекций.
Что касается вариантов использования, я считаю, что badcube может найти применение в приложениях быстрого ввода данных, в которых настройка MongoDB представляет собой слишком много работы или где данные необходимо ввести в файл JSON и в конечном итоге импортировать в базу данных noSQL. В таком приложении тот факт, что библиотека выводит массив JSON, упростит импорт данных. Учитывая, что у badcube нет зависимостей (серьезно, нулевых зависимостей), его можно добавить в проект почти бесплатно с точки зрения занимаемого места.
Двигаясь вперед, я хотел бы вернуться и написать документацию для базы данных, а также переписать некоторые функции, обсуждаемые в этой статье. А пока всем весело, и пиар приветствуется!