Skip to content

Commit

Permalink
feat: read ccomponents
Browse files Browse the repository at this point in the history
  • Loading branch information
adrienaury committed Mar 22, 2024
1 parent 9086d1d commit 91b6bc1
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 123 deletions.
94 changes: 94 additions & 0 deletions pkg/multimap/multimap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright (C) 2024 CGI France
//
// This file is part of SILO.
//
// SILO is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// SILO is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with SILO. If not, see <http://www.gnu.org/licenses/>.

package multimap

type Multimap[K, V comparable] map[K]map[V]int

// Add a key/value pair to the multimap.
func (m Multimap[K, V]) Add(key K, value V) {
set, ok := m[key]
if !ok {
set = make(map[V]int)
}

set[value]++

m[key] = set
}

// Delete values associated to key.
func (m Multimap[K, V]) Delete(key K) []V {
set, ok := m[key]
if !ok {
return []V{}
}

values := make([]V, 0, len(set))
for value := range set {
values = append(values, value)
}

delete(m, key)

return values
}

// Get values associated to key.
func (m Multimap[K, V]) Get(key K) []V {
set, ok := m[key]
if !ok {
return []V{}
}

values := make([]V, 0, len(set))
for value := range set {
values = append(values, value)
}

return values
}

// Get a random key in the multimap.
func (m Multimap[K, V]) RandomKey() (K, bool) { //nolint:ireturn
for key := range m {
return key, true
}

return *new(K), false
}

// Count the number of values associated to key.
func (m Multimap[K, V]) Count(key K) int {
return len(m[key])
}

// Copy the multimap.
func (m Multimap[K, V]) Copy() Multimap[K, V] {
multimapCopy := make(Multimap[K, V], len(m))

for key, values := range m {
valuesCopy := make(map[V]int, len(values))
for value, count := range values {
valuesCopy[value] = count
}

multimapCopy[key] = valuesCopy
}

return multimapCopy
}
28 changes: 16 additions & 12 deletions pkg/silo/default_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,34 +17,38 @@

package silo

import "github.com/cgi-fr/silo/pkg/multimap"

type BackendInMemory struct {
links []DataLink
nodes []DataNode
links multimap.Multimap[DataNode, DataNode]
}

func NewBackendInMemory() *BackendInMemory {
return &BackendInMemory{
links: []DataLink{},
nodes: []DataNode{},
links: multimap.Multimap[DataNode, DataNode]{},
}
}

