(no subject)
Jan. 22nd, 2026 12:29 pmГибридная Графовая База Данных (отвертка с моторчиком для моделирования моделей)
День 4. API для работы с узлами, контейнеры, локальные файлы.
Основные операции с данными - добавление новых, изменение и удаление. Также данные сохраняются в локальном или удалённом хранилище и загружаются для использования в приложении. Практически всё то же самое, что и в табличных базах данных. Плюс поддержка сериализации объектов (на примере с++).
Для операций со всеми узлами данных/графа используется c++ класс EgDataNodesType.
Чтобы добавить гибкости использования, имя типа-класса-блюпринта устанавливается не при создании объекта, а функцией Connect(). Поскольку все типы-классы-блюпринты узлов должны регистрироваться в метаданных, сюда вставлена заглушка класса функциональности центральной базы.
source egDataNodesType.h
Каждый отдельный узел представляет класс EgDataNode, в котором для хранения данных узла служит EgPtrArrayType<EgByteArrayAbstractType*>*, объединяющий вышеупомянутые вспомогательные классы c++.
Чтобы динамически связывать узлы данных после загрузки линков, добавлены структуры in/outLinks и функции для работы с ними.
При апдейте индексов могут потребоваться данные до их изменения, они сохраняются в indexedFieldsOldValues.
Для удобства использования некоторые функции сделаны в виде операторов ( [], <<, >> ).
source egDataNode.h
Поскольку в egDataNodesType много функциональности, часть внутреннего кода лучше перенести в другие классы. Так данные узлов хранятся в контейнерах класса EgDataNodesContainerType, который в свою очередь использует EgDataNodesLocalFileType для файловых операций.
source egDataNodesContainer.h
День 4. API для работы с узлами, контейнеры, локальные файлы.
Основные операции с данными - добавление новых, изменение и удаление. Также данные сохраняются в локальном или удалённом хранилище и загружаются для использования в приложении. Практически всё то же самое, что и в табличных базах данных. Плюс поддержка сериализации объектов (на примере с++).
Для операций со всеми узлами данных/графа используется c++ класс EgDataNodesType.
Чтобы добавить гибкости использования, имя типа-класса-блюпринта устанавливается не при создании объекта, а функцией Connect(). Поскольку все типы-классы-блюпринты узлов должны регистрироваться в метаданных, сюда вставлена заглушка класса функциональности центральной базы.
source egDataNodesType.h
#pragma once #include "egDataNodesContainer.h" typedef void (*serialLoadFunctionType) (EgDataNode& dataNode); typedef void (*serialStoreFunctionType) (EgDataNode& dataNode); class EgLinksType; // arrow links class EgDatabaseType; // peer database class EgDataNodesType { // "type" means c++ type, data metatype called "blueprint" public: bool isConnected { false }; // checked blueprint file and central egDb metadata bool isDataLoaded { false }; bool isDataUpdated { false }; std::string dataNodesName; EgDatabaseType* metaInfoDatabase { nullptr }; // nodes and links layout == blueprint == class == type info EgDataNodeBlueprintType* dataNodeBlueprint { nullptr }; // layout == blueprint == class == type of these data nodes EgDataNodesContainerType* nodesContainer { nullptr }; // data storage of all these nodes EgDataNodesMapType& dataMap; // shortcut to container nodes map, for (auto iter : dataMap) serialLoadFunctionType serialLoadFunction { nullptr }; // function for automated data load from node to void* localDataPtr serialStoreFunctionType serialStoreFunction { nullptr }; // function for automated data store to node form void* localDataPtr EgDataNodesType(); ~EgDataNodesType() { clear(); delete dataNodeBlueprint; delete nodesContainer; } void clear(); int ConnectSystemNodeType(std::string a_dataNodesName); // for local testing or internal database storages int OpenLocalBlueprint(); int Connect(const std::string& nodesNameStr, EgDatabaseType& myDB); int AddDataNode(EgDataNode* newNode); int AddDataNode(EgDataNode& newNode) { return AddDataNode(&newNode); } EgDataNodesType& operator << (EgDataNode* newNode) { AddDataNode(newNode); return *this; } EgDataNodesType& operator << (EgDataNode& newNode) { AddDataNode(newNode); return *this; } EgDataNodeIDType getAddedNodeID() { return nodesContainer->lastNodeID; } int MarkUpdatedDataNode(EgDataNodeIDType nodeID); int MarkUpdatedDataNode(EgDataNode& updNode) { return MarkUpdatedDataNode(updNode.dataNodeID); } void DeleteDataNode(EgDataNodeIDType delID); void DeleteDataNode(EgDataNode& delNode) { return DeleteDataNode(delNode.dataNodeID); } int Store(); int LoadAllNodes(); bool LoadNodesEQ(const std::string& indexName, EgByteArrayAbstractType& fieldValue); // Projects.LoadIndexedNodes(IC<int>("owner", EQ, 2) && IC<int>("status", EQ, 3)); // int LoadNodesByOffsets() { return nodesContainer-> LoadLocalNodesByOffsets(indexOffsets); } EgDataNode& operator[](EgDataNodeIDType nodeID); };
Каждый отдельный узел представляет класс EgDataNode, в котором для хранения данных узла служит EgPtrArrayType<EgByteArrayAbstractType*>*, объединяющий вышеупомянутые вспомогательные классы c++.
Чтобы динамически связывать узлы данных после загрузки линков, добавлены структуры in/outLinks и функции для работы с ними.
При апдейте индексов могут потребоваться данные до их изменения, они сохраняются в indexedFieldsOldValues.
Для удобства использования некоторые функции сделаны в виде операторов ( [], <<, >> ).
source egDataNode.h
#pragma once #include "egDataNodeBlueprint.h" #include "../service/egPtrArray.h" class EgDataNode { public: EgDataNodeIDType dataNodeID { 0 }; EgFileOffsetType dataFileOffset { 0 }; // stored offset for local file operations speedup EgDataNodeBlueprintType* dataNodeBlueprint { nullptr }; // blueprint == class == type of data nodes void* serialDataPtr { nullptr }; // link to ext data for serialization EgPtrArrayType<EgByteArrayAbstractType*>* dataFieldsPtrs; int insertIndex {0}; // stored index for AddNextDataFieldFromType() FIXME check reset std::unordered_map < std::string, EgByteArraySlicerType* > indexedFieldsOldValues; // store old value for index update std::unordered_map < EgBlueprintIDType, EgLinkDataPtrsNodePtrsMapType > inLinks; // links IDs resolving to ptrs std::unordered_map < EgBlueprintIDType, EgLinkDataPtrsNodePtrsMapType > outLinks; EgDataNode() = delete; // {} // for debug only EgDataNode(EgDataNodeBlueprintType* a_dataNodeBlueprint, bool initMe = true); EgDataNode(EgDataNodeBlueprintType* a_dataNodeBlueprint, void* a_serialDataPtr); ~EgDataNode() { /*std::cout << "EgDataNodeType destructor, ID = " << dataNodeID << std::endl; clear(); */ } void clear(); void init(); EgLinkDataPtrsNodePtrsMapType* getInLinksMap(EgBlueprintIDType linkBlueprintID); EgLinkDataPtrsNodePtrsMapType* getOutLinksMap(EgBlueprintIDType linkBlueprintID); void* getNextInLinkSerialPtr (EgBlueprintIDType linkBlueprintID, EgDataNode* prevLinkDataPtr); // link data, not linked node void* getNextOutLinkSerialPtr(EgBlueprintIDType linkBlueprintID, EgDataNode* prevLinkDataPtr); void deleteInLink (EgBlueprintIDType linkBlueprintID, EgDataNode* delLinkNodePtr); // link data, not linked node void deleteOutLink(EgBlueprintIDType linkBlueprintID, EgDataNode* delLinkNodePtr); EgDataNode* getInLinkedNode(EgBlueprintIDType linkBlueprintID, EgDataNode* linkNodePtr); // node, not link EgDataNode* getOutLinkedNode(EgBlueprintIDType linkBlueprintID, EgDataNode* linkNodePtr); // EgDataNodeType* getNextInLinkedNode(EgBlueprintIDType linkBlueprintID, EgDataNodeType* prevLinkDataPtr); // node, not link // EgDataNodeType* getNextOutLinkedNode(EgBlueprintIDType linkBlueprintID, EgDataNodeType* prevLinkDataPtr); void InsertDataFieldFromCharStr(const char* str); void InsertDataFieldFromByteArray(EgByteArrayAbstractType& ba); void InsertRawByteArrayPtr(EgByteArraySlicerType* baPtr); EgByteArrayAbstractType& operator[](const std::string& fieldStrName); // field data by name EgDataNode& operator << (const char* str) { InsertDataFieldFromCharStr(str); return *this; } EgDataNode& operator << (std::string& s) { InsertDataFieldFromCharStr(s.c_str()); return *this; } EgDataNode& operator << (const std::string& s) { InsertDataFieldFromCharStr(s.c_str()); return *this; } EgDataNode& operator << (EgByteArrayAbstractType& ba) { InsertDataFieldFromByteArray(ba); return *this; } template <typename T> EgDataNode& operator << (const T& value) { if (insertIndex < dataNodeBlueprint->fieldsCount) { EgByteArraySlicerType *byteArray = new EgByteArraySlicerType(dataNodeBlueprint->theHamSlicer, sizeof(value)); // use ham slicer allocator memcpy((void *)byteArray-> dataChunk, (void *)&value, sizeof(value)); dataFieldsPtrs->ptrsArray[insertIndex++] = byteArray; } else std::cout << "ERROR: AddNextDataFieldFromType() fields count overflow: " << dataNodeBlueprint-> blueprintName << std::endl; return *this; } void makeIndexedFieldsCopy(); // store old value for index update void writeDataFieldsToFile (EgFileType& theFile); // EgDataFieldsType& df, local file operations void readDataFieldsFromFile(EgFileType& theFile); }; // ========= Byte Array Length Convertors =============== uint8_t egConvertStaticToFlex(StaticLengthType staticVal, ByteType* flexibleVal); // convert fixed length dataset size to variable length uint8_t egConvertFlexToStatic(ByteType* flexibleVal, StaticLengthType& staticVal); // reverse convert variable length dataset size to fixed length // ======================== Debug ======================== // void PrintEgDataNodeOffsets(const EgDataNodeType& dataNode); void PrintEgDataNodeFields (const EgDataNode& dataNode);
Поскольку в egDataNodesType много функциональности, часть внутреннего кода лучше перенести в другие классы. Так данные узлов хранятся в контейнерах класса EgDataNodesContainerType, который в свою очередь использует EgDataNodesLocalFileType для файловых операций.
source egDataNodesContainer.h
#pragma once #include "egDataNodesLocalFile.h" class EgDataNodesContainerType { public: EgDataNodeIDType nodesCount {0}; EgDataNodeIDType lastNodeID {0}; EgDataNodeBlueprintType* dataNodeBlueprint { nullptr }; // blueprint == class == type of data nodes EgDataNodesLocalFileType* LocalNodesFile { nullptr }; // data files *.gdn load/store support // data nodes content and changes tracking EgDataNodesMapType dataNodes; // active nodes container EgDataNodesOrdMapType addedDataNodes; EgDataNodesMapType updatedDataNodes; EgDataNodesMapType deletedDataNodes; // TODO : clear all addons on node delete EgDataNodesContainerType(): LocalNodesFile (new EgDataNodesLocalFileType()) {} void init(EgDataNodeBlueprintType* a_dataNodeBlueprint); // get blueprint from upper layer TODO init indexes by blueprint ~EgDataNodesContainerType() { clear(); /* delete dataNodeBlueprint;*/ delete LocalNodesFile; } // FIXME check dynamic blueprint void clear(); int GetLastID(); int LoadLocalBlueprint(); EgDataNode* GetNodePtrByID(EgDataNodeIDType nodeID); int AddDataNode(EgDataNode* newNode); int MarkUpdatedDataNode(EgDataNodeIDType nodeID); void DeleteDataNode(const EgDataNodeIDType delID); EgDataNodesContainerType& operator << (EgDataNode* newNode); int StoreToLocalFile(); int LoadAllLocalFileNodes(); bool LoadLocalNodesEQ(const std::string& indexName, EgByteArrayAbstractType& fieldValue); int LoadLocalNodesByOffsets(std::set<EgFileOffsetType>& index_offsets); // ======================== Debug ======================== void PrintDataNodesContainer(); void PrintNodesChain(); };