Skip to content

Commit d677198

Browse files
committed
implement server-side redirects for @_exported paths
1 parent cef4147 commit d677198

21 files changed

+397
-40
lines changed

Sources/UnidocDB/Mongo.CollectionModel (ext).swift

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,26 @@ import MongoDB
22

33
// TODO: we need to unify ``Unidoc.Scalar`` and ``Unidoc.Group`, most likely by introducing
44
// a new type ``Unidoc.Vertex``.
5-
extension Mongo.CollectionModel // where Element.ID == Unidoc.Scalar
5+
extension Mongo.CollectionModel where Element.ID == Unidoc.Scalar
66
{
7-
/// Deletes all records from the collection within the specified zone.
7+
/// Deletes all records from the collection within the specified volume.
8+
func deleteAll(in range:Unidoc.Edition) async throws
9+
{
10+
try await self.clear(range: range)
11+
}
12+
}
13+
extension Mongo.CollectionModel where Element.ID == Unidoc.Group
14+
{
15+
/// Deletes all records from the collection within the specified volume.
16+
func deleteAll(in range:Unidoc.Edition) async throws
17+
{
18+
try await self.clear(range: range)
19+
}
20+
}
21+
extension Mongo.CollectionModel
22+
{
23+
/// Deletes all records from the collection within the specified volume.
24+
private
825
func clear(range:Unidoc.Edition) async throws
926
{
1027
let response:Mongo.DeleteResponse = try await session.run(

Sources/UnidocDB/Unidoc.DB.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,12 @@ extension Unidoc.DB
149149
.init(database: self.id, session: self.session)
150150
}
151151

152+
@inlinable public
153+
var redirects:Redirects
154+
{
155+
.init(database: self.id, session: self.session)
156+
}
157+
152158
@inlinable public
153159
var vertices:Vertices
154160
{
@@ -204,6 +210,7 @@ extension Unidoc.DB:Mongo.DatabaseModel
204210
try await self.packageDependencies.setup()
205211
try await self.editionDependencies.setup()
206212
try await self.volumes.setup()
213+
try await self.redirects.setup()
207214
try await self.vertices.setup()
208215
try await self.groups.setup()
209216
try await self.search.setup()
@@ -575,9 +582,10 @@ extension Unidoc.DB
575582
try await self.editionDependencies.clear(dependent: volume.id)
576583
try await self.packageDependencies.clear(dependent: volume.id)
577584

578-
try await self.vertices.clear(range: volume.id)
579-
try await self.groups.clear(range: volume.id)
580-
try await self.trees.clear(range: volume.id)
585+
try await self.redirects.deleteAll(in: volume.id)
586+
try await self.vertices.deleteAll(in: volume.id)
587+
try await self.groups.deleteAll(in: volume.id)
588+
try await self.trees.deleteAll(in: volume.id)
581589

582590
try await self.search.delete(id: volume.symbol)
583591
// Delete this last, otherwise if one of the other steps fails, we won’t
@@ -661,6 +669,7 @@ extension Unidoc.DB
661669
try await self.search.insert(search)
662670
try await self.trees.insert(mesh.trees)
663671

672+
try await self.redirects.insert(mesh.redirects)
664673
try await self.vertices.insert(mesh.vertices)
665674
try await self.groups.insert(mesh.groups,
666675
realm: mesh.metadata.latest ? mesh.metadata.realm : nil)
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import MongoDB
2+
import MongoQL
3+
import Unidoc
4+
import UnidocRecords
5+
6+
extension Unidoc.DB
7+
{
8+
@frozen public
9+
struct Redirects
10+
{
11+
public
12+
let database:Mongo.Database
13+
public
14+
let session:Mongo.Session
15+
16+
@inlinable
17+
init(database:Mongo.Database, session:Mongo.Session)
18+
{
19+
self.database = database
20+
self.session = session
21+
}
22+
}
23+
}
24+
extension Unidoc.DB.Redirects
25+
{
26+
public static
27+
let indexPaths:Mongo.CollectionIndex = .init("Paths",
28+
collation: VolumeCollation.spec,
29+
unique: true)
30+
{
31+
$0[Element[.id] / Unidoc.Redirect[.volume]] = (+)
32+
$0[Element[.stem]] = (+)
33+
$0[Element[.hash]] = (+)
34+
}
35+
}
36+
extension Unidoc.DB.Redirects:Mongo.CollectionModel
37+
{
38+
public
39+
typealias Element = Unidoc.RedirectVertex
40+
41+
@inlinable public static
42+
var name:Mongo.Collection { "VolumeRedirects" }
43+
44+
@inlinable public static
45+
var indexes:[Mongo.CollectionIndex] { [ Self.indexPaths ] }
46+
}
47+
extension Unidoc.DB.Redirects:Mongo.RecodableModel
48+
{
49+
}
50+
extension Unidoc.DB.Redirects
51+
{
52+
@discardableResult
53+
func deleteAll(in volume:Unidoc.Edition) async throws -> Int
54+
{
55+
try await self.deleteAll
56+
{
57+
$0[Element[.id] / Unidoc.Redirect[.volume]] = volume
58+
}
59+
}
60+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import MongoQL
2+
import UnidocRecords
3+
4+
extension Unidoc.Redirect:Mongo.MasterCodingModel
5+
{
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import MongoQL
2+
import UnidocRecords
3+
4+
extension Unidoc.RedirectVertex:Mongo.MasterCodingModel
5+
{
6+
}

Sources/UnidocLinker/Sema/Unidoc.Linker.Tables.swift

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ extension Unidoc.Linker
3636

3737
private(set)
3838
var extensions:Unidoc.Linker.Table<Unidoc.Extension>
39+
var redirects:[Unidoc.RedirectVertex]
3940
var articles:[Unidoc.ArticleVertex]
4041
var decls:[Unidoc.DeclVertex]
4142

@@ -59,6 +60,7 @@ extension Unidoc.Linker
5960
self.next = .init(base: self.context.current.id)
6061

6162
self.extensions = extensions
63+
self.redirects = []
6264
self.articles = []
6365
self.decls = []
6466

@@ -388,6 +390,10 @@ extension Unidoc.Linker.Tables
388390
{
389391
self.link(articles: articles, under: namespace)
390392
}
393+
394+
// Create redirects
395+
self.redirect(decls: culture.reexports.unhashed, hashed: false, from: namespace)
396+
self.redirect(decls: culture.reexports.hashed, hashed: true, from: namespace)
391397
}
392398

393399
return self.modules.map
@@ -518,7 +524,7 @@ extension Unidoc.Linker.Tables
518524
route: decl.route),
519525
signature: decl.signature.map { self.current.scalars.decls[$0] },
520526
symbol: symbol,
521-
stem: .decl(namespace.module, decl.path, orientation: decl.phylum.orientation),
527+
stem: .decl(namespace.module, decl.path, decl.phylum),
522528
_requirements: [],
523529
superforms: self.context.sort(superforms, by: Unidoc.SemanticPriority.self),
524530
namespace: namespace.id,
@@ -548,4 +554,32 @@ extension Unidoc.Linker.Tables
548554

549555
return miscellaneous
550556
}
557+
558+
private mutating
559+
func redirect(decls:[Int32],
560+
hashed:Bool,
561+
from namespace:SymbolGraph.NamespaceContext<Void>)
562+
{
563+
for local:Int32 in decls
564+
{
565+
let symbol:Symbol.Decl = self.current.decls.symbols[local]
566+
567+
guard
568+
let id:Unidoc.Scalar = self.current.scalars.decls[local],
569+
let decl:SymbolGraph.Decl = self.context[decl: id]
570+
else
571+
{
572+
continue
573+
}
574+
575+
let redirect:Unidoc.RedirectVertex = .init(id: .init(
576+
volume: self.current.id,
577+
target: id),
578+
stem: .decl(namespace.module, decl.path, decl.phylum),
579+
hash: .decl(symbol),
580+
hashed: hashed)
581+
582+
self.redirects.append(redirect)
583+
}
584+
}
551585
}

Sources/UnidocLinker/Sema/Unidoc.Linker.TreeMapper.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ extension Unidoc.Linker.TreeMapper
151151
phylum: decl.phylum,
152152
kinks: decl.kinks,
153153
route: decl.route),
154-
stem: .decl(namespace, decl.path, orientation: decl.phylum.orientation),
154+
stem: .decl(namespace, decl.path, decl.phylum),
155155
hash: .decl(symbol))
156156
}
157157
}

