Skip to content

Commit 8f7016f

Browse files
authored
CBL-7166 : Implement Merge-Dict conflict resolver in C TestServer (#245)
* CBL-7166 : Implement Merge-Dict conflict resolver in C TestServer * Implemented MergeDictConflictResolver. * Made variable names more clear in the existing MergeConflictResolver. * Corrected the Merge_Dict spec for error cases. * Fix bug not returning error for conflicting values
1 parent 1b1005b commit 8f7016f

2 files changed

Lines changed: 105 additions & 28 deletions

File tree

servers/c/src/cbl/CBLReplicationConflictResolver.cpp

Lines changed: 103 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include "CBLHeader.h"
55
#include CBL_HEADER(CouchbaseLite.h)
66

7+
#include "Defer.h"
8+
#include "Define.h"
79
#include "JSON.h"
810

911
using namespace std;
@@ -12,8 +14,8 @@ using namespace ts::support::json_util;
1214
namespace ts::cbl {
1315
class LocalWinsConflictResolver : public ConflictResolver {
1416
public:
15-
explicit LocalWinsConflictResolver(const ConflictResolverSpec &spec) : ConflictResolver(
16-
spec) {}
17+
explicit LocalWinsConflictResolver(const ConflictResolverSpec &spec)
18+
: ConflictResolver(spec) {}
1719

1820
const CBLDocument *
1921
resolve(const CBLDocument *localDoc, const CBLDocument *remoteDoc) override {
@@ -25,8 +27,8 @@ namespace ts::cbl {
2527

2628
class RemoteWinsConflictResolver : public ConflictResolver {
2729
public:
28-
explicit RemoteWinsConflictResolver(const ConflictResolverSpec &spec) : ConflictResolver(
29-
spec) {}
30+
explicit RemoteWinsConflictResolver(const ConflictResolverSpec &spec)
31+
: ConflictResolver(spec) {}
3032

3133
const CBLDocument *
3234
resolve(const CBLDocument *localDoc, const CBLDocument *remoteDoc) override {
@@ -38,8 +40,8 @@ namespace ts::cbl {
3840

3941
class DeleteConflictResolver : public ConflictResolver {
4042
public:
41-
explicit DeleteConflictResolver(const ConflictResolverSpec &spec) : ConflictResolver(
42-
spec) {}
43+
explicit DeleteConflictResolver(const ConflictResolverSpec &spec)
44+
: ConflictResolver(spec) {}
4345

4446
const CBLDocument *
4547
resolve(const CBLDocument *localDoc, const CBLDocument *remoteDoc) override {
@@ -51,8 +53,9 @@ namespace ts::cbl {
5153

5254
class MergeConflictResolver : public ConflictResolver {
5355
public:
54-
explicit MergeConflictResolver(const ConflictResolverSpec &spec) : ConflictResolver(
55-
spec) {
56+
explicit MergeConflictResolver(const ConflictResolverSpec &spec)
57+
: ConflictResolver(spec)
58+
{
5659
auto params = ConflictResolver::spec().params;
5760
_property = GetValue<string>(params, "property");
5861
}
@@ -61,31 +64,31 @@ namespace ts::cbl {
6164
resolve(const CBLDocument *localDoc, const CBLDocument *remoteDoc) override {
6265
if (!localDoc || !remoteDoc) { return nullptr; }
6366

64-
auto doc = CBLDocument_MutableCopy(remoteDoc);
67+
auto mergedDoc = CBLDocument_MutableCopy(remoteDoc);
68+
auto mergedValues = FLMutableArray_New();
6569

66-
FLSlice key = FLStr(_property.data());
67-
auto array = FLMutableArray_New();
68-
auto prop = CBLDocument_Properties(localDoc);
69-
auto value = FLDict_Get(prop, key);
70-
if (value) {
71-
FLMutableArray_AppendValue(array, value);
70+
FLSlice docKey = FLStr(_property.data());
71+
72+
auto localProps = CBLDocument_Properties(localDoc);
73+
auto localValue = FLDict_Get(localProps, docKey);
74+
if (localValue) {
75+
FLMutableArray_AppendValue(mergedValues, localValue);
7276
} else {
73-
FLMutableArray_AppendNull(array);
77+
FLMutableArray_AppendNull(mergedValues);
7478
}
7579

76-
prop = CBLDocument_Properties(remoteDoc);
77-
value = FLDict_Get(prop, key);
78-
if (value) {
79-
FLMutableArray_AppendValue(array, value);
80+
auto remoteProps = CBLDocument_Properties(remoteDoc);
81+
auto remoteValue = FLDict_Get(remoteProps, docKey);
82+
if (remoteValue) {
83+
FLMutableArray_AppendValue(mergedValues, remoteValue);
8084
} else {
81-
FLMutableArray_AppendNull(array);
85+
FLMutableArray_AppendNull(mergedValues);
8286
}
8387

84-
auto props = CBLDocument_MutableProperties(doc);
85-
FLMutableDict_SetArray(props, key, array);
86-
FLMutableArray_Release(array);
87-
88-
return doc;
88+
auto mergedProps = CBLDocument_MutableProperties(mergedDoc);
89+
FLMutableDict_SetArray(mergedProps, docKey, mergedValues);
90+
FLMutableArray_Release(mergedValues);
91+
return mergedDoc;
8992
}
9093

9194
static string name() { return "merge"; }
@@ -94,6 +97,78 @@ namespace ts::cbl {
9497
string _property;
9598
};
9699

100+
class MergeDictConflictResolver : public ConflictResolver {
101+
public:
102+
explicit MergeDictConflictResolver(const ConflictResolverSpec &spec)
103+
: ConflictResolver(spec)
104+
{
105+
auto params = ConflictResolver::spec().params;
106+
_property = GetValue<string>(params, "property");
107+
}
108+
109+
const CBLDocument *
110+
resolve(const CBLDocument *localDoc, const CBLDocument *remoteDoc) override {
111+
if (!localDoc || !remoteDoc) { return nullptr; }
112+
113+
auto mergedDoc = CBLDocument_MutableCopy(remoteDoc);
114+
auto mergedProps = CBLDocument_MutableProperties(mergedDoc);
115+
116+
FLSlice docKey = FLS(_property);
117+
118+
auto localProps = CBLDocument_Properties(localDoc);
119+
auto localDict = FLValue_AsDict(FLDict_Get(localProps, docKey)) ;
120+
121+
auto remoteProps = CBLDocument_Properties(remoteDoc);
122+
auto remoteDict = FLValue_AsDict(FLDict_Get(remoteProps, docKey));
123+
124+
if (!localDict || !remoteDict) {
125+
FLMutableDict_SetString(mergedProps, docKey, FLStr("Both values are not dictionary"));
126+
return mergedDoc;
127+
}
128+
129+
auto mergedDict = FLMutableDict_New();
130+
DEFER { FLMutableDict_Release(mergedDict); };
131+
132+
// Merge Local:
133+
{
134+
FLDictIterator iter;
135+
FLDictIterator_Begin(localDict, &iter);
136+
FLValue value;
137+
while (nullptr != (value = FLDictIterator_GetValue(&iter))) {
138+
FLString key = FLDictIterator_GetKeyString(&iter);
139+
FLMutableDict_SetValue(mergedDict, key, value);
140+
FLDictIterator_Next(&iter);
141+
}
142+
}
143+
144+
// Merge Remote:
145+
{
146+
FLDictIterator iter;
147+
FLDictIterator_Begin(remoteDict, &iter);
148+
FLValue value;
149+
while (nullptr != (value = FLDictIterator_GetValue(&iter))) {
150+
FLString key = FLDictIterator_GetKeyString(&iter);
151+
FLValue currentValue = FLDict_Get(mergedDict, key);
152+
if (currentValue && !FLValue_IsEqual(value, currentValue)) {
153+
string error = "Conflicting values found at key named '" + STR(key) + "'";
154+
FLMutableDict_SetString(mergedProps, docKey, FLS(error));
155+
return mergedDoc;
156+
}
157+
FLMutableDict_SetValue(mergedDict, key, value);
158+
FLDictIterator_Next(&iter);
159+
}
160+
}
161+
162+
FLMutableDict_SetDict(mergedProps, docKey, mergedDict);
163+
return mergedDoc;
164+
}
165+
166+
static string name() { return "merge-dict"; }
167+
168+
private:
169+
string _property;
170+
};
171+
97172
ConflictResolver *ConflictResolver::make_resolver(const ConflictResolverSpec &spec) {
98173
if (spec.name == LocalWinsConflictResolver::name()) {
99174
return new LocalWinsConflictResolver(spec);
@@ -103,6 +178,8 @@ namespace ts::cbl {
103178
return new DeleteConflictResolver(spec);
104179
} else if (spec.name == MergeConflictResolver::name()) {
105180
return new MergeConflictResolver(spec);
181+
} else if (spec.name == MergeDictConflictResolver::name()) {
182+
return new MergeDictConflictResolver(spec);
106183
}
107184
return nullptr;
108185
}

spec/api/conflict-resolvers.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@ Performs a merge of the specified dictionary property by combining the two dicti
4646

4747
* If one of the values is not a dictionary, the merged value will be set as:
4848
```
49-
{"error": "Both values are not dictionary"}
49+
{property: "Both values are not dictionary"}
5050
```
5151
* For any duplicated keys, the values must be the same, otherwise, the merged value will be set as:
5252
```
53-
{"error": "Conflicting values found at key named 'key-name'"}
53+
{property: "Conflicting values found at key named 'key-name'"}
5454
```
5555
5656
**name** : `merge-dict`

0 commit comments

Comments
 (0)