-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathdocument.go
194 lines (166 loc) · 6.05 KB
/
document.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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
package Sleep
import (
"labix.org/v2/mgo"
"labix.org/v2/mgo/bson"
"reflect"
)
type Document struct {
C *mgo.Collection
Model *Model
// a pointer to the schema
schema interface{}
isQueried bool
populated map[string]interface{}
//an instance of the schema that this document represents
schemaStruct interface{}
Found bool
Virtual *Virtual
}
// Save uses MongoDB's upsert command to either update an existing document or insert it into the collection.
// The document's schma MUST have an Id field.
func (d *Document) Save() error {
idField := reflect.ValueOf(d.schema).Elem().FieldByName("Id")
reflect.ValueOf(d.schema).MethodByName("PreSave").Call([]reflect.Value{})
id := idField.Interface()
_, err := d.C.UpsertId(id, d.schema)
if err != nil {
d.Found = true
reflect.ValueOf(d.schema).MethodByName("PostSave").Call([]reflect.Value{})
}
return err
}
// Use this method to check if this document is in fact populated with data from the database.
// Sleep suppresses mgo's ErrNotFound error and instead provides this interface for checking if results were returned.
func (d *Document) IsValid() bool {
return d.Found
}
//Same as Query.Populate() except it can be called on an existing document.
func (d *Document) Populate(fields ...string) error {
dummyQuery := d.Model.Find(bson.M{}).Populate(fields...)
err := dummyQuery.populateExec(d.schema)
return err
}
//Same as populate but used to populate only a single field. Its last parameter is a pointer
//to the variable to hold the value of the result
func (d *Document) PopulateOne(field string, value interface{}) error {
dummyQuery := d.Model.Find(bson.M{}).Populate(field)
err := dummyQuery.populateExec(d.schema)
if err != nil {
return err
}
populatedField, ok := d.populated[field]
if ok {
reflect.ValueOf(value).Elem().Set(reflect.ValueOf(populatedField))
}
return nil
}
//Same as Query.PopulateQuery() except the last parameter is a pointer to the variable to hold
//the value of the result.
func (d *Document) PopulateQuery(path string, q *Query, value interface{}) error {
dummyQuery := d.Model.Find(bson.M{}).PopulateQuery(path, q)
err := dummyQuery.populateExec(d.schema)
if err != nil {
return err
}
populatedField, ok := d.populated[path]
if ok {
reflect.ValueOf(value).Elem().Set(reflect.ValueOf(populatedField))
}
return nil
}
// Populated gives access to the document's populated fields. This method does NOT make a database query.
// It returns only existing populated fields.
//
//
// The path must be exactly the same as what was passed to Query.Populate() or Query.PopulateQuery() and is case sensitive.
//
// The result parameter must be of the correct Type.
// For example, if the field was defined as such in the schema:
//
// Foo: bson.ObjectId `model:"Bar"`
//
// Then the argument must be of type *Bar
// Or, if the field was defined as:
//
// Foo: []bson.ObjectId `model:"Bar"`
//
// Then the argument must be of type: *[]*Bar
//
//
func (d *Document) Populated(path string, result interface{}) bool {
value, ok := d.populated[path]
if !ok {
return ok
}
if reflect.ValueOf(result).Type().Kind() != reflect.Ptr {
panic("Expected a pointer, got a value")
}
reflect.ValueOf(result).Elem().Set(reflect.ValueOf(value).Elem())
return ok
}
// Removes the document from the database
func (d *Document) Remove() error {
reflect.ValueOf(d.schema).MethodByName("PreRemove").Call([]reflect.Value{})
id := reflect.ValueOf(d.schema).Elem().FieldByName("Id").Interface().(bson.ObjectId)
err := d.C.Remove(bson.M{"_id": id})
//if we want it gone and it's already gone, should we really freak out?
if err == mgo.ErrNotFound {
err = nil
}
if err != nil {
reflect.ValueOf(d.schema).MethodByName("PostRemove").Call([]reflect.Value{})
}
return err
}
//implement Apply function here
// it takes care of applying changes/merging to the document from another document
func (d *Document) Apply(update interface{}) error {
change := mgo.Change{
Update: update,
Upsert: true,
ReturnNew: true}
id := reflect.ValueOf(d.schema).Elem().FieldByName("Id").Interface().(bson.ObjectId)
_, err := d.C.FindId(id).Apply(change, d.schema)
return err
}
// implement populate function here so that a document is able to be populated
// after the initial query for its value
//implement stand-in hooks here
// PreSave is a stand-in method that can be implemented in the schema defination struct
// to be called before the document is saved to the database.
//
// The method should have a reciever that is a pointer to the schema type
func (d *Document) PreSave() {
}
// PostSave is a stand-in method that can be implemented in the schema defination struct
// to be called after the document is saved to the database.
//
// The method should have a reciever that is a pointer to the schema type
func (d *Document) PostSave() {
}
// PreRemove is a stand-in method that can be implemented in the schema defination struct
// to be called before the document is removed from the database.
//
// The method should have a reciever that is a pointer to the schema type
func (d *Document) PreRemove() {
}
// PostRemove is a stand-in method that can be implemented in the schema defination struct
// to be called after the document is removed from the database.
//
// The method should have a reciever that is a pointer to the schema type
func (d *Document) PostRemove() {
}
// OnCreate is a stand-in method that can be implemented in the schema defination struct
// to be called when the document is created using Sleep.Model.CreateDoc method.
// Use `OnResult()` to be called then the document is queried from the database.
//
// The method should have a reciever that is a pointer to the schema type
func (d *Document) OnCreate() {
}
// OnResult is a stand-in method that can be implemented in the schema defination struct
// to be called when the document is created from the results out of the database.
// Use `OnCreate()` to be called then the document is created using Sleep.Model.CreateDoc method
//
// The method should have a reciever that is a pointer to the schema type
func (d *Document) OnResult() {
}