forked from EdwinWalela/clover
-
Notifications
You must be signed in to change notification settings - Fork 0
/
document.go
131 lines (110 loc) · 3.1 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
package clover
import (
"encoding/json"
"strings"
)
// Document represents a document as a map.
type Document struct {
fields map[string]interface{}
}
// ObjectId returns the id of the document, provided that the document belongs to some collection. Otherwise, it returns the empty string.
func (doc *Document) ObjectId() string {
id := doc.Get(objectIdField)
if id == nil {
return ""
}
return id.(string)
}
// NewDocument creates a new empty document.
func NewDocument() *Document {
return &Document{
fields: make(map[string]interface{}),
}
}
// NewDocumentOf creates a new document and initializes it with the content of the provided map.
func NewDocumentOf(fields map[string]interface{}) *Document {
return &Document{
fields: fields,
}
}
// Copy returns a shallow copy of the underlying document.
func (doc *Document) Copy() *Document {
return &Document{
fields: copyMap(doc.fields),
}
}
func lookupField(name string, fieldMap map[string]interface{}, force bool) (map[string]interface{}, interface{}, string) {
fields := strings.Split(name, ".")
var exists bool
var f interface{}
currMap := fieldMap
for i, field := range fields {
f, exists = currMap[field]
m, isMap := f.(map[string]interface{})
if force {
if (!exists || !isMap) && i < len(fields)-1 {
m = make(map[string]interface{})
currMap[field] = m
f = m
}
} else if !exists {
return nil, nil, ""
}
if i < len(fields)-1 {
currMap = m
}
}
return currMap, f, fields[len(fields)-1]
}
// Has tells returns true if the document contains a field with the supplied name.
func (doc *Document) Has(name string) bool {
fieldMap, _, _ := lookupField(name, doc.fields, false)
return fieldMap != nil
}
// Get retrieves the value of a field. Nested fields can be accessed using dot.
func (doc *Document) Get(name string) interface{} {
_, v, _ := lookupField(name, doc.fields, false)
return v
}
// Set maps a field to a value. Nested fields can be accessed using dot.
func (doc *Document) Set(name string, value interface{}) {
m, _, fieldName := lookupField(name, doc.fields, true)
m[fieldName] = value
}
// SetAll sets each field specified in the input map to the corresponding value. Nested fields can be accessed using dot.
func (doc *Document) SetAll(values map[string]interface{}) {
for updateField, updateValue := range values {
doc.Set(updateField, updateValue)
}
}
// Unmarshal stores the document in the value pointed by v.
func (doc *Document) Unmarshal(v interface{}) error {
bytes, err := json.Marshal(doc.fields)
if err != nil {
return err
}
return json.Unmarshal(bytes, v)
}
func compareDocuments(first *Document, second *Document, sortOpts []SortOption) int {
for _, opt := range sortOpts {
field := opt.Field
direction := opt.Direction
firstHas := first.Has(field)
secondHas := second.Has(field)
if !firstHas && secondHas {
return -direction
}
if firstHas && !secondHas {
return direction
}
if firstHas && secondHas {
res, canCompare := compareValues(first.Get(field), second.Get(field))
if canCompare {
if res != 0 {
return res * direction
}
}
}
}
return 0
}