1package gocb
2
3import "fmt"
4
5// MapGet retrieves a single item from a map document by its key.
6func (b *Bucket) MapGet(key, path string, valuePtr interface{}) (Cas, error) {
7	tracespan := b.startKvOpTrace("MapGet")
8	defer tracespan.Finish()
9
10	frag, err := b.startLookupIn("", key, 0).Get(path).execute(tracespan.Context())
11	if err != nil {
12		return 0, err
13	}
14	err = frag.ContentByIndex(0, valuePtr)
15	if err != nil {
16		return 0, err
17	}
18	return frag.Cas(), nil
19}
20
21// MapRemove removes a specified key from the specified map document.
22func (b *Bucket) MapRemove(key, path string) (Cas, error) {
23	tracespan := b.startKvOpTrace("MapRemove")
24	defer tracespan.Finish()
25
26	frag, err := b.startMutateIn("", key, 0, 0, 0, 0, 0).Remove(path).execute(tracespan.Context())
27	if err != nil {
28		return 0, err
29	}
30	return frag.Cas(), nil
31}
32
33// MapSize returns the current number of items in a map document.
34// PERFORMANCE NOTICE: This currently performs a full document fetch...
35func (b *Bucket) MapSize(key string) (uint, Cas, error) {
36	var mapContents map[string]interface{}
37	cas, err := b.Get(key, &mapContents)
38	if err != nil {
39		return 0, 0, err
40	}
41
42	return uint(len(mapContents)), cas, nil
43}
44
45// MapAdd inserts an item to a map document.
46func (b *Bucket) MapAdd(key, path string, value interface{}, createMap bool) (Cas, error) {
47	for {
48		frag, err := b.startMutateIn("MapAdd", key, 0, 0, 0, 0, 0).
49			Insert(path, value, false).Execute()
50		if err != nil {
51			if IsKeyNotFoundError(err) && createMap {
52				data := make(map[string]interface{})
53				data[path] = value
54				cas, err := b.Insert(key, data, 0)
55				if err != nil {
56					if IsKeyExistsError(err) {
57						continue
58					}
59
60					return 0, err
61				}
62				return cas, nil
63			}
64			return 0, err
65		}
66		return frag.Cas(), nil
67	}
68}
69
70// ListGet retrieves an item from a list document by index.
71func (b *Bucket) ListGet(key string, index uint, valuePtr interface{}) (Cas, error) {
72	frag, err := b.LookupIn(key).Get(fmt.Sprintf("[%d]", index)).Execute()
73	if err != nil {
74		return 0, err
75	}
76	err = frag.ContentByIndex(0, valuePtr)
77	if err != nil {
78		return 0, err
79	}
80	return frag.Cas(), nil
81}
82
83// ListAppend inserts an item to the end of a list document.
84func (b *Bucket) ListAppend(key string, value interface{}, createList bool) (Cas, error) {
85	for {
86		frag, err := b.MutateIn(key, 0, 0).ArrayAppend("", value, false).Execute()
87		if err != nil {
88			if IsKeyNotFoundError(err) && createList {
89				var data []interface{}
90				data = append(data, value)
91				cas, err := b.Insert(key, data, 0)
92				if err != nil {
93					if IsKeyExistsError(err) {
94						continue
95					}
96
97					return 0, err
98				}
99				return cas, nil
100			}
101			return 0, err
102		}
103		return frag.Cas(), nil
104	}
105}
106
107// ListPrepend inserts an item to the beginning of a list document.
108func (b *Bucket) ListPrepend(key string, value interface{}, createList bool) (Cas, error) {
109	for {
110		frag, err := b.MutateIn(key, 0, 0).ArrayPrepend("", value, false).Execute()
111		if err != nil {
112			if IsKeyNotFoundError(err) && createList {
113				var data []interface{}
114				data = append(data, value)
115				cas, err := b.Insert(key, data, 0)
116				if err != nil {
117					if IsKeyExistsError(err) {
118						continue
119					}
120
121					return 0, err
122				}
123				return cas, nil
124			}
125			return 0, err
126		}
127		return frag.Cas(), nil
128	}
129}
130
131// ListRemove removes an item from a list document by its index.
132func (b *Bucket) ListRemove(key string, index uint) (Cas, error) {
133	frag, err := b.MutateIn(key, 0, 0).Remove(fmt.Sprintf("[%d]", index)).Execute()
134	if err != nil {
135		return 0, err
136	}
137	return frag.Cas(), nil
138}
139
140// ListSet replaces the item at a particular index of a list document.
141func (b *Bucket) ListSet(key string, index uint, value interface{}) (Cas, error) {
142	frag, err := b.MutateIn(key, 0, 0).Replace(fmt.Sprintf("[%d]", index), value).Execute()
143	if err != nil {
144		return 0, err
145	}
146	return frag.Cas(), nil
147}
148
149// ListSize returns the current number of items in a list.
150// PERFORMANCE NOTICE: This currently performs a full document fetch...
151func (b *Bucket) ListSize(key string) (uint, Cas, error) {
152	var listContents []interface{}
153	cas, err := b.Get(key, &listContents)
154	if err != nil {
155		return 0, 0, err
156	}
157
158	return uint(len(listContents)), cas, nil
159}
160
161// SetAdd adds a new value to a set document.
162func (b *Bucket) SetAdd(key string, value interface{}, createSet bool) (Cas, error) {
163	for {
164		frag, err := b.MutateIn(key, 0, 0).ArrayAddUnique("", value, false).Execute()
165		if err != nil {
166			if IsKeyNotFoundError(err) && createSet {
167				var data []interface{}
168				data = append(data, value)
169				cas, err := b.Insert(key, data, 0)
170				if err != nil {
171					if IsKeyExistsError(err) {
172						continue
173					}
174
175					return 0, err
176				}
177				return cas, nil
178			}
179			return 0, err
180		}
181		return frag.Cas(), nil
182	}
183}
184
185// SetExists checks if a particular value exists within the specified set document.
186// PERFORMANCE WARNING: This performs a full set fetch and compare.
187func (b *Bucket) SetExists(key string, value interface{}) (bool, Cas, error) {
188	var setContents []interface{}
189	cas, err := b.Get(key, &setContents)
190	if err != nil {
191		return false, 0, err
192	}
193
194	for _, item := range setContents {
195		if item == value {
196			return true, cas, nil
197		}
198	}
199
200	return false, 0, nil
201}
202
203// SetSize returns the current number of values in a set.
204// PERFORMANCE NOTICE: This currently performs a full document fetch...
205func (b *Bucket) SetSize(key string) (uint, Cas, error) {
206	var setContents []interface{}
207	cas, err := b.Get(key, &setContents)
208	if err != nil {
209		return 0, 0, err
210	}
211
212	return uint(len(setContents)), cas, nil
213}
214
215// SetRemove removes a single specified value from the specified set document.
216// WARNING: This relies on Go's interface{} comparison behaviour!
217// PERFORMANCE WARNING: This performs full set fetch, modify, store cycles.
218func (b *Bucket) SetRemove(key string, value interface{}) (Cas, error) {
219	for {
220		var setContents []interface{}
221		cas, err := b.Get(key, &setContents)
222		if err != nil {
223			return 0, err
224		}
225
226		foundItem := false
227		newSetContents := make([]interface{}, 0)
228		for _, item := range setContents {
229			if item == value {
230				foundItem = true
231			} else {
232				newSetContents = append(newSetContents, item)
233			}
234		}
235
236		if !foundItem {
237			return 0, ErrRangeError
238		}
239
240		cas, err = b.Replace(key, newSetContents, cas, 0)
241		if err != nil {
242			if IsKeyExistsError(err) {
243				// If this is just a CAS error, try again!
244				continue
245			}
246
247			return 0, err
248		}
249
250		return cas, nil
251	}
252}
253
254// QueuePush adds a new item to the end of a queue.
255func (b *Bucket) QueuePush(key string, value interface{}, createQueue bool) (Cas, error) {
256	return b.ListPrepend(key, value, createQueue)
257}
258
259// QueuePop pops the oldest item from a queue and returns it.
260func (b *Bucket) QueuePop(key string, valuePtr interface{}) (Cas, error) {
261	for {
262		getFrag, err := b.LookupIn(key).Get("[-1]").Execute()
263		if err != nil {
264			return 0, err
265		}
266
267		rmFrag, err := b.MutateIn(key, getFrag.Cas(), 0).Remove("[-1]").Execute()
268		if err != nil {
269			if IsKeyExistsError(err) {
270				// If this is just a CAS error, try again!
271				continue
272			}
273
274			return 0, err
275		}
276
277		err = getFrag.ContentByIndex(0, valuePtr)
278		if err != nil {
279			return 0, err
280		}
281
282		return rmFrag.Cas(), nil
283	}
284}
285
286// QueueSize returns the current size of a queue.
287func (b *Bucket) QueueSize(key string) (uint, Cas, error) {
288	var queueContents []interface{}
289	cas, err := b.Get(key, &queueContents)
290	if err != nil {
291		return 0, 0, err
292	}
293
294	return uint(len(queueContents)), cas, nil
295}
296