forked from juju/juju
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcollection.go
145 lines (130 loc) · 5.4 KB
/
collection.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
// Copyright 2012-2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package state
import (
"github.com/juju/errors"
"github.com/juju/mgo/v2"
"github.com/juju/mgo/v2/bson"
"github.com/juju/juju/mongo"
)
// modelStateCollection wraps a mongo.Collection, preserving the
// mongo.Collection interface and its Writeable behaviour. It will
// automatically modify query selectors and documents so that queries
// and inserts only interact with data for a single model (where
// possible).
type modelStateCollection struct {
mongo.WriteCollection
modelUUID string
queryTracker *queryTracker
}
// Writeable is part of the Collection interface.
func (c *modelStateCollection) Writeable() mongo.WriteCollection {
// Note that we can't delegate this to the embedded WriteCollection:
// that would return a writeable collection without any env-handling.
return c
}
// Count returns the number of documents in the collection that belong
// to the model that the modelStateCollection is filtering on.
func (c *modelStateCollection) Count() (int, error) {
if c.queryTracker != nil {
c.queryTracker.TrackRead(c.Name(), "count")
}
return c.WriteCollection.Find(bson.D{{"model-uuid", c.modelUUID}}).Count()
}
// Find performs a query on the collection. The query must be given as
// either nil or a bson.D.
//
// An "model-uuid" condition will always be added to the query to ensure
// that only data for the model being filtered on is returned.
//
// If a simple "_id" field selector is included in the query
// (e.g. "{{"_id", "foo"}}" the relevant model UUID prefix will
// be added on to the id. Note that more complex selectors using the
// "_id" field (e.g. using the $in operator) will not be modified. In
// these cases it is up to the caller to add model UUID
// prefixes when necessary.
func (c *modelStateCollection) Find(query interface{}) mongo.Query {
if c.queryTracker != nil {
c.queryTracker.TrackRead(c.Name(), query)
}
return c.WriteCollection.Find(c.mungeQuery(query))
}
// FindId looks up a single document by _id. If the id is a string the
// relevant model UUID prefix will be added to it. Otherwise, the
// query will be handled as per Find().
func (c *modelStateCollection) FindId(id interface{}) mongo.Query {
if c.queryTracker != nil {
c.queryTracker.TrackRead(c.Name(), bson.M{"_id": id})
}
if sid, ok := id.(string); ok {
return c.WriteCollection.FindId(ensureModelUUID(c.modelUUID, sid))
}
return c.Find(bson.D{{"_id", id}})
}
// Insert adds one or more documents to a collection. If the document
// id is a string the model UUID prefix will be automatically
// added to it. The model-uuid field will also be automatically added if
// it is missing. An error will be returned if an model-uuid field is
// provided but is the wrong value.
func (c *modelStateCollection) Insert(docs ...interface{}) error {
var mungedDocs []interface{}
for _, doc := range docs {
mungedDoc, err := mungeDocForMultiModel(doc, c.modelUUID, modelUUIDRequired)
if err != nil {
return errors.Trace(err)
}
mungedDocs = append(mungedDocs, mungedDoc)
}
return c.WriteCollection.Insert(mungedDocs...)
}
// Update finds a single document matching the provided query document and
// modifies it according to the update document.
//
// An "model-uuid" condition will always be added to the query to ensure
// that only data for the model being filtered on is returned.
//
// If a simple "_id" field selector is included in the query
// (e.g. "{{"_id", "foo"}}" the relevant model UUID prefix will
// be added on to the id. Note that more complex selectors using the
// "_id" field (e.g. using the $in operator) will not be modified. In
// these cases it is up to the caller to add model UUID
// prefixes when necessary.
func (c *modelStateCollection) Update(query interface{}, update interface{}) error {
return c.WriteCollection.Update(c.mungeQuery(query), update)
}
// UpdateId finds a single document by _id and modifies it according to the
// update document. The id must be a string or bson.ObjectId. The model
// UUID will be automatically prefixed on to the id if it's a string and the
// prefix isn't there already.
func (c *modelStateCollection) UpdateId(id interface{}, update interface{}) error {
if sid, ok := id.(string); ok {
return c.WriteCollection.UpdateId(ensureModelUUID(c.modelUUID, sid), update)
}
return c.WriteCollection.UpdateId(bson.D{{"_id", id}}, update)
}
// Remove deletes a single document using the query provided. The
// query will be handled as per Find().
func (c *modelStateCollection) Remove(query interface{}) error {
return c.WriteCollection.Remove(c.mungeQuery(query))
}
// RemoveId deletes a single document by id. If the id is a string the
// relevant model UUID prefix will be added on to it. Otherwise, the
// query will be handled as per Find().
func (c *modelStateCollection) RemoveId(id interface{}) error {
if sid, ok := id.(string); ok {
return c.WriteCollection.RemoveId(ensureModelUUID(c.modelUUID, sid))
}
return c.Remove(bson.D{{"_id", id}})
}
// RemoveAll deletes all documents that match a query. The query will
// be handled as per Find().
func (c *modelStateCollection) RemoveAll(query interface{}) (*mgo.ChangeInfo, error) {
return c.WriteCollection.RemoveAll(c.mungeQuery(query))
}
func (c *modelStateCollection) mungeQuery(inq interface{}) bson.D {
outq, err := mungeDocForMultiModel(inq, c.modelUUID, modelUUIDRequired|noModelUUIDInInput)
if err != nil {
panic(err)
}
return outq
}