Skip to content

Commit 2cd044a

Browse files
committed
Impl SurroundingTablesWithin
1 parent 35aa4d3 commit 2cd044a

File tree

3 files changed

+290
-0
lines changed

3 files changed

+290
-0
lines changed

db/schema.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,9 @@ func (s *Schema) ToErd() string {
3131

3232
return strings.Join(lines, "\n\n")
3333
}
34+
35+
// SurroundingTablesWithin returns surrounding tables from table
36+
func (s *Schema) SurroundingTablesWithin(tableName string, distance int) []string {
37+
explorer := NewSchemaExplorer(s)
38+
return explorer.Explore(tableName, distance)
39+
}

db/schema_explorer.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package db
2+
3+
import (
4+
"github.com/deckarep/golang-set"
5+
"sort"
6+
)
7+
8+
// SchemaExplorer represents schema explorer
9+
type SchemaExplorer struct {
10+
schema *Schema
11+
graph *UndirectedGraph
12+
}
13+
14+
// NewSchemaExplorer returns a new SchemaExplorer instance
15+
func NewSchemaExplorer(schema *Schema) *SchemaExplorer {
16+
graph := NewUndirectedGraph()
17+
for _, table := range schema.Tables {
18+
for _, foreignKey := range table.ForeignKeys {
19+
graph.PutSymmetric(table.Name, foreignKey.ToTable, true)
20+
}
21+
}
22+
23+
return &SchemaExplorer{schema: schema, graph: graph}
24+
}
25+
26+
// Explore returns surrounding tables from table
27+
func (e *SchemaExplorer) Explore(tableName string, distance int) []string {
28+
foundTableNames := mapset.NewSet()
29+
30+
e.explore(tableName, distance, foundTableNames, 0)
31+
32+
var tableNames []string
33+
foundTableNames.Each(func(i interface{}) bool {
34+
tableNames = append(tableNames, i.(string))
35+
return false
36+
})
37+
38+
sort.Strings(tableNames)
39+
return tableNames
40+
}
41+
42+
func (e *SchemaExplorer) explore(tableName string, distance int, foundTableNames mapset.Set, pos int) {
43+
if pos > distance || foundTableNames.Contains(tableName) {
44+
return
45+
}
46+
foundTableNames.Add(tableName)
47+
48+
for _, aroundTableName := range e.graph.GetRowColumns(tableName) {
49+
e.explore(aroundTableName, distance, foundTableNames, pos+1)
50+
}
51+
}

