Can DBreeze handle inheritance of Object?

Mar 24, 2016 at 11:29 PM
I'm considering to use DBreeze as DB of private development. So I want to know DBreeze can handle inheritance of Object like iOS's CoreData, or not.

CoreData supports inheritance of Object, so it is very easy to create File-system-like structure.

For example,
  • FileSystemObject : Object
    • name : String
    • parent : Folder
  • Folder : FileSystemObject
    • children : [FileSystemObject]
  • File : FileSystemObject
    • content : Blob
Such structure is easy to create and handle in CoreData. Can DBreeze create and handle such structure natively and easily?

Needless to say, DBreeze is just a database, so I can realize this if I divide the object to multiple table and treating them as single object by self-coding.

But my main concern is reducing structure design and amount of coding, so I want to know DBreeze can handle this natively...
Coordinator
Mar 26, 2016 at 10:53 AM
Edited Mar 26, 2016 at 11:48 PM
Hi, natively we don't have such, but it's very easy to implement.
Here is a possible solution. Create class TreeNode
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using DBreeze;
using DBreeze.Utils;

namespace DataTreeView
{
    public class TreeNode
    {
        public enum eNodetype
        {
            Folder,
            File
        }

        public eNodetype NodeType = eNodetype.File;
        public string NodeName = String.Empty;

        public long NodeId = 1;
        public long ParentNodeId = 0;

        //Interesting for File NodeType
        /// <summary>
        /// This is filled when we insert node
        /// </summary>
        public byte[] NodeContent = null;
        /// <summary>
        /// This is returned when we get node. Later must be used GetContent
        /// </summary>
        public byte[] ContentRef = null;

        public DBreezeEngine engine = null;
        public string treeStoragetableName = "";
                

        /// <summary>
        /// This way we initialize Root node
        /// </summary>
        /// <param name="treeStoragetableName"></param>
        /// <param name="engine"></param>
        public TreeNode(string treeStoragetableName, DBreezeEngine engine)
        {
            this.engine = engine;
            this.treeStoragetableName = treeStoragetableName;
            this.NodeId = 1;
            this.ParentNodeId = 0;
        }

        /// <summary>
        /// This is used internally or when we want to insert some new nodes
        /// </summary>
        /// <param name="nodetype"></param>
        /// <param name="nodeName"></param>
        public TreeNode(eNodetype nodetype, string nodeName)
        {
            this.NodeType = nodetype;
            this.NodeName = nodeName;
        }

        /// <summary>
        /// Returns node by ParentIdAndNodeId
        /// </summary>
        /// <param name="parentNodeId"></param>
        /// <param name="nodeId"></param>
        /// <returns></returns>
        public TreeNode GetNodebyParentIdAndNodeId(long parentNodeId, long nodeId)
        {
            using (var tran = engine.GetTransaction())
            {
                DBreeze.DataTypes.NestedTable nt2 = tran.SelectTable(treeStoragetableName, new byte[] { 2 }, 0);
                nt2.ValuesLazyLoadingIsOn = false;

                TreeNode node = null;
                byte[] val = null;

                byte[] fromKey = parentNodeId.To_8_bytes_array_BigEndian().ConcatMany(nodeId.To_8_bytes_array_BigEndian(), new byte[] { 0 });
                byte[] toKey = parentNodeId.To_8_bytes_array_BigEndian().ConcatMany(nodeId.To_8_bytes_array_BigEndian(), new byte[] { 255 });

                foreach (var row in nt2.SelectForwardFromTo<byte[], byte[]>(fromKey, true, toKey, true))
                {
                    val = row.Value;
                    if (row.Key.Substring(16, 1)[0] == 1)
                    {
                        //File node
                        node = new TreeNode(TreeNode.eNodetype.File, System.Text.Encoding.UTF8.GetString(val.Substring(16)));
                        //Setting up reference to the content
                        node.ContentRef = val.Substring(0, 16);
                    }
                    else
                    {
                        //Folder node
                        node = new TreeNode(TreeNode.eNodetype.Folder, System.Text.Encoding.UTF8.GetString(val));
                    }

                    node.ParentNodeId = row.Key.Substring(0, 8).To_Int64_BigEndian();
                    node.NodeId = row.Key.Substring(8, 8).To_Int64_BigEndian();

                    node.engine = this.engine;
                    node.treeStoragetableName = this.treeStoragetableName;

                    return node;
                }

                return null;
            }
        }