Sources/UnidocLinker/Sema/Unidoc.LinkerTables.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ extension Unidoc.LinkerTables
451451
route: decl.route),
452452
signature: decl.signature.map { self.current.scalars.decls[$0] },
453453
symbol: symbol,
454-
stem: .decl(namespace.symbol, decl.path, orientation: decl.phylum.orientation),
454+
stem: .decl(namespace.symbol, decl.path, decl.phylum),
455455
_requirements: [],
456456
superforms: self.linker.sort(superforms, by: Unidoc.SemanticPriority.self),
457457
namespace: namespace.colony,

Sources/UnidocLinker/Unidoc.Mesh.Interior.swift

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,21 @@ extension Unidoc.Mesh
1717
var index:JSON
1818
public
1919
var trees:[Unidoc.TypeTree]
20+
public
21+
var redirects:[Unidoc.RedirectVertex]
2022

2123
private
2224
init(vertices:Vertices,
2325
groups:Groups,
2426
index:JSON,
25-
trees:[Unidoc.TypeTree])
27+
trees:[Unidoc.TypeTree],
28+
redirects:[Unidoc.RedirectVertex])
2629
{
2730
self.vertices = vertices
2831
self.groups = groups
2932
self.index = index
3033
self.trees = trees
34+
self.redirects = redirects
3135
}
3236
}
3337
}
@@ -70,6 +74,7 @@ extension Unidoc.Mesh.Interior
7074
let products:[Unidoc.ProductVertex]
7175
let cultures:[Unidoc.CultureVertex]
7276

