public async Task <IEnumerable <MD5Sum> > GetDescendantIds(MD5Sum documentId)
        {
            var builder = ImmutableArray.CreateBuilder <MD5Sum>();

            using (var connection = new NpgsqlConnection(connectionString))
            {
                await connection.OpenAsync();

                using (var cmd = new NpgsqlCommand())
                {
                    cmd.Connection  = connection;
                    cmd.CommandText = "SELECT descendantId FROM relation WHERE antecedentId=@documentId;";
                    cmd.Parameters.AddWithValue("@documentId", documentId.ToGuid());

                    using (var reader = await cmd.ExecuteReaderAsync())
                    {
                        while (await reader.ReadAsync())
                        {
                            var figure = reader.GetGuid(0);
                            builder.Add(new MD5Sum(figure.ToByteArray()));
                        }
                    }
                }
            }

            return(builder);
        }
        public async Task <string> GetDocumentBodyAsync(MD5Sum id, bool ignoreBlock = false)
        {
            using (var connection = new NpgsqlConnection(connectionString))
            {
                await connection.OpenAsync();

                if (!ignoreBlock)
                {
                    using (var cmd = new NpgsqlCommand())
                    {
                        cmd.Connection  = connection;
                        cmd.CommandText = "SELECT isVoluntary FROM documentBlock WHERE id=@id;";
                        cmd.Parameters.AddWithValue("@id", id.ToGuid());

                        using (var reader = await cmd.ExecuteReaderAsync())
                        {
                            if (await reader.ReadAsync())
                            {
                                throw new DocumentBlockedException(reader.GetBoolean(0));
                            }
                        }
                    }
                }

                using (var cmd = new NpgsqlCommand())
                {
                    cmd.Connection  = connection;
                    cmd.CommandText = "SELECT body FROM documentBody WHERE id=@id;";
                    cmd.Parameters.AddWithValue("@id", id.ToGuid());

                    using (var reader = await cmd.ExecuteReaderAsync())
                    {
                        if (await reader.ReadAsync())
                        {
                            return(reader.GetString(0));
                        }
                        throw new FileNotFoundException();
                    }
                }
            }
        }
        public async Task <IEnumerable <Relation> > GetFamilyAsync(MD5Sum familyMemberId)
        {
            var idBoxedInGuidForDatabase = familyMemberId.ToGuid();
            var relationsBuilder         = ImmutableHashSet.CreateBuilder <Relation>();
            var closedSet = ImmutableHashSet.CreateBuilder <Guid>();
            var openSet   = ImmutableHashSet.CreateBuilder <Guid>();

            openSet.Add(idBoxedInGuidForDatabase);

            using (var connection = new NpgsqlConnection(connectionString))
            {
                await connection.OpenAsync();

                while (openSet.Count > 0)
                {
                    var openSetAsArray = openSet.ToArray();
                    closedSet.UnionWith(openSet);
                    openSet.Clear();

                    using (var cmd = new NpgsqlCommand())
                    {
                        cmd.Connection  = connection;
                        cmd.CommandText = "SELECT antecedentId, descendantId FROM relation WHERE ARRAY[antecedentId, descendantId] && @openSet;";
                        cmd.Parameters.AddWithValue("@openSet", openSetAsArray);

                        using (var reader = await cmd.ExecuteReaderAsync())
                        {
                            while (await reader.ReadAsync())
                            {
                                var alfa  = reader.GetGuid(0);
                                var bravo = reader.GetGuid(1);
                                openSet.Add(alfa);
                                openSet.Add(bravo);
                                relationsBuilder.Add(new Relation(new MD5Sum(alfa.ToByteArray()), new MD5Sum(bravo.ToByteArray())));
                            }
                        }
                    }

                    openSet.ExceptWith(closedSet);
                }
            }

            return(relationsBuilder);
        }
        public async Task <DocumentMetadata> GetDocumentMetadataAsync(MD5Sum id)
        {
            using (var connection = new NpgsqlConnection(connectionString))
            {
                await connection.OpenAsync();

                using (var cmd = new NpgsqlCommand())
                {
                    cmd.Connection  = connection;
                    cmd.CommandText = "SELECT title,authorId,timestamp FROM document WHERE id=@id;";
                    cmd.Parameters.AddWithValue("@id", id.ToGuid());

                    using (var reader = await cmd.ExecuteReaderAsync())
                    {
                        if (await reader.ReadAsync())
                        {
                            return(new DocumentMetadata(id, reader.GetString(0), reader.GetGuid(1), reader.GetDateTime(2)));
                        }
                        throw new FileNotFoundException();
                    }
                }
            }
        }