forked from couchbaselabs/devguide-examples
-
Notifications
You must be signed in to change notification settings - Fork 1
/
subdoc-updating.cc
176 lines (148 loc) · 5.37 KB
/
subdoc-updating.cc
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
// This file is a slight variation from subdoc-retrieving.cc
#include <libcouchbase/couchbase.h>
#if LCB_VERSION < 0x020508
#error "Example requires libcouchbase 2.5.8 or greater!"
#endif
#include <libcouchbase/subdoc.h>
#include <string>
#include <vector>
struct Result {
std::string value;
lcb_error_t status;
Result() : status(LCB_MAX_ERROR) {}
bool valid() const
{
return status != LCB_MAX_ERROR;
}
void reset()
{
status = LCB_MAX_ERROR;
value.clear();
}
};
struct SubdocResults {
lcb_error_t status;
std::vector< Result > results;
void reset(size_t max_results)
{
results.resize(max_results);
for (size_t ii = 0; ii < max_results; ii++) {
results[ii].reset();
}
}
};
extern "C" {
static void sdmutate_callback(lcb_t, int, const lcb_RESPSUBDOC *resp)
{
// "cast" to specific callback type
SubdocResults *results = reinterpret_cast< SubdocResults * >(resp->cookie);
results->status = resp->rc;
if (resp->rc != LCB_SUCCESS && resp->rc != LCB_SUBDOC_MULTI_FAILURE) {
// If the error code is neither SUCCESS nor SUBDOC_MULTI_FAILURE then
// it means that there are no results and an error occurred during
// document access.
return;
}
lcb_SDENTRY ent = {};
size_t ii = 0;
while (lcb_sdresult_next(resp, &ent, &ii)) {
// Not all results are returned. Those that are returned
// can be correlated with the request path index by using
// lcb_SDENTRY::index
Result &r = results->results[ent.index];
r.status = ent.status;
if (ent.nvalue) {
r.value.assign(reinterpret_cast< const char * >(ent.value), ent.nvalue);
}
}
}
static void fulldoc_get_callback(lcb_t, int, const lcb_RESPGET *resp)
{
assert(resp->rc == LCB_SUCCESS);
printf("Document is now: %.*s\n", (int)resp->nvalue, (char *)resp->value);
}
}
int main(int, char **)
{
lcb_create_st crst = {};
lcb_t instance;
lcb_error_t rc;
crst.version = 3;
crst.v.v3.connstr = "couchbase://127.0.0.1/default";
crst.v.v3.username = "testuser";
crst.v.v3.passwd = "password";
lcb_create(&instance, &crst);
lcb_connect(instance);
lcb_wait(instance);
rc = lcb_get_bootstrap_status(instance);
if (rc != LCB_SUCCESS) {
printf("Unable to bootstrap cluster: %s\n", lcb_strerror_short(rc));
exit(1);
}
// Store a key first, so we know it will exist later on. In real production
// environments, we'd also want to install a callback for storage operations
// so we know if they succeeded
lcb_CMDSTORE scmd = {};
const char *key = "a_key";
const char *value = "{\"name\":\"mark\", \"array\":[1,2,3,4], \"email\":\"m@n.com\"}";
LCB_CMD_SET_KEY(&scmd, key, strlen(key));
LCB_CMD_SET_VALUE(&scmd, value, strlen(value));
scmd.operation = LCB_SET; // Upsert
lcb_store3(instance, NULL, &scmd);
lcb_wait(instance);
// Install the callback for GET operations. Note this can be done at any
// time before the operation is scheduled
lcb_install_callback3(instance, LCB_CALLBACK_SDMUTATE, reinterpret_cast< lcb_RESPCALLBACK >(sdmutate_callback));
SubdocResults my_results;
lcb_SDSPEC specs[3] = {};
lcb_CMDSUBDOC sdcmd = {};
LCB_CMD_SET_KEY(&sdcmd, key, strlen(key));
specs[0].sdcmd = LCB_SDCMD_ARRAY_ADD_LAST;
LCB_SDSPEC_SET_PATH(&specs[0], "array", strlen("array"));
LCB_SDSPEC_SET_VALUE(&specs[0], "42", 2);
specs[1].sdcmd = LCB_SDCMD_COUNTER;
LCB_SDSPEC_SET_PATH(&specs[1], "array[0]", strlen("array[0]"));
LCB_SDSPEC_SET_VALUE(&specs[1], "99", 2);
specs[2].sdcmd = LCB_SDCMD_DICT_UPSERT;
LCB_SDSPEC_SET_PATH(&specs[2], "description", strlen("description"));
LCB_SDSPEC_SET_VALUE(&specs[2], "\"just a dev\"", strlen("\"just a dev\""));
sdcmd.specs = specs;
sdcmd.nspecs = 3;
my_results.reset(3);
lcb_subdoc3(instance, &my_results, &sdcmd);
lcb_wait(instance);
// Should have three results
assert(my_results.status == LCB_SUCCESS);
for (size_t ii = 0; ii < my_results.results.size(); ++ii) {
const Result &r = my_results.results[ii];
if (!r.valid()) {
printf("[%lu]: No output\n", ii);
} else {
printf("[%lu]: %s\n", ii, r.value.c_str());
}
}
lcb_install_callback3(instance, LCB_CALLBACK_GET, (lcb_RESPCALLBACK)fulldoc_get_callback);
printf("=== Current doc ===\n");
lcb_CMDGET gcmd = {};
LCB_CMD_SET_KEY(&gcmd, key, strlen(key));
lcb_get3(instance, NULL, &gcmd);
lcb_wait(instance);
// Show how to set command options!
memset(&specs[0], 0, sizeof specs[0]);
const char *deep_path = "some.deep.path";
LCB_SDSPEC_SET_PATH(&specs[0], deep_path, strlen(deep_path));
LCB_SDSPEC_SET_VALUE(&specs[0], "true", 4);
specs[0].sdcmd = LCB_SDCMD_DICT_UPSERT;
sdcmd.nspecs = 1;
my_results.reset(1);
lcb_subdoc3(instance, &my_results, &sdcmd);
lcb_wait(instance);
// Should fail with LCB_SUBDOC_PATH_ENOENT
printf("upserting deep path fails: %s\n", lcb_strerror_short(my_results.results[0].status));
// Use a flag
specs[0].options = LCB_SDSPEC_F_MKINTERMEDIATES;
lcb_subdoc3(instance, &my_results, &sdcmd);
lcb_wait(instance);
printf("Status with MKINTERMEDIATES: %s\n", lcb_strerror_short(my_results.status));
lcb_destroy(instance);
}