Skip to content

Commit 2befe18

Browse files
authored
feat: Add and, nor operators in QueryBuilder (#795)
1 parent 26b8698 commit 2befe18

File tree

7 files changed

+213
-3
lines changed

7 files changed

+213
-3
lines changed

packages/dart/CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## [3.1.6](https://github.com/parse-community/Parse-SDK-Flutter/compare/dart-3.1.5...dart-3.1.6) (2022-12-21)
2+
3+
### Bug Fixes
4+
5+
* Add `and`, `nor` operators in QueryBuilder ([#795](https://github.com/parse-community/Parse-SDK-Flutter/issues/795))
6+
17
## [3.1.5](https://github.com/parse-community/Parse-SDK-Flutter/compare/dart-3.1.4...dart-3.1.5) (2022-12-16)
28

39
### Bug Fixes

packages/dart/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,8 @@ QueryBuilder<ParseObject> mainQuery = QueryBuilder.or(
313313
var apiResponse = await mainQuery.query();
314314
```
315315

316+
To find objects that match several queries use __QueryBuilder.and__. To find objects that do not match any given query use __QueryBuilder.nor__.
317+
316318
The features available are:-
317319
* Equals
318320
* Contains

packages/dart/lib/src/base/parse_constants.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
part of flutter_parse_sdk;
22

33
// Library
4-
const String keySdkVersion = '3.1.5';
4+
const String keySdkVersion = '3.1.6';
55
const String keyLibraryName = 'Flutter Parse SDK';
66

77
// End Points

packages/dart/lib/src/network/parse_query.dart

+13-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,19 @@ class QueryBuilder<T extends ParseObject> {
1010
}
1111

1212
QueryBuilder.or(this.object, List<QueryBuilder<T>> list) {
13-
String query = '"\$or":[';
13+
_constructorInitializer(query: '"\$or":[', list: list);
14+
}
15+
16+
QueryBuilder.and(this.object, List<QueryBuilder<T>> list) {
17+
_constructorInitializer(query: '"\$and":[', list: list);
18+
}
19+
20+
QueryBuilder.nor(this.object, List<QueryBuilder<T>> list) {
21+
_constructorInitializer(query: '"\$nor":[', list: list);
22+
}
23+
24+
void _constructorInitializer(
25+
{required String query, required List<QueryBuilder<T>> list}) {
1426
for (int i = 0; i < list.length; ++i) {
1527
if (i > 0) {
1628
query += ',';

packages/dart/pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: parse_server_sdk
22
description: Dart plugin for Parse Server, (https://parseplatform.org), (https://back4app.com)
3-
version: 3.1.5
3+
version: 3.1.6
44
homepage: https://github.com/parse-community/Parse-SDK-Flutter
55

66
environment:

packages/dart/test/parse_query_test.dart

+188
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,193 @@ void main() {
6262
'where={"\$relatedTo":{"object":{"__type":"Pointer","className":"Post","objectId":"8TOXdXf3tz"},"key":"likes"}}');
6363
expect(result.query, expectedQuery.query);
6464
});
65+
66+
test('QueryBuilder.or', () async {
67+
final MockParseClient client = MockParseClient();
68+
69+
await Parse().initialize(
70+
'appId',
71+
'https://test.parse.com',
72+
debug: true,
73+
// to prevent automatic detection
74+
fileDirectory: 'someDirectory',
75+
// to prevent automatic detection
76+
appName: 'appName',
77+
// to prevent automatic detection
78+
appPackageName: 'somePackageName',
79+
// to prevent automatic detection
80+
appVersion: 'someAppVersion',
81+
);
82+
83+
ParseObject user = ParseObject("_User", client: client);
84+
var firstName = QueryBuilder<ParseObject>(user)
85+
..regEx('firstName', "Liam");
86+
87+
var lastName = QueryBuilder<ParseObject>(user)
88+
..regEx('lastName', "Johnson");
89+
90+
QueryBuilder<ParseObject> mainQuery = QueryBuilder.or(
91+
user,
92+
[firstName, lastName],
93+
);
94+
95+
when(client.get(
96+
any,
97+
options: anyNamed("options"),
98+
onReceiveProgress: anyNamed("onReceiveProgress"),
99+
)).thenAnswer((_) async => ParseNetworkResponse(
100+
statusCode: 200,
101+
data:
102+
"{\"results\": [{\"className\": \"_User\",\"objectId\": \"fqx5BECOME\",\"createdAt\": \"2022-10-25T06:04:47.138Z\",\"updatedAt\": \"2022-10-25T06:05:22.328Z\",\"firstName\": \"Liam1\",\"lastName\": \"Johnson1\"},{\"className\": \"_User\",\"objectId\": \"hAtRRYGrUO\",\"createdAt\": \"2022-01-24T15:53:48.396Z\",\"updatedAt\": \"2022-01-25T05:52:01.701Z\",\"firstName\": \"Liam2\",\"lastName\": \"Johnson2\"}]}"));
103+
104+
var response = await mainQuery.query();
105+
106+
expect(response.results?.first, isA<ParseObject>());
107+
108+
ParseObject parseObject = response.results?.first;
109+
110+
expect(parseObject.get<String>("firstName"), "Liam1");
111+
expect(parseObject.objectId, "fqx5BECOME");
112+
expect(parseObject.createdAt, DateTime.parse("2022-10-25T06:04:47.138Z"));
113+
expect(parseObject.updatedAt, DateTime.parse("2022-10-25T06:05:22.328Z"));
114+
115+
final Uri result = Uri.parse(verify(client.get(
116+
captureAny,
117+
options: anyNamed("options"),
118+
onReceiveProgress: anyNamed("onReceiveProgress"),
119+
)).captured.single);
120+
121+
expect(result.path, '/classes/_User');
122+
123+
final Uri expectedQuery = Uri(
124+
query:
125+
'where={"\$or":[{"firstName":{"\$regex":"Liam"}},{"lastName":{"\$regex":"Johnson"}}]}');
126+
expect(result.query, expectedQuery.query);
127+
});
128+
129+
test('QueryBuilder.and', () async {
130+
final MockParseClient client = MockParseClient();
131+
132+
await Parse().initialize(
133+
'appId',
134+
'https://test.parse.com',
135+
debug: true,
136+
// to prevent automatic detection
137+
fileDirectory: 'someDirectory',
138+
// to prevent automatic detection
139+
appName: 'appName',
140+
// to prevent automatic detection
141+
appPackageName: 'somePackageName',
142+
// to prevent automatic detection
143+
appVersion: 'someAppVersion',
144+
);
145+
146+
ParseObject user = ParseObject("_User", client: client);
147+
var firstName = QueryBuilder<ParseObject>(user)
148+
..regEx('firstName', "jak");
149+
150+
var lastName = QueryBuilder<ParseObject>(user)..regEx('lastName', "jaki");
151+
152+
QueryBuilder<ParseObject> mainQuery = QueryBuilder.and(
153+
user,
154+
[firstName, lastName],
155+
);
156+
157+
when(client.get(
158+
any,
159+
options: anyNamed("options"),
160+
onReceiveProgress: anyNamed("onReceiveProgress"),
161+
)).thenAnswer((_) async => ParseNetworkResponse(
162+
statusCode: 200,
163+
data:
164+
"{\"results\": [{\"className\": \"_User\",\"objectId\": \"fqx5BECOME\",\"createdAt\": \"2022-10-25T06:04:47.138Z\",\"updatedAt\": \"2022-10-25T06:05:22.328Z\",\"firstName\": \"jak1\",\"lastName\": \"jaki1\"},{\"className\": \"_User\",\"objectId\": \"hAtRRYGrUO\",\"createdAt\": \"2022-01-24T15:53:48.396Z\",\"updatedAt\": \"2022-01-25T05:52:01.701Z\",\"firstName\": \"jak2\",\"lastName\": \"jaki2\"}]}"));
165+
166+
var response = await mainQuery.query();
167+
168+
expect(response.results?.first, isA<ParseObject>());
169+
170+
ParseObject parseObject = response.results?.first;
171+
172+
expect(parseObject.get<String>("firstName"), "jak1");
173+
expect(parseObject.objectId, "fqx5BECOME");
174+
expect(parseObject.createdAt, DateTime.parse("2022-10-25T06:04:47.138Z"));
175+
expect(parseObject.updatedAt, DateTime.parse("2022-10-25T06:05:22.328Z"));
176+
177+
final Uri result = Uri.parse(verify(client.get(
178+
captureAny,
179+
options: anyNamed("options"),
180+
onReceiveProgress: anyNamed("onReceiveProgress"),
181+
)).captured.single);
182+
183+
expect(result.path, '/classes/_User');
184+
185+
final Uri expectedQuery = Uri(
186+
query:
187+
'where={"\$and":[{"firstName":{"\$regex":"jak"}},{"lastName":{"\$regex":"jaki"}}]}');
188+
expect(result.query, expectedQuery.query);
189+
});
190+
191+
test('QueryBuilder.nor', () async {
192+
final MockParseClient client = MockParseClient();
193+
194+
await Parse().initialize(
195+
'appId',
196+
'https://test.parse.com',
197+
debug: true,
198+
// to prevent automatic detection
199+
fileDirectory: 'someDirectory',
200+
// to prevent automatic detection
201+
appName: 'appName',
202+
// to prevent automatic detection
203+
appPackageName: 'somePackageName',
204+
// to prevent automatic detection
205+
appVersion: 'someAppVersion',
206+
);
207+
208+
ParseObject user = ParseObject("_User", client: client);
209+
var firstName = QueryBuilder<ParseObject>(user)
210+
..regEx('firstName', "Oliver");
211+
212+
var lastName = QueryBuilder<ParseObject>(user)
213+
..regEx('lastName', "Smith");
214+
215+
QueryBuilder<ParseObject> mainQuery = QueryBuilder.nor(
216+
user,
217+
[firstName, lastName],
218+
);
219+
220+
when(client.get(
221+
any,
222+
options: anyNamed("options"),
223+
onReceiveProgress: anyNamed("onReceiveProgress"),
224+
)).thenAnswer((_) async => ParseNetworkResponse(
225+
statusCode: 200,
226+
data:
227+
"{\"results\": [{\"className\": \"_User\",\"objectId\": \"fqx5BECOME\",\"createdAt\": \"2022-10-25T06:04:47.138Z\",\"updatedAt\": \"2022-10-25T06:05:22.328Z\",\"firstName\": \"Oliver1\",\"lastName\": \"Smith1\"},{\"className\": \"_User\",\"objectId\": \"hAtRRYGrUO\",\"createdAt\": \"2022-01-24T15:53:48.396Z\",\"updatedAt\": \"2022-01-25T05:52:01.701Z\",\"firstName\": \"Oliver2\",\"lastName\": \"Smith2\"}]}"));
228+
229+
var response = await mainQuery.query();
230+
231+
expect(response.results?.first, isA<ParseObject>());
232+
233+
ParseObject parseObject = response.results?.first;
234+
235+
expect(parseObject.get<String>("firstName"), "Oliver1");
236+
expect(parseObject.objectId, "fqx5BECOME");
237+
expect(parseObject.createdAt, DateTime.parse("2022-10-25T06:04:47.138Z"));
238+
expect(parseObject.updatedAt, DateTime.parse("2022-10-25T06:05:22.328Z"));
239+
240+
final Uri result = Uri.parse(verify(client.get(
241+
captureAny,
242+
options: anyNamed("options"),
243+
onReceiveProgress: anyNamed("onReceiveProgress"),
244+
)).captured.single);
245+
246+
expect(result.path, '/classes/_User');
247+
248+
final Uri expectedQuery = Uri(
249+
query:
250+
'where={"\$nor":[{"firstName":{"\$regex":"Oliver"}},{"lastName":{"\$regex":"Smith"}}]}');
251+
expect(result.query, expectedQuery.query);
252+
});
65253
});
66254
}

packages/flutter/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,8 @@ QueryBuilder<ParseObject> mainQuery = QueryBuilder.or(
351351
var apiResponse = await mainQuery.query();
352352
```
353353

354+
To find objects that match several queries use __QueryBuilder.and__. To find objects that do not match any given query use __QueryBuilder.nor__.
355+
354356
The features available are:-
355357
* Equals
356358
* Contains

0 commit comments

Comments
 (0)