        /// <summary>
        /// Reading all children
        /// </summary>
        /// <returns></returns>
        public IEnumerable<TreeNode> GetNodes()
        {
            using (var tran = engine.GetTransaction())
            {                
                DBreeze.DataTypes.NestedTable nt2 = tran.SelectTable(treeStoragetableName, new byte[] { 2 }, 0);
                nt2.ValuesLazyLoadingIsOn = false;

                TreeNode node = null;
                byte[] val = null;               

                byte[] fromKey = this.NodeId.To_8_bytes_array_BigEndian().ConcatMany(long.MinValue.To_8_bytes_array_BigEndian(), new byte[] { 0 });
                byte[] toKey = this.NodeId.To_8_bytes_array_BigEndian().ConcatMany(long.MaxValue.To_8_bytes_array_BigEndian(), new byte[] { 255 });
                
                foreach (var row in nt2.SelectForwardFromTo<byte[], byte[]>(fromKey, true, toKey, true))
                {
                    val = row.Value;                
                    if (row.Key.Substring(16, 1)[0] == 1)
                    {
                        //File node
                        node = new TreeNode(TreeNode.eNodetype.File, System.Text.Encoding.UTF8.GetString(val.Substring(16)));
                        //Setting up reference to the content
                        node.ContentRef = val.Substring(0, 16);
                    }
                    else
                    {
                        //Folder node
                        node = new TreeNode(TreeNode.eNodetype.Folder, System.Text.Encoding.UTF8.GetString(val));
                    }

                    node.ParentNodeId = row.Key.Substring(0, 8).To_Int64_BigEndian();
                    node.NodeId = row.Key.Substring(8, 8).To_Int64_BigEndian();

                    node.engine = this.engine;
                    node.treeStoragetableName = this.treeStoragetableName;

                    yield return node;
                }
            }
        }

        /// <summary>
        /// Adding children
        /// </summary>
        /// <param name="nodes"></param>
        public void AddNodes(IEnumerable<TreeNode> nodes)
        {
            if (nodes == null || nodes.Count() == 0)
                throw new Exception("Nodes are not supplied");

            using (var tran = engine.GetTransaction())
            {                
                DBreeze.DataTypes.NestedTable nt1 = tran.InsertTable(treeStoragetableName, new byte[] { 1 }, 0);  //Here we store id
                DBreeze.DataTypes.NestedTable nt2 = tran.InsertTable(treeStoragetableName, new byte[] { 2 }, 0);  //here is a structure table                

                //For speeding up insert process (only if speed is not good enough) unremark next line
               // nt2.Technical_SetTable_OverwriteIsNotAllowed();

                byte[] val = null;

                long maxId = nt1.Select<byte[], long>(new byte[] { 1 }).Value;

                if (maxId == 0) //Root node has id 1
                    maxId++;

                foreach (var node in nodes)
                {
                    maxId++;
                    if (node == null)
                        throw new Exception("Node can't be empty");

                    //Those nodes will receive id 1
                    node.ParentNodeId = this.NodeId;
                    node.NodeId = maxId;

                    //ParentNodeId(long),NodeId(long),1byte nodetype
                    byte[] key = node.ParentNodeId.To_8_bytes_array_BigEndian()
                        .ConcatMany(node.NodeId.To_8_bytes_array_BigEndian(),
                        node.NodeType == TreeNode.eNodetype.File ? new byte[] { 1 } : new byte[] { 0 }
                        );

                    if (node.NodeType == eNodetype.File)
                    {
                        node.ContentRef = nt2.InsertDataBlock(node.ContentRef, node.NodeContent);
                        val = node.ContentRef.Concat(node.NodeName.To_UTF8Bytes());
                    }
                    else
                        val = node.NodeName.To_UTF8Bytes();
                    
                    nt2.Insert<byte[], byte[]>(key, val);
                }
                //Latest used Id
                nt1.Insert<byte[], long>(new byte[] { 1 }, maxId);

                tran.Commit();
            }//eo using

        }//eo func


