Skip to content

Commit 63fb586

Browse files
authored
fix: Incorrect results when ParseQuery contains special characters (#866)
1 parent 19bbb6f commit 63fb586

File tree

5 files changed

+108
-28
lines changed

5 files changed

+108
-28
lines changed

packages/dart/CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## [5.1.2](https://github.com/parse-community/Parse-SDK-Flutter/compare/dart-5.1.1...dart-5.1.2) (2023-05-29)
2+
3+
### Bug Fixes
4+
5+
* Incorrect results when `ParseQuery` contains special characters ([#866](https://github.com/parse-community/Parse-SDK-Flutter/pull/866))
6+
17
## [5.1.1](https://github.com/parse-community/Parse-SDK-Flutter/compare/dart-5.1.0...dart-5.1.1) (2023-05-20)
28

39
### Bug Fixes

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 = '5.1.1';
4+
const String keySdkVersion = '5.1.2';
55
const String keyLibraryName = 'Flutter Parse SDK';
66

77
// End Points

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

+60-26
Original file line numberDiff line numberDiff line change
@@ -116,32 +116,49 @@ class QueryBuilder<T extends ParseObject> {
116116
limiters['include'] = concatenateArray(objectTypes);
117117
}
118118

119-
/// Returns an object where the [String] column starts with [value]
120-
void whereStartsWith(String column, String query,
121-
{bool caseSensitive = false}) {
119+
/// Add a constraint for finding objects where the String value in [column]
120+
/// starts with [prefix]
121+
void whereStartsWith(
122+
String column,
123+
String prefix, {
124+
bool caseSensitive = false,
125+
}) {
126+
prefix = Uri.encodeComponent(prefix);
127+
122128
if (caseSensitive) {
123129
queries.add(MapEntry<String, dynamic>(
124-
_singleQuery, '"$column":{"\$regex": "^$query"}'));
130+
_singleQuery, '"$column":{"\$regex": "^$prefix"}'));
125131
} else {
126132
queries.add(MapEntry<String, dynamic>(
127-
_singleQuery, '"$column":{"\$regex": "^$query", "\$options": "i"}'));
133+
_singleQuery, '"$column":{"\$regex": "^$prefix", "\$options": "i"}'));
128134
}
129135
}
130136

131-
/// Returns an object where the [String] column ends with [value]
132-
void whereEndsWith(String column, String query,
133-
{bool caseSensitive = false}) {
137+
/// Add a constraint for finding objects where the String value in [column]
138+
/// ends with [prefix]
139+
void whereEndsWith(
140+
String column,
141+
String prefix, {
142+
bool caseSensitive = false,
143+
}) {
144+
prefix = Uri.encodeComponent(prefix);
145+
134146
if (caseSensitive) {
135147
queries.add(MapEntry<String, dynamic>(
136-
_singleQuery, '"$column":{"\$regex": "$query\$"}'));
148+
_singleQuery, '"$column":{"\$regex": "$prefix\$"}'));
137149
} else {
138-
queries.add(MapEntry<String, dynamic>(
139-
_singleQuery, '"$column":{"\$regex": "$query\$", "\$options": "i"}'));
150+
queries.add(MapEntry<String, dynamic>(_singleQuery,
151+
'"$column":{"\$regex": "$prefix\$", "\$options": "i"}'));
140152
}
141153
}
142154

143-
/// Returns an object where the [String] column equals [value]
155+
/// Add a constraint to the query that requires a particular [column]'s value
156+
/// to be equal to the provided [value]
144157
void whereEqualTo(String column, dynamic value) {
158+
if (value is String) {
159+
value = Uri.encodeComponent(value);
160+
}
161+
145162
queries.add(_buildQueryWithColumnValueAndOperator(
146163
MapEntry<String, dynamic>(column, value), _noOperatorNeeded));
147164
}
@@ -174,8 +191,13 @@ class QueryBuilder<T extends ParseObject> {
174191
MapEntry<String, dynamic>(column, value), '\$gte'));
175192
}
176193

177-
/// Returns an object where the [String] column is not equal to value
194+
/// Add a constraint to the query that requires a particular [column]'s value
195+
/// to be not equal to the provided [value]
178196
void whereNotEqualTo(String column, dynamic value) {
197+
if (value is String) {
198+
value = Uri.encodeComponent(value);
199+
}
200+
179201
queries.add(_buildQueryWithColumnValueAndOperator(
180202
MapEntry<String, dynamic>(column, value), '\$ne'));
181203
}
@@ -229,26 +251,38 @@ class QueryBuilder<T extends ParseObject> {
229251
MapEntry<String, dynamic>(column, value), '\$regex'));
230252
}
231253

