-
Notifications
You must be signed in to change notification settings - Fork 80
/
similar.go
141 lines (116 loc) · 3.11 KB
/
similar.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package hubbub
import (
"regexp"
"time"
"github.com/google/go-github/v31/github"
"github.com/imjasonmiller/godice"
"k8s.io/klog/v2"
)
var nonLetter = regexp.MustCompile(`[^a-zA-Z]`)
// A subset of Conversation for related items (requires less memory than a Conversation)
type RelatedConversation struct {
Organization string `json:"org"`
Project string `json:"project"`
ID int `json:"int"`
URL string `json:"url"`
Title string `json:"title"`
Author *github.User `json:"author"`
Type string `json:"type"`
Created time.Time `json:"created"`
}
func compressTitle(t string) string {
return nonLetter.ReplaceAllString(t, "")
}
func (h *Engine) updateSimilarityTables(rawTitle, url string) {
title := compressTitle(rawTitle)
result, existing := h.titleToURLs.LoadOrStore(title, []string{url})
if existing {
foundURL := false
otherURLs := []string{}
for _, v := range result.([]string) {
if v == url {
foundURL = true
break
}
otherURLs = append(otherURLs, v)
}
if !foundURL {
klog.V(1).Infof("updating %q with %v", rawTitle, otherURLs)
h.titleToURLs.Store(title, append(otherURLs, url))
}
return
}
// Update us -> them title similarity
similarTo := []string{}
h.titleToURLs.Range(func(k, v interface{}) bool {
otherTitle := k.(string)
if otherTitle == title {
return true
}
if godice.CompareString(title, otherTitle) > h.MinSimilarity {
klog.V(1).Infof("%q is similar to %q", rawTitle, otherTitle)
similarTo = append(similarTo, otherTitle)
}
return true
})
h.similarTitles.Store(title, similarTo)
// Update them -> us title similarity
for _, other := range similarTo {
klog.V(1).Infof("updating %q to map to %s", other, title)
others, ok := h.similarTitles.Load(other)
if ok {
h.similarTitles.Store(other, append(others.([]string), title))
}
}
}
func (h *Engine) FindSimilar(co *Conversation) []*RelatedConversation {
simco := []*RelatedConversation{}
title := compressTitle(co.Title)
similarURLs := []string{}
tres, ok := h.similarTitles.Load(title)
if !ok {
return nil
}
for _, ot := range tres.([]string) {
ures, ok := h.titleToURLs.Load(ot)
if ok {
similarURLs = append(similarURLs, ures.([]string)...)
}
}
if len(similarURLs) == 0 {
return nil
}
klog.V(2).Infof("#%d %q is similar to %v", co.ID, co.Title, similarURLs)
added := map[string]bool{}
for _, url := range similarURLs {
// We found ourselves with a different title
if url == co.URL {
continue
}
// May happen if we've seen a URL with different titles
if added[url] {
continue
}
oco := h.seen[url]
if oco == nil {
klog.Warningf("find similar: no conversation found for %s", url)
continue
}
klog.V(2).Infof("found %s: %q", url, oco.Title)
simco = append(simco, makeRelated(h.seen[url]))
added[url] = true
}
return simco
}
func makeRelated(c *Conversation) *RelatedConversation {
return &RelatedConversation{
Organization: c.Organization,
Project: c.Project,
ID: c.ID,
URL: c.URL,
Title: c.Title,
Author: c.Author,
Type: c.Type,
Created: c.Created,
}
}