Skip to content

Commit 9e32cf7

Browse files
authored
fix(DataStore): QueryPredicate translation (#961)
* PR for fixing nested sql statement * added one more unit test * rename opAhead to predicateIndex * added one integration test Co-authored-by: Guo <[email protected]>
1 parent 8d213de commit 9e32cf7

File tree

3 files changed

+301
-36
lines changed

3 files changed

+301
-36
lines changed

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/Storage/SQLite/SQLStatement+Condition.swift

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,30 +23,37 @@ private func translateQueryPredicate(from modelSchema: ModelSchema,
2323
namespace: Substring? = nil) -> SQLPredicate {
2424
var sql: [String] = []
2525
var bindings: [Binding?] = []
26-
var groupType: QueryPredicateGroupType = .and
2726
let indentPrefix = " "
2827
var indentSize = 1
29-
var groupOpened = false
3028

31-
func translate(_ pred: QueryPredicate) {
29+
func translate(_ pred: QueryPredicate, predicateIndex: Int, groupType: QueryPredicateGroupType) {
3230
let indent = String(repeating: indentPrefix, count: indentSize)
3331
if let operation = pred as? QueryPredicateOperation {
34-
let logicalOperator = groupOpened ? "" : "\(groupType.rawValue) "
3532
let column = operation.operator.columnFor(field: operation.field,
3633
namespace: namespace)
37-
sql.append("\(indent)\(logicalOperator)\(column) \(operation.operator.sqlOperation)")
34+
if predicateIndex == 0 {
35+
sql.append("\(indent)\(column) \(operation.operator.sqlOperation)")
36+
} else {
37+
sql.append("\(indent)\(groupType.rawValue) \(column) \(operation.operator.sqlOperation)")
38+
}
39+
3840
bindings.append(contentsOf: operation.operator.bindings)
39-
groupOpened = false
4041
} else if let group = pred as? QueryPredicateGroup {
4142
var shouldClose = false
42-
groupOpened = group.type != groupType
43-
if groupOpened {
43+
44+
if predicateIndex == 0 {
45+
sql.append("\(indent)(")
46+
} else {
4447
sql.append("\(indent)\(groupType.rawValue) (")
45-
groupType = group.type
46-
indentSize += 1
47-
shouldClose = true
4848
}
49-
group.predicates.forEach { translate($0) }
49+
50+
indentSize += 1
51+
shouldClose = true
52+
53+
for index in 0 ..< group.predicates.count {
54+
translate(group.predicates[index], predicateIndex: index, groupType: group.type)
55+
}
56+
5057
if shouldClose {
5158
indentSize -= 1
5259
sql.append("\(indent))")
@@ -57,7 +64,10 @@ private func translateQueryPredicate(from modelSchema: ModelSchema,
5764
}
5865
}
5966
}
60-
translate(predicate)
67+
68+
// the very first `and` is always prepended, using -1 for if statement checking
69+
// the very first `and` is to connect `where` clause with translated QueryPredicate
70+
translate(predicate, predicateIndex: -1, groupType: .and)
6171
return (sql.joined(separator: "\n"), bindings)
6272
}
6373

AmplifyPlugins/DataStore/AWSDataStoreCategoryPluginIntegrationTests/DataStoreLocalStoreTests.swift

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,39 @@ import AWSPluginsCore
1414
@testable import AmplifyTestCommon
1515
@testable import AWSDataStoreCategoryPlugin
1616

17+
// swfitlint:disable file_length
18+
// swiftlint:disable type_body_length
1719
class DataStoreLocalStoreTests: LocalStoreIntegrationTestBase {
1820

21+
/// - Given: 4 posts that has been saved
22+
/// - When:
23+
/// - query with grouped predicate
24+
/// - Then:
25+
/// - 2 post instances will be returned
26+
/// - second page returns the remaining 5 posts
27+
/// - the 15 retrieved posts have unique identifiers
28+
func testQueryWithGroupedQueryPredicateInput() throws {
29+
setUpLocalStoreForGroupedPredicateTest()
30+
var posts = [Post]()
31+
let queryFirstTimeSuccess = expectation(description: "Query post completed")
32+
let post = Post.keys
33+
let predicate = (post.id <= 1 && post.title == "title1")
34+
|| (post.rating > 2 && post.status == PostStatus.private)
35+
Amplify.DataStore.query(Post.self,
36+
where: predicate) { result in
37+
switch result {
38+
case .success(let returnPosts):
39+
posts.append(contentsOf: returnPosts)
40+
queryFirstTimeSuccess.fulfill()
41+
case .failure(let error):
42+
XCTFail("Error querying posts: \(error)")
43+
}
44+
}
45+
wait(for: [queryFirstTimeSuccess], timeout: 10)
46+
47+
XCTAssertEqual(posts.count, 2)
48+
}
49+
1950
/// - Given: 15 posts that has been saved
2051
/// - When:
2152
/// - query with pagination input given a page number and limit 10
@@ -366,4 +397,28 @@ class DataStoreLocalStoreTests: LocalStoreIntegrationTestBase {
366397
}
367398
return savedPosts
368399
}
400+
401+
func setUpLocalStoreForGroupedPredicateTest() {
402+
var savedPosts = [Post]()
403+
savedPosts.append(Post(id: "1", title: "title1", content: "content1",
404+
createdAt: .now(), rating: 1, status: .draft))
405+
savedPosts.append(Post(id: "2", title: "title2", content: "content2",
406+
createdAt: .now(), rating: 2, status: .private))
407+
savedPosts.append(Post(id: "3", title: "title3", content: "content3",
408+
createdAt: .now(), rating: 3, status: .draft))
409+
savedPosts.append(Post(id: "4", title: "title4", content: "content4",
410+
createdAt: .now(), rating: 4, status: .private))
411+
for post in savedPosts {
412+
let saveSuccess = expectation(description: "Save post completed")
413+
Amplify.DataStore.save(post) { result in
414+
switch result {
415+
case .success:
416+
saveSuccess.fulfill()
417+
case .failure(let error):
418+
XCTFail("Error saving post, \(error)")
419+
}
420+
}
421+
wait(for: [saveSuccess], timeout: 10)
422+
}
423+
}
369424
}

0 commit comments

Comments
 (0)