232-
/// Performs a search to see if [String] contains other string
233-
void whereContains(String column, String value,
234-
{bool caseSensitive = false}) {
254+
/// Add a constraint for finding String values that contain the provided
255+
/// [substring]
256+
void whereContains(
257+
String column,
258+
String substring, {
259+
bool caseSensitive = false,
260+
}) {
261+
substring = Uri.encodeComponent(substring);
262+
235263
if (caseSensitive) {
236264
queries.add(MapEntry<String, dynamic>(
237-
_singleQuery, '"$column":{"\$regex": "$value"}'));
265+
_singleQuery, '"$column":{"\$regex": "$substring"}'));
238266
} else {
239-
queries.add(MapEntry<String, dynamic>(
240-
_singleQuery, '"$column":{"\$regex": "$value", "\$options": "i"}'));
267+
queries.add(MapEntry<String, dynamic>(_singleQuery,
268+
'"$column":{"\$regex": "$substring", "\$options": "i"}'));
241269
}
242270
}
243271

244-
/// Powerful search for containing whole words. This search is much quicker than regex and can search for whole words including whether they are case sensitive or not.
245-
/// This search can also order by the score of the search
246-
void whereContainsWholeWord(String column, String query,
247-
{bool caseSensitive = false,
248-
bool orderByScore = true,
249-
bool diacriticSensitive = false}) {
272+
/// Powerful search for containing whole words. This search is much quicker
273+
/// than regex and can search for whole words including whether they are case
274+
/// sensitive or not. This search can also order by the score of the search
275+
void whereContainsWholeWord(
276+
String column,
277+
String searchTerm, {
278+
bool caseSensitive = false,
279+
bool orderByScore = true,
280+
bool diacriticSensitive = false,
281+
}) {
282+
searchTerm = Uri.encodeComponent(searchTerm);
283+
250284
queries.add(MapEntry<String, dynamic>(_singleQuery,
251-
'"$column":{"\$text":{"\$search":{"\$term": "$query", "\$caseSensitive": $caseSensitive , "\$diacriticSensitive": $diacriticSensitive }}}'));
285+
'"$column":{"\$text":{"\$search":{"\$term": "$searchTerm", "\$caseSensitive": $caseSensitive , "\$diacriticSensitive": $diacriticSensitive }}}'));
252286
if (orderByScore) {
253287
orderByAscending('\$score');
254288
keysToReturn(['\$score']);

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: The Dart SDK to connect to Parse Server. Build your apps faster with Parse Platform, the complete application stack.
3-
version: 5.1.1
3+
version: 5.1.2
44
homepage: https://github.com/parse-community/Parse-SDK-Flutter
55

66
environment:

packages/dart/test/src/network/parse_query_test.dart

+40
Original file line numberDiff line numberDiff line change
@@ -465,5 +465,45 @@ void main() {
465465
// assert
466466
expect(result.query.contains("%22object2%22,%22include%22"), true);
467467
});
468+
469+
test('the result query should contains encoded special characters values',
470+
() {
471+
// arrange
472+
final queryBuilder = QueryBuilder.name('Diet_Plans');
473+
474+
// act
475+
queryBuilder.whereEqualTo('some-column', 'some+test=test');
476+
queryBuilder.whereStartsWith('some-other-column', 'pre+fix');
477+
queryBuilder.whereEndsWith('some-column2', 'end+with');
478+
queryBuilder.whereNotEqualTo('some-column3', 'not+equal');
479+
queryBuilder.whereContains('some-column3', 'WW+E');
480+
queryBuilder.whereContainsWholeWord(
481+
'some-column3',
482+
'Programming++',
483+
orderByScore: false,
484+
);
485+
486+
// assert
487+
final queryString = queryBuilder.buildQuery();
488+
const encodedPlusChar = '%2B'; // +
489+
const encodedEqualChar = '%3D'; // =
490+
491+
expect(queryString, contains(encodedPlusChar));
492+
expect(queryString, contains(encodedEqualChar));
493+
494+
final expectedQueryString = StringBuffer();
495+
expectedQueryString.writeAll([
496+
'where={',
497+
'"some-column": "some${encodedPlusChar}test${encodedEqualChar}test",',
498+
'"some-other-column":{"\$regex": "^pre${encodedPlusChar}fix", "\$options": "i"},',
499+
'"some-column2":{"\$regex": "end${encodedPlusChar}with\$", "\$options": "i"},',
500+
'"some-column3":{ "\$ne":"not${encodedPlusChar}equal"},',
501+
'"some-column3":{"\$regex": "WW${encodedPlusChar}E", "\$options": "i"},',
502+
'"some-column3":{"\$text":{"\$search":{"\$term": "Programming$encodedPlusChar$encodedPlusChar", "\$caseSensitive": false , "\$diacriticSensitive": false }}}',
503+
'}',
504+
]);
505+
506+
expect(queryString, equals(expectedQueryString.toString()));
507+
});
468508
});
469509
}

0 commit comments

Comments
 (0)