db/schema_test.go

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,236 @@ articles }-- users`,
121121
})
122122
}
123123
}
124+
125+
func TestSchema_SurroundingTablesWithin(t *testing.T) {
126+
tables := []*Table{
127+
{
128+
Name: "articles",
129+
Columns: []*Column{
130+
{
131+
Name: "id",
132+
Type: "integer",
133+
NotNull: true,
134+
PrimaryKey: true,
135+
},
136+
{
137+
Name: "user_id",
138+
Type: "integer",
139+
NotNull: true,
140+
},
141+
},
142+
ForeignKeys: []*ForeignKey{
143+
{
144+
Sequence: 0,
145+
FromColumn: "user_id",
146+
ToTable: "users",
147+
ToColumn: "id",
148+
},
149+
},
150+
},
151+
{
152+
Name: "comments",
153+
Columns: []*Column{
154+
{
155+
Name: "id",
156+
Type: "integer",
157+
NotNull: true,
158+
PrimaryKey: true,
159+
},
160+
{
161+
Name: "article_id",
162+
Type: "integer",
163+
NotNull: true,
164+
},
165+
},
166+
ForeignKeys: []*ForeignKey{
167+
{
168+
Sequence: 0,
169+
FromColumn: "article_id",
170+
ToTable: "articles",
171+
ToColumn: "id",
172+
},
173+
},
174+
},
175+
{
176+
Name: "followers",
177+
Columns: []*Column{
178+
{
179+
Name: "id",
180+
Type: "integer",
181+
NotNull: true,
182+
PrimaryKey: true,
183+
},
184+
{
185+
Name: "user_id",
186+
Type: "integer",
187+
NotNull: true,
188+
},
189+
{
190+
Name: "target_user_id",
191+
Type: "integer",
192+
NotNull: true,
193+
},
194+
},
195+
ForeignKeys: []*ForeignKey{
196+
{
197+
Sequence: 0,
198+
FromColumn: "user_id",
199+
ToTable: "users",
200+
ToColumn: "id",
201+
},
202+
{
203+
Sequence: 0,
204+
FromColumn: "target_user_id",
205+
ToTable: "users",
206+
ToColumn: "id",
207+
},
208+
},
209+
},
210+
{
211+
Name: "followings",
212+
Columns: []*Column{
213+
{
214+
Name: "id",
215+
Type: "integer",
216+
NotNull: true,
217+
PrimaryKey: true,
218+
},
219+
{
220+
Name: "user_id",
221+
Type: "integer",
222+
NotNull: true,
223+
},
224+
{
225+
Name: "target_user_id",
226+
Type: "integer",
227+
NotNull: true,
228+
},
229+
},
230+
ForeignKeys: []*ForeignKey{
231+
{
232+
Sequence: 0,
233+
FromColumn: "user_id",
234+
ToTable: "users",
235+
ToColumn: "id",
236+
},
237+
{
238+
Sequence: 0,
239+
FromColumn: "target_user_id",
240+
ToTable: "users",
241+
ToColumn: "id",
242+
},
243+
},
244+
},
245+
{
246+
Name: "likes",
247+
Columns: []*Column{
248+
{
249+
Name: "article_id",
250+
Type: "integer",
251+
NotNull: true,
252+
},
253+
{
254+
Name: "user_id",
255+
Type: "integer",
256+
NotNull: true,
257+
},
258+
},
259+
ForeignKeys: []*ForeignKey{
260+
{
261+
Sequence: 0,
262+
FromColumn: "article_id",
263+
ToTable: "articles",
264+
ToColumn: "id",
265+
},
266+
{
267+
Sequence: 0,
268+
FromColumn: "user_id",
269+
ToTable: "users",
270+
ToColumn: "id",
271+
},
272+
},
273+
},
274+
{
275+
Name: "revisions",
276+
Columns: []*Column{
277+
{
278+
Name: "id",
279+
Type: "integer",
280+
NotNull: true,
281+
PrimaryKey: true,
282+
},
283+
{
284+
Name: "article_id",
285+
Type: "integer",
286+
NotNull: true,
287+
},
288+
},
289+
ForeignKeys: []*ForeignKey{
290+
{
291+
Sequence: 0,
292+
FromColumn: "article_id",
293+
ToTable: "articles",
294+
ToColumn: "id",
295+
},
296+
},
297+
},
298+
{
299+
Name: "users",
300+
Columns: []*Column{
301+
{
302+
Name: "id",
303+
Type: "integer",
304+
NotNull: true,
305+
PrimaryKey: true,
306+
},
307+
{
308+
Name: "name",
309+
Type: "text",
310+
},
311+
},
312+
},
313+
}
314+
315+
type fields struct {
316+
Tables []*Table
317+
}
318+
type args struct {
319+
tableName string
320+
distance int
321+
}
322+
tests := []struct {
323+
name string
324+
fields fields
325+
args args
326+
want []string
327+
}{
328+
{
329+
name: "distance within 1 from articles",
330+
fields: fields{
331+
Tables: tables,
332+
},
333+
args: args{
334+
tableName: "articles",
335+
distance: 1,
336+
},
337+
want: []string{
338+
"articles",
339+
"comments",
340+
"likes",
341+
"revisions",
342+
"users",
343+
},
344+
},
345+
}
346+
for _, tt := range tests {
347+
t.Run(tt.name, func(t *testing.T) {
348+
s := &Schema{
349+
Tables: tt.fields.Tables,
350+
}
351+
352+
got := s.SurroundingTablesWithin(tt.args.tableName, tt.args.distance)
353+
assert.Equal(t, tt.want, got)
354+
})
355+
}
356+
}

0 commit comments

Comments
 (0)