-
Notifications
You must be signed in to change notification settings - Fork 379
Expand file tree
/
Copy pathmetadata_dump.go
More file actions
161 lines (141 loc) · 4.49 KB
/
metadata_dump.go
File metadata and controls
161 lines (141 loc) · 4.49 KB
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
153
154
155
156
157
158
159
160
161
// Copyright (C) MongoDB, Inc. 2014-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package mongodump
import (
"context"
"fmt"
"io"
"github.com/mongodb/mongo-tools/common/bsonutil"
"github.com/mongodb/mongo-tools/common/db"
"github.com/mongodb/mongo-tools/common/intents"
"github.com/mongodb/mongo-tools/common/log"
"go.mongodb.org/mongo-driver/bson"
)
// Metadata holds information about a collection's options and indexes.
type Metadata struct {
Options bson.D `bson:"options,omitempty"`
Indexes []bson.D `bson:"indexes"`
UUID string `bson:"uuid,omitempty"`
CollectionName string `bson:"collectionName"`
Type string `bson:"type,omitempty"`
}
// IndexDocumentFromDB is used internally to preserve key ordering.
type IndexDocumentFromDB struct {
Options bson.M `bson:",inline"`
Key bson.D `bson:"key"`
}
// dumpMetadata gets the metadata for a collection and writes it
// in readable JSON format.
func (dump *MongoDump) dumpMetadata(
intent *intents.Intent,
buffer resettableOutputBuffer,
) (err error) {
meta := Metadata{
// We have to initialize Indexes to an empty slice, not nil, so that an empty
// array is marshaled into json instead of null. That is, {indexes:[]} is okay
// but {indexes:null} will cause assertions in our legacy C++ mongotools
Indexes: []bson.D{},
}
// The collection options were already gathered while building the list of intents.
meta.Options = intent.Options
// If a collection has a UUID, it was gathered while building the list of
// intents. Otherwise, it will be the empty string.
meta.UUID = intent.UUID
// Adding the collection name is useful if a long collection name results in a truncated
// bson or metadata file name, in which case the collection name can be found here.
meta.CollectionName = intent.C
if intent.Type != "" {
meta.Type = intent.Type
}
// Second, we read the collection's index information by either calling
// listIndexes (pre-2.7 systems) or querying system.indexes.
// We keep a running list of all the indexes
// for the current collection as we iterate over the cursor, and include
// that list as the "indexes" field of the metadata document.
log.Logvf(log.DebugHigh, "\treading indexes for `%v`", intent.Namespace())
session, err := dump.SessionProvider.GetSession()
if err != nil {
return err
}
if dump.OutputOptions.ViewsAsCollections || intent.IsView() {
log.Logvf(
log.DebugLow,
"not dumping indexes metadata for '%v' because it is a view",
intent.Namespace(),
)
} else {
// get the indexes
indexesIter, err := db.GetIndexes(session.Database(intent.DB).Collection(intent.C))
if err != nil {
return err
}
if indexesIter == nil {
log.Logvf(log.Always, "the collection %v appears to have been dropped after the dump started", intent.Namespace())
return nil
}
defer indexesIter.Close(context.Background())
ctx := context.Background()
for indexesIter.Next(ctx) {
indexOpts := &bson.D{}
err := indexesIter.Decode(indexOpts)
if err != nil {
return fmt.Errorf("error converting index: %v", err)
}
meta.Indexes = append(meta.Indexes, *indexOpts)
}
if err := indexesIter.Err(); err != nil {
return fmt.Errorf("error getting indexes for collection `%v`: %v", intent.Namespace(), err)
}
}
// Finally, we send the results to the writer as JSON bytes
jsonBytes, err := bsonutil.MarshalExtJSONWithBSONRoundtripConsistency(meta, true, false)
if err != nil {
return fmt.Errorf(
"error marshaling metadata json for collection `%v`: %v",
intent.Namespace(),
err,
)
}
err = intent.MetadataFile.Open()
if err != nil {
return err
}
defer func() {
closeErr := intent.MetadataFile.Close()
if err == nil && closeErr != nil {
err = fmt.Errorf(
"error writing metadata for collection `%v` to disk: %v",
intent.Namespace(),
closeErr,
)
}
}()
var f io.Writer
f = intent.MetadataFile
if buffer != nil {
buffer.Reset(f)
f = buffer
defer func() {
closeErr := buffer.Close()
if err == nil && closeErr != nil {
err = fmt.Errorf(
"error writing metadata for collection `%v` to disk: %v",
intent.Namespace(),
closeErr,
)
}
}()
}
_, err = f.Write(jsonBytes)
if err != nil {
err = fmt.Errorf(
"error writing metadata for collection `%v` to disk: %v",
intent.Namespace(),
err,
)
}
return
}