        /// <summary>
        /// GetContent of a file node
        /// </summary>
        /// <returns></returns>
        public byte[] GetContent()
        {
            if (this.NodeType != eNodetype.File)
                throw new Exception("NodeType is not File");

            if (this.ContentRef == null)
                return null;

            using (var tran = engine.GetTransaction())
            {                
                DBreeze.DataTypes.NestedTable nt2 = null;             
                nt2 = tran.SelectTable(treeStoragetableName, new byte[] { 2 }, 0);  //here is a structure table

                return nt2.SelectDataBlock(this.ContentRef);
            }

        }

        /// <summary>
        /// Recursively reads out all nodes starting from this 
        /// </summary>
        /// <returns></returns>
        public IEnumerable<TreeNode> ReadOutAllNodesFromCurrentRecursively()
        {
            foreach (var node in ReadOutNodes(this))
                yield return node;           
        }

        IEnumerable<TreeNode> ReadOutNodes(TreeNode node)
        {
            foreach (var tn in node.GetNodes())
            {                
                yield return tn;         
                foreach(var inode in ReadOutNodes(tn))
                    yield return inode;
            }
        }

    }
}

Coordinator
Mar 26, 2016 at 10:54 AM
Edited Mar 26, 2016 at 11:48 PM
Now let's use it. Code for Form1.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using DBreeze;

namespace FileLikeStructure
{
    public partial class Form1 : Form
    {
        DBreezeEngine engine = null;
        DataTreeView.TreeNode xtree = null;
        public Form1()
        {
            InitializeComponent();

            engine = new DBreezeEngine(@"E:\temp\DBreezeTest\DBR1");
            xtree = new DataTreeView.TreeNode("testtree", engine);
        }

        private void button1_Click(object sender, EventArgs e)
        {



            var nodes = new List<DataTreeView.TreeNode>();

            /*Storing in the root first 3 nodes*/
            nodes.Add(new DataTreeView.TreeNode(DataTreeView.TreeNode.eNodetype.Folder, "folder1"));
            nodes.Add(new DataTreeView.TreeNode(DataTreeView.TreeNode.eNodetype.Folder, "folder2"));
            var fileNode = new DataTreeView.TreeNode(DataTreeView.TreeNode.eNodetype.File, "file1");
            fileNode.NodeContent = new byte[] { 1, 2, 3, 4, 5 };
            nodes.Add(fileNode);

            xtree.AddNodes(nodes);

            /*Reading out those nodes and in case if node is folder2, inserting under it some more nodes*/

            foreach (var tn in xtree.GetNodes())
            {
                if (tn.NodeName == "folder2")
                {
                    nodes.Add(new DataTreeView.TreeNode(DataTreeView.TreeNode.eNodetype.Folder, "xfolder1"));
                    nodes.Add(new DataTreeView.TreeNode(DataTreeView.TreeNode.eNodetype.Folder, "xfolder2"));
                    nodes.Add(new DataTreeView.TreeNode(DataTreeView.TreeNode.eNodetype.File, "xfile1"));

                    tn.AddNodes(nodes);


                }
            }


            /*----------Reading all out starting from root*/           

            foreach (var tn in xtree.ReadOutAllNodesFromCurrentRecursively())
            {
                Console.WriteLine(tn.NodeName + "_" + tn.NodeId + "_" + tn.ParentNodeId);
                if (tn.NodeType == DataTreeView.TreeNode.eNodetype.File)
                {
                    //Showing content of the file
                    byte[] cnt = tn.GetContent();
                }
            }
        }


    }
}
Coordinator
Mar 26, 2016 at 10:58 AM
It's also possible to implement more nice features, like search by name etc...
Coordinator
Mar 30, 2016 at 11:06 AM
So, currently starting from v.01.074.20160329 of DBreeze database we have requested ability. Please read docu from [20160329] and make your comments.