From b3296bd1e54baa82a8b622aed46c95d43b794a50 Mon Sep 17 00:00:00 2001 From: Luka Vandervelden Date: Tue, 20 Nov 2018 07:35:49 +0900 Subject: [PATCH] WIP partitioning. --- orm.cr | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 124 insertions(+), 2 deletions(-) diff --git a/orm.cr b/orm.cr index cb1bade..778bbc2 100644 --- a/orm.cr +++ b/orm.cr @@ -2,8 +2,39 @@ require "json" class FS::Hash(K, V) + class PartitionData(V) + property name : String + property key_proc : Proc(V, String) + + def initialize(@name, @key_proc) + end + end + + @partitions = [] of PartitionData(V) + def initialize(@directory_name : String) - initialize + Dir.mkdir_p @directory_name + end + + ## + # name is the name that will be used on the file system. + def new_partition(name : String, &block : Proc(V, String)) + @partitions.push PartitionData(V).new name, block + + Dir.mkdir_p "#{@directory_name}/.by_#{name}" + end + + def get_partition(name : String, key : K) + r_value = Array(V).new + + partition_directory = "#{@directory_name}/.by_#{name}/#{key}" + Dir.each_child partition_directory do |child| + pp child + + r_value << V.from_json File.read "#{partition_directory}/#{child}" + end + + r_value end def []?(key) @@ -20,7 +51,21 @@ class FS::Hash(K, V) end def []=(key, value) + # FIXME: Update partitions pointing to previous value (in any) + File.write file_path(key), value.to_json + + @partitions.each do |index| + index_key = index.key_proc.call value + + symlink = file_path(key, index.name, index_key) + + Dir.mkdir_p File.dirname symlink + + File.delete symlink if File.exists? symlink + + File.symlink symlink_path(key), symlink + end end def delete(key) @@ -32,11 +77,29 @@ class FS::Hash(K, V) # FIXME: Only intercept “no such file" errors end + unless value.nil? + @partitions.each do |index| + index_key = index.key_proc.call value + + symlink = file_path(key, index.name, index_key) + + puts "old index #{key.to_s} => #{index_key}" + puts "symlink is #{symlink}" + + File.delete symlink + end + end + value end + ## + # CAUTION: Very slow. Try not to use. + # Can be useful for making dumps or to restore a database, however. def each Dir.each_child @directory_name do |child| + next if child.match /^\./ + full_path = "#{@directory_name}/#{child}" begin @@ -57,18 +120,30 @@ class FS::Hash(K, V) "#{@directory_name}/#{key.to_s}.json" end + private def file_path(key : String, index_name : String, index_key : String) + "#{@directory_name}/.by_#{index_name}/#{index_key}/#{key}.json" + end + + private def symlink_path(key : K) + "../../#{key.to_s}.json" + end + private def read(file_path : String) V.from_json File.read file_path end end +# Basic mapping testing. + a = FS::Hash(String, JSON::Any).new "test-storage" +a["a"] = JSON::Any.new "now exists" + pp! a["a"] pp! a["no file found"]? pp! a["invalid json"]? -pp! a["new entry"] = "blip blop" +pp! a["new entry"] = JSON::Any.new "blip blop" pp! a.delete "new entry" pp! a.delete "non-existant entry" @@ -76,3 +151,50 @@ a.each do |k, v| pp! k, v end +# Indexation testing. + +require "uuid" + +class Article + JSON.mapping({ + id: String, + title: String, + author: String + }) + + def initialize(@id, @title, @author) + end + + getter author + getter id +end + +articles = FS::Hash(String, Article).new "articles" +by_author = articles.new_partition "author", &.author + +article = Article.new UUID.random.to_s, "Bleh foo bar", "Satsuki" +articles[article.id] = article + +article = Article.new UUID.random.to_s, "Bleh foo bar", "Natsuki" +articles[article.id] = article + +article = Article.new UUID.random.to_s, "Bleh foo bar", "Mutsuki" +articles[article.id] = article + +articles.delete articles.get_partition("author", "Natsuki")[0].id + +article = Article.new UUID.random.to_s, "Bleh foo bar", "Satsuki" +articles[article.id] = article + +articles.delete articles.get_partition("author", "Satsuki")[1].id + +article = Article.new UUID.random.to_s, "Bleh foo bar", "Satsuki" +articles[article.id] = article + +article = Article.new UUID.random.to_s, "Bleh foo bar", "Nagatsuki" +articles[article.id] = article + +articles.each do |a, b| + p a, b +end +