func (b *BackendInMemory) StoreLink(link DataLink) error {
b.links = append(b.links, link)
func (b *BackendInMemory) Store(key DataNode, value DataNode) error {
b.links.Add(key, value)

return nil
}

func (b *BackendInMemory) StoreNode(node DataNode) error {
b.nodes = append(b.nodes, node)
func (b *BackendInMemory) Snapshot() Snapshot { //nolint:ireturn
return &BackendInMemory{
links: b.links.Copy(),
}
}

func (b *BackendInMemory) Close() error {
return nil
}

func (b *BackendInMemory) ReadLinks() DataLinkReader { //nolint:ireturn
return NewDataLinkReaderInMemory(b.links)
func (b *BackendInMemory) Next() (DataNode, bool) {
return b.links.RandomKey()
}

func (b *BackendInMemory) ReadNodes() DataNodeReader { //nolint:ireturn
return NewDataNodeReaderInMemory(b.nodes)
func (b *BackendInMemory) PullAll(node DataNode) ([]DataNode, error) {
return b.links.Delete(node), nil
}
13 changes: 9 additions & 4 deletions pkg/silo/driven.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,15 @@ type DataNodeReader interface {
}

type Backend interface {
StoreLink(link DataLink) error
StoreNode(node DataNode) error
ReadLinks() DataLinkReader
ReadNodes() DataNodeReader
Store(key DataNode, value DataNode) error
Snapshot() Snapshot
Close() error
}

type Snapshot interface {
Next() (DataNode, bool)
PullAll(node DataNode) ([]DataNode, error)
Close() error
}

type DumpWriter interface {
Expand Down
103 changes: 21 additions & 82 deletions pkg/silo/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ import (
"fmt"
"io"
"strconv"

"github.com/dominikbraun/graph"
)

type Driver struct {
Expand All @@ -38,46 +36,25 @@ func NewDriver(backend Backend, writer DumpWriter) *Driver {
}
}

//nolint:cyclop
func (d *Driver) Dump() error {
nodes, _ := d.ReadAllNodes()
links, _ := d.ReadAllLinks()
nodemap := map[string]DataNode{}

grph := graph.New[string, DataNode](func(n DataNode) string { return n.String() })

for _, node := range nodes {
nodemap[node.String()] = node

if err := grph.AddVertex(node); err != nil && !errors.Is(err, graph.ErrVertexAlreadyExists) {
return fmt.Errorf("%w", err)
}
}

for _, link := range links {
if err := grph.AddEdge(link.E1.String(), link.E2.String()); err != nil && !errors.Is(err, graph.ErrEdgeAlreadyExists) { //nolint:lll
return fmt.Errorf("%w", err)
}
}
snapshot := d.backend.Snapshot()

for count := 0; ; count++ {
if len(nodemap) == 0 {

entryNode, present := snapshot.Next()
if !present {
break
}

for key := range nodemap {
err := graph.BFS[string, DataNode](grph, key, func(hash string) bool {
_ = d.writer.Write(nodemap[hash], strconv.Itoa(count))
_ = d.writer.Write(entryNode, strconv.Itoa(count))

delete(nodemap, hash)

return false
})
if err != nil {
return fmt.Errorf("%w", err)
}
connectedNodes, err := snapshot.PullAll(entryNode)
if err != nil {
return fmt.Errorf("%w", err)
}

break
for _, connectedNode := range connectedNodes {
_ = d.writer.Write(connectedNode, strconv.Itoa(count))
}
}

Expand All @@ -97,16 +74,14 @@ func (d *Driver) Scan(input DataRowReader) error {
break
}

nodes, links := Scan(datarow)
links := Scan(datarow)

for _, node := range nodes {
if err := d.backend.StoreNode(node); err != nil {
for _, link := range links {
if err := d.backend.Store(link.E1, link.E2); err != nil {
return fmt.Errorf("%w: %w", ErrPersistingData, err)
}
}

for _, link := range links {
if err := d.backend.StoreLink(link); err != nil {
if err := d.backend.Store(link.E2, link.E1); err != nil {
return fmt.Errorf("%w: %w", ErrPersistingData, err)
}
}
Expand All @@ -115,47 +90,7 @@ func (d *Driver) Scan(input DataRowReader) error {
return nil
}

func (d *Driver) ReadAllNodes() ([]DataNode, error) {
nodes := []DataNode{}
reader := d.backend.ReadNodes()

for {
node, err := reader.ReadDataNode()
if err != nil && !errors.Is(err, io.EOF) {
return nodes, fmt.Errorf("%w: %w", ErrReadingPersistedData, err)
}

if errors.Is(err, io.EOF) {
break
}

nodes = append(nodes, node)
}

return nodes, nil
}

func (d *Driver) ReadAllLinks() ([]DataLink, error) {
links := []DataLink{}
reader := d.backend.ReadLinks()

for {
link, err := reader.ReadDataLink()
if err != nil && !errors.Is(err, io.EOF) {
return links, fmt.Errorf("%w: %w", ErrReadingPersistedData, err)
}

if errors.Is(err, io.EOF) {
break
}

links = append(links, link)
}

return links, nil
}

func Scan(datarow DataRow) ([]DataNode, []DataLink) {
func Scan(datarow DataRow) []DataLink {
nodes := []DataNode{}
links := []DataLink{}

Expand All @@ -165,12 +100,16 @@ func Scan(datarow DataRow) ([]DataNode, []DataLink) {
}
}

if len(nodes) == 1 {
links = append(links, DataLink{E1: nodes[0], E2: nodes[0]})
}

// find all pairs in nodes
for i := 0; i < len(nodes); i++ {
for j := i + 1; j < len(nodes); j++ {
links = append(links, DataLink{E1: nodes[i], E2: nodes[j]})
}
}

return nodes, links
return links
}
25 changes: 0 additions & 25 deletions pkg/silo/driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"testing"

"github.com/cgi-fr/silo/pkg/silo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

Expand All @@ -41,14 +40,6 @@ func TestNominal(t *testing.T) {
err := driver.Scan(input)
require.NoError(t, err)

nodes, err := driver.ReadAllNodes()
require.NoError(t, err)
assert.Len(t, nodes, 8)

links, err := driver.ReadAllLinks()
require.NoError(t, err)
assert.Len(t, links, 12)

require.NoError(t, driver.Dump())
}

Expand All @@ -68,14 +59,6 @@ func TestPartialNull(t *testing.T) {
err := driver.Scan(input)
require.NoError(t, err)

nodes, err := driver.ReadAllNodes()
require.NoError(t, err)
assert.Len(t, nodes, 4)

links, err := driver.ReadAllLinks()
require.NoError(t, err)
assert.Len(t, links, 2)

require.NoError(t, driver.Dump())
}

Expand All @@ -95,13 +78,5 @@ func TestPartialMissing(t *testing.T) {
err := driver.Scan(input)
require.NoError(t, err)

nodes, err := driver.ReadAllNodes()
require.NoError(t, err)
assert.Len(t, nodes, 4)

links, err := driver.ReadAllLinks()
require.NoError(t, err)
assert.Len(t, links, 2)

require.NoError(t, driver.Dump())
}
4 changes: 4 additions & 0 deletions pkg/silo/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ func toStringRepresentationBuffered(value any, stringbuffer *strings.Builder) {
stringbuffer.WriteString("bool(")
stringbuffer.WriteString(strconv.FormatBool(tvalue))
stringbuffer.WriteByte(')')
case float32:
stringbuffer.WriteString("number(")
stringbuffer.WriteString(strconv.FormatFloat(float64(tvalue), 'g', -1, 32))
stringbuffer.WriteByte(')')
case float64:
stringbuffer.WriteString("number(")
stringbuffer.WriteString(strconv.FormatFloat(tvalue, 'g', -1, 64))
Expand Down

0 comments on commit 91b6bc1

Please sign in to comment.