-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindexes.go
152 lines (138 loc) · 4.96 KB
/
indexes.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
142
143
144
145
146
147
148
149
150
151
152
package filters
import (
"github.com/RoaringBitmap/roaring/roaring64"
vocab "github.com/go-ap/activitypub"
"github.com/go-ap/filters/index"
)
var hFn = index.HashFn
func extractBitmapsForSubprop(checks Checks, indexes map[index.Type]index.Indexable, typ index.Type) []*roaring64.Bitmap {
found := roaring64.FastAnd(extractBitmaps(checks, indexes)...)
if found.GetCardinality() == 0 {
return nil
}
iter := found.Iterator()
if iter == nil {
return nil
}
refs := make([]uint64, 0, found.GetCardinality())
for x := iter.PeekNext(); iter.HasNext(); x = iter.Next() {
refs = append(refs, x)
}
return index.GetBitmaps[uint64](indexes[typ], refs...)
}
const (
ByID = index.ByID
ByType = index.ByType
ByName = index.ByName
ByPreferredUsername = index.ByPreferredUsername
BySummary = index.BySummary
ByContent = index.ByContent
ByActor = index.ByActor
ByObject = index.ByObject
ByRecipients = index.ByRecipients
ByAttributedTo = index.ByAttributedTo
ByInReplyTo = index.ByInReplyTo
)
func extractBitmaps(checks Checks, indexes map[index.Type]index.Indexable) []*roaring64.Bitmap {
result := make([]*roaring64.Bitmap, 0)
for _, check := range checks {
switch fil := check.(type) {
case notCrit:
toExclude := roaring64.FastOr(extractBitmaps(Checks(fil), indexes)...)
if toExclude.GetCardinality() == 0 {
continue
}
all := roaring64.FastOr(index.GetBitmaps[uint64](indexes[ByID])...)
if all.GetCardinality() > 0 {
all.AndNot(toExclude)
result = append(result, all)
}
case idEquals:
result = append(result, index.GetBitmaps[uint64](indexes[ByID], hFn(vocab.IRI(fil)))...)
case iriEquals:
result = append(result, index.GetBitmaps[uint64](indexes[ByID], hFn(vocab.IRI(fil)))...)
case checkAny:
anys := extractBitmaps(Checks(fil), indexes)
result = append(result, roaring64.FastOr(anys...))
case checkAll:
alls := extractBitmaps(Checks(fil), indexes)
result = append(result, roaring64.FastAnd(alls...))
case naturalLanguageValCheck:
switch fil.typ {
case byName:
// NOTE(marius): the naturalLanguageValChecks have this idiosyncrasy of doing name searches for
// both Name and PreferredUsername fields, so until we split them, we should use the same logic here.
ors := make([]*roaring64.Bitmap, 0)
ors = append(ors, index.GetBitmaps[string](indexes[ByName], fil.checkValue)...)
ors = append(ors, index.GetBitmaps[string](indexes[ByPreferredUsername], fil.checkValue)...)
if len(ors) > 0 {
result = append(result, roaring64.FastOr(ors...))
}
case bySummary:
result = append(result, index.GetBitmaps[string](indexes[BySummary], fil.checkValue)...)
case byContent:
result = append(result, index.GetBitmaps[string](indexes[ByContent], fil.checkValue)...)
default:
}
case withTypes:
ors := make([]*roaring64.Bitmap, 0)
for _, tf := range fil {
ors = append(ors, index.GetBitmaps[string](indexes[ByType], string(tf))...)
}
if len(ors) > 0 {
result = append(result, roaring64.FastOr(ors...))
}
case actorChecks:
actorRefs := extractBitmapsForSubprop(Checks(fil), indexes, ByActor)
result = append(result, roaring64.FastOr(actorRefs...))
case objectChecks:
objectRefs := extractBitmapsForSubprop(Checks(fil), indexes, ByObject)
result = append(result, roaring64.FastOr(objectRefs...))
case attributedToEquals:
result = append(result, index.GetBitmaps[uint64](indexes[ByAttributedTo], hFn(vocab.IRI(fil)))...)
case inReplyToEquals:
result = append(result, index.GetBitmaps[uint64](indexes[ByInReplyTo], hFn(vocab.IRI(fil)))...)
case authorized:
if iri := vocab.IRI(fil); iri.Equals(vocab.PublicNS, true) {
result = append(result, index.GetBitmaps[uint64](indexes[ByRecipients], hFn(iri))...)
} else {
result = append(result,
roaring64.FastOr(
index.GetBitmaps[uint64](indexes[ByRecipients], hFn(vocab.PublicNS), hFn(iri))...,
),
)
}
case recipients:
result = append(result, index.GetBitmaps[uint64](indexes[ByRecipients], hFn(vocab.IRI(fil)))...)
}
}
return result
}
func (ff Checks) IndexMatch(indexes map[index.Type]index.Indexable) *roaring64.Bitmap {
if len(ff) == 0 {
return nil
}
// NOTE(marius): A normal list of Check functions in this package corresponds
// to a filter equivalent of All(Checks...).
// We can therefore use an AND operator for the bitmaps.
ands := extractBitmaps(ff, indexes)
return roaring64.FastAnd(ands...)
}
// SearchIndex does a fast index search for the received filters.
func SearchIndex(i *index.Index, ff ...Check) ([]vocab.IRI, error) {
bmp := Checks(ff).IndexMatch(i.Indexes)
if bmp.GetCardinality() == 0 {
return nil, nil
}
it := bmp.Iterator()
if it == nil {
return nil, nil
}
result := make([]vocab.IRI, 0, bmp.GetCardinality())
for x := it.PeekNext(); it.HasNext(); x = it.Next() {
if iri, ok := i.Ref[x]; ok {
result = append(result, iri)
}
}
return result, nil
}