77+
let redirects:[Unidoc.RedirectVertex]
7378
let articles:[Unidoc.ArticleVertex]
7479
let decls:[Unidoc.DeclVertex]
7580
let groups:Unidoc.Mesh.Groups
@@ -82,6 +87,8 @@ extension Unidoc.Mesh.Interior
8287
conformances = tables.linkConformingTypes()
8388
products = tables.linkProducts()
8489
cultures = tables.linkCultures()
90+
91+
redirects = tables.redirects
8592
articles = tables.articles
8693
decls = tables.decls
8794
groups = tables.groups
@@ -104,6 +111,7 @@ extension Unidoc.Mesh.Interior
104111
cultures = tables.linkCultures()
105112

106113
extensions = tables.extensions
114+
redirects = []
107115
groups = tables.groups
108116

109117
linker = tables.linker
@@ -112,6 +120,7 @@ extension Unidoc.Mesh.Interior
112120
self.init(around: landingVertex,
113121
conformances: conformances,
114122
extensions: extensions,
123+
redirects: redirects,
115124
products: products,
116125
cultures: cultures,
117126
articles: articles,
@@ -124,6 +133,7 @@ extension Unidoc.Mesh.Interior
124133
init(around landing:consuming Unidoc.LandingVertex,
125134
conformances:consuming Unidoc.Linker.Table<Unidoc.Conformers>,
126135
extensions:consuming Unidoc.Linker.Table<Unidoc.Extension>,
136+
redirects:consuming [Unidoc.RedirectVertex],
127137
products:consuming [Unidoc.ProductVertex],
128138
cultures:consuming [Unidoc.CultureVertex],
129139
articles:consuming [Unidoc.ArticleVertex],
@@ -278,6 +288,7 @@ extension Unidoc.Mesh.Interior
278288
foreign: foreign),
279289
groups: groups,
280290
index: index,
281-
trees: trees)
291+
trees: trees,
292+
redirects: redirects)
282293
}
283294
}

Sources/UnidocLinker/Unidoc.Mesh.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,12 @@ extension Unidoc.Mesh
5050
var trees:[Unidoc.TypeTree] { self.interior.trees }
5151

5252
@inlinable public
53-
var volume:Symbol.Volume { self.metadata.symbol }
53+
var redirects:[Unidoc.RedirectVertex] { self.interior.redirects }
5454
}
5555
extension Unidoc.Mesh
5656
{
57+
@inlinable public
58+
var volume:Symbol.Volume { self.metadata.symbol }
5759
@inlinable public
5860
var id:Unidoc.Edition { self.metadata.id }
5961
}

Sources/UnidocQueries/Unidoc.DB (ext).swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import FNV1
12
import MongoDB
23
import SHA1
34
import Symbols
@@ -63,3 +64,37 @@ extension Unidoc.DB
6364
}
6465
}
6566
}
67+
extension Unidoc.DB
68+
{
69+
public
70+
func redirect(exported:Unidoc.Shoot,
71+
from volume:Unidoc.Edition) async throws -> Unidoc.RedirectOutput?
72+
{
73+
try await self.query(with: Unidoc.RedirectByExportQuery.init(
74+
volume: volume,
75+
stem: exported.stem,
76+
hash: exported.hash),
77+
on: .nearest)
78+
}
79+
80+
public
81+
func redirect(visited shoot:Unidoc.Shoot,
82+
in package:Unidoc.Package) async throws -> Unidoc.RedirectOutput?
83+
{
84+
guard
85+
let visited:Unidoc.SearchbotCoverage = try await self.searchbotGrid.find(id: .init(
86+
trunk: package,
87+
shoot: shoot)),
88+
let redirect:Unidoc.RedirectOutput = try await self.query(
89+
with: Unidoc.RedirectByInternalHintQuery<Unidoc.Shoot>.init(
90+
volume: visited.ok,
91+
lookup: shoot),
92+
on: .nearest)
93+
else
94+
{
95+
return nil
96+
}
97+
98+
return redirect
99+
}
100+
}

0 commit comments

Comments
 (0)