Flutter 关系数据查询教程:使用Parse进行复杂查询
在Flutter中解析关系查询
介绍
您已经看到 如何存储关系数据对象 使用Parse.Pointer和Parse.Relations数据类型。您已经了解到在Parse中,可以将任何ParseObject作为其他ParseObject中的值,从而建立它们之间的关系。在内部,Parse框架将把被引用的对象存储在一个地方以保持一致性。这可以在构建和运行复杂查询时为您提供额外的能力。
此外,您已经 学习了如何使用QueryBuilder,使用get可以从Back4App中检索单个ParseObject。还有许多其他方法可以使用QueryBuilder检索数据。
在本指南中,您将深入研究QueryBuilder类,并查看可以用来构建关系查询的方法。您将使用一个简单的数据库类和一些模拟数据来使用Flutter在Back4App上执行查询。
先决条件
要完成本教程,您需要:
- 一个连接到 Back4app 的 Flutter 应用。
- 一台运行 Android 或 iOS 的设备(或虚拟设备)。
目标
从 Flutter 应用查询存储在 Back4App 上的关系数据。
1 - 理解 QueryBuilder 类
任何 Parse 查询操作都使用 QueryBuilder 对象类型,它将帮助您在应用程序中从数据库中检索特定数据。
要创建一个新的 QueryBuilder,您需要将所需的 ParseObject 子类作为参数传递,该子类将包含您的查询结果。
了解 QueryBuilder 仅在调用检索方法查询后才会解析是至关重要的,因此可以设置查询并在实际调用之前链接多个修饰符。
1 // This will create your query
2 QueryBuilder<ParseObject> queryBook =
3 QueryBuilder<ParseObject>(ParseObject('Book'));
4
5 // The query will resolve only after calling this method
6 final ParseResponse apiResponse = await queryBook.query();
7
8 if (apiResponse.success && apiResponse.results != null) {
9 ....
10 } else {
11 ...
12 }你可以在官方文档中阅读更多关于 QueryBuilder 类的内容 这里.
在 Back4App 上使用 JavaScript 控制台
在你的 Back4App 应用程序的仪表板中,你会找到一个非常有用的 API 控制台,在其中你可以直接运行 JavaScript 代码。在本指南中,你将使用它在 Back4App 中存储数据对象。在你的应用主仪表板中,转到 Core->API 控制台->Javascript。

2 - 在 Back4app 上保存数据
要运行本指南中的查询,您首先需要为您的应用填充一些数据。
类包括: 作者, 书籍, 出版商 和 书店. 其中 书籍 与 出版商 有 1:N 关系,与 作者 有 N:N 关系,而 书店 与书籍有 N:N 关系。
这里是 Parse.Object 类的创建代码,所以请在您的 API 控制台中运行它:
1 // Add objects and create tables
2 // Authors
3 const AuthorA = new Parse.Object('Author');
4 AuthorA.set('name', 'Aaron Writer');
5 await AuthorA.save();
6
7 const AuthorB = new Parse.Object('Author');
8 AuthorB.set('name', 'Beatrice Novelist');
9 await AuthorB.save();
10
11 const AuthorC = new Parse.Object('Author');
12 AuthorC.set('name', 'Casey Columnist');
13 await AuthorC.save();
14
15 const AuthorD = new Parse.Object('Author');
16 AuthorD.set('name', 'Gary Stur');
17 await AuthorD.save();
18
19 const AuthorE = new Parse.Object('Author');
20 AuthorE.set('name', 'Mary Sue');
21 await AuthorE.save();
22
23 // Publishers
24 const PublisherA = new Parse.Object('Publisher');
25 PublisherA.set('name', 'Acacia Publishings');
26 await PublisherA.save();
27
28 const PublisherB = new Parse.Object('Publisher');
29 PublisherB.set('name', 'Birch Distributions');
30 await PublisherB.save();
31
32 const PublisherC = new Parse.Object('Publisher');
33 PublisherC.set('name', 'Acacia Distributions');
34 await PublisherC.save();
35
36
37 // Books
38 const BookA = new Parse.Object('Book');
39 BookA.set('title', 'A Love Story');
40 BookA.set('publisher', PublisherA);
41 BookA.set('publishingDate', new Date('05/07/1998'));
42 const BookARelation = BookA.relation("authors");
43 BookARelation.add(AuthorA);
44 await BookA.save();
45
46 const BookB = new Parse.Object('Book');
47 BookB.set('title', 'Benevolent Elves');
48 BookB.set('publisher', PublisherB);
49 BookB.set('publishingDate', new Date('11/31/2008'));
50 const BookBRelation = BookB.relation("authors");
51 BookBRelation.add(AuthorB);
52 await BookB.save();
53
54 const BookC = new Parse.Object('Book');
55 BookC.set('title', 'Can You Believe It?');
56 BookC.set('publisher', PublisherB);
57 BookC.set('publishingDate', new Date('08/21/2018'));
58 const BookCRelation = BookC.relation("authors");
59 BookCRelation.add(AuthorA);
60 BookCRelation.add(AuthorC);
61 await BookC.save();
62
63 // BookStore
64 const BookStoreA = new Parse.Object('BookStore');
65 BookStoreA.set('name', 'Books of Love');
66 const BookStoreARelation = BookStoreA.relation("books");
67 BookStoreARelation.add(BookA);
68 await BookStoreA.save();
69
70 const BookStoreB = new Parse.Object('BookStore');
71 BookStoreB.set('name', 'Fantasy Books');
72 const BookStoreBRelation = BookStoreB.relation("books");
73 BookStoreBRelation.add(BookB);
74 await BookStoreB.save();
75
76 const BookStoreC = new Parse.Object('BookStore');
77 BookStoreC.set('name', 'General Books');
78 const BookStoreCRelation = BookStoreC.relation("books");
79 BookStoreCRelation.add(BookA);
80 BookStoreCRelation.add(BookC);
81 await BookStoreC.save();
82
83 console.log('Success');运行此代码后,您现在应该在数据库中拥有 Author、Publisher、Book 和 BookStore 类。
您的新类应该看起来像这样:

现在让我们看看每个 QueryBuilder 方法的示例,以及它们的简要说明。
3 - 查询数据
现在您已经有了一个填充的类,我们可以执行一些关系查询。
现在您已经填充了所有类,我们可以在其中执行一些关系查询。
让我们开始通过出版商过滤书籍结果,搜索属于出版商 “Acacia Publishings”(或“出版商A”)的书籍,使用基本的 .whereEqualTo 方法进行关系指针查询:
1 // Get PublisherA object
2 final QueryBuilder<ParseObject> publisherQueryA =
3 QueryBuilder<ParseObject>(ParseObject('Publisher'))
4 ..whereEqualTo('name', 'Acacia Publishings');
5
6 final ParseResponse publisherResponse = await publisherQueryA.query();
7 if (!publisherResponse.success) {
8 return;
9 }
10 final publisherA = publisherResponse.results?.first as ParseObject;
11
12 // Query Books with PublisherA
13 final QueryBuilder<ParseObject> bookQuery =
14 QueryBuilder<ParseObject>(ParseObject('Book'))
15 ..whereEqualTo('publisher', publisherA);
16
17 final ParseResponse bookResponse = await bookQuery.query();
18 if (!bookResponse.success) {
19 return;
20 }
21
22 for (var book in bookResponse.results as List<ParseObject>) {
23 print(book.get('title'));
24 }现在让我们查询哪些书店对象包含出版日期晚于 2010 年 1 月 1 日的书籍,使用内部查询与 whereGreaterThan 方法,然后使用 whereMatchesQuery 方法:
1 // Create inner Book query
2 final QueryBuilder<ParseObject> bookQuery =
3 QueryBuilder<ParseObject>(ParseObject('Book'))
4 ..whereGreaterThan('publishingDate', DateTime(2010, 01, 01));
5
6 // Query BookStore using inner Book query
7 final QueryBuilder<ParseObject> bookStoreQuery =
8 QueryBuilder<ParseObject>(ParseObject('BookStore'))
9 ..whereMatchesQuery('books', bookQuery);
10
11 final ParseResponse bookStoreResponse = await bookStoreQuery.query();
12 if (!bookStoreResponse.success) {
13 return;
14 }
15
16 for (var b in bookStoreResponse.results as List<ParseObject>) {
17 print(b.get('name'));
18 }现在让我们创建另一个查询,寻找与书籍 Can You Believe It 相关的作者,使用 whereRelatedTo:
1 // Get Book object
2 final QueryBuilder<ParseObject> bookQuery =
3 QueryBuilder<ParseObject>(ParseObject('Book'))
4 ..whereEqualTo('title', 'Can You Believe It?');
5
6 final ParseResponse bookResponse = await bookQuery.query();
7 if (!bookResponse.success) {
8 return;
9 }
10
11 final book = bookResponse.results?.first as ParseObject;
12
13 // Get Author with relation with Book Can You Believe It?
14 final QueryBuilder<ParseObject> authorsQuery =
15 QueryBuilder<ParseObject>(ParseObject('Author'))
16 ..whereRelatedTo('authors', 'Book', book.objectId!);
17
18 final ParseResponse authorResponse = await authorsQuery.query();
19
20 // Let's show the results
21 if (authorResponse.success && authorResponse.results != null) {
22 for (var a in authorResponse.results! as List<ParseObject>) {
23 print(a.get('name'));
24 }
25 }4 - 从 Flutter 查询
现在让我们在 Flutter 应用中使用我们的示例查询,界面简单,包含一个显示结果的列表和三个用于调用查询的按钮。
打开你的 Flutter 项目,进入 main.dart 文件,清理所有代码,并用以下内容替换它:
1 import 'package:flutter/cupertino.dart';
2 import 'package:flutter/material.dart';
3 import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
4
5 void main() async {
6 WidgetsFlutterBinding.ensureInitialized();
7
8 final keyApplicationId = 'YOUR_APP_ID_HERE';
9 final keyClientKey = 'YOUR_CLIENT_KEY_HERE';
10 final keyParseServerUrl = 'https://parseapi.back4app.com';
11
12 await Parse().initialize(keyApplicationId, keyParseServerUrl,
13 clientKey: keyClientKey, debug: true);
14
15 runApp(MaterialApp(
16 title: 'Flutter - GeoPoint',
17 debugShowCheckedModeBanner: false,
18 home: HomePage(),
19 ));
20 }
21
22 class HomePage extends StatefulWidget {
23 @override
24 _HomePageState createState() => _HomePageState();
25 }
26
27 class _HomePageState extends State<HomePage> {
28 List<ParseObject> results = <ParseObject>[];
29 double selectedDistance = 3000;
30
31 void doQueryPointer() async {
32 // Get PublisherA object
33 final QueryBuilder<ParseObject> publisherQueryA =
34 QueryBuilder<ParseObject>(ParseObject('Publisher'))
35 ..whereEqualTo('name', 'Acacia Publishings');
36
37 final ParseResponse publisherResponse = await publisherQueryA.query();
38 if (!publisherResponse.success) {
39 return;
40 }
41 final publisherA = publisherResponse.results?.first as ParseObject;
42
43 // Query Books with PublisherA
44 final QueryBuilder<ParseObject> bookQuery =
45 QueryBuilder<ParseObject>(ParseObject('Book'))
46 ..whereEqualTo('publisher', publisherA);
47
48 final ParseResponse bookResponse = await bookQuery.query();
49
50 if (!bookResponse.success) {
51 setState(() {
52 results.clear();
53 });
54 } else {
55 setState(() {
56 results = bookResponse.results as List<ParseObject>;
57 });
58 }
59 }
60
61 void doQueryMatches() async {
62 // Create inner Book query
63 final QueryBuilder<ParseObject> bookQuery =
64 QueryBuilder<ParseObject>(ParseObject('Book'))
65 ..whereGreaterThan('publishingDate', DateTime(2010, 01, 01));
66
67 // Query BookStore using inner Book query
68 final QueryBuilder<ParseObject> bookStoreQuery =
69 QueryBuilder<ParseObject>(ParseObject('BookStore'))
70 ..whereMatchesQuery('books', bookQuery);
71
72 final ParseResponse bookStoreResponse = await bookStoreQuery.query();
73 if (!bookStoreResponse.success) {
74 setState(() {
75 results.clear();
76 });
77 } else {
78 setState(() {
79 results = bookStoreResponse.results as List<ParseObject>;
80 });
81 }
82 }
83
84 void doQueryRelatedTo() async {
85 // Get Book object
86 final QueryBuilder<ParseObject> bookQuery =
87 QueryBuilder<ParseObject>(ParseObject('Book'))
88 ..whereEqualTo('title', 'Can You Believe It?');
89
90 final ParseResponse bookResponse = await bookQuery.query();
91 if (!bookResponse.success) {
92 return;
93 }
94
95 final book = bookResponse.results?.first as ParseObject;
96
97 // Get Author with relation with Book Can You Believe It?
98 final QueryBuilder<ParseObject> authorsQuery =
99 QueryBuilder<ParseObject>(ParseObject('Author'))
100 ..whereRelatedTo('authors', 'Book', book.objectId!);
101
102 final ParseResponse authorResponse = await authorsQuery.query();
103
104 if (!authorResponse.success) {
105 setState(() {
106 results.clear();
107 });
108 } else {
109 setState(() {
110 results = authorResponse.results as List<ParseObject>;
111 });
112 }
113 }
114
115 void doClearResults() async {
116 setState(() {
117 results.clear();
118 });
119 }
120
121 @override
122 Widget build(BuildContext context) {
123 return Scaffold(
124 body: Padding(
125 padding: const EdgeInsets.all(16.0),
126 child: Column(
127 crossAxisAlignment: CrossAxisAlignment.stretch,
128 children: [
129 Container(
130 height: 200,
131 child: Image.network(
132 'https://blog.back4app.com/wp-content/uploads/2017/11/logo-b4a-1-768x175-1.png'),
133 ),
134 Center(
135 child: const Text('Flutter on Back4app - GeoPoint',
136 style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
137 ),
138 SizedBox(
139 height: 8,
140 ),
141 Container(
142 height: 50,
143 child: ElevatedButton(
144 onPressed: doQueryPointer,
145 child: Text('Query A'),
146 style: ElevatedButton.styleFrom(primary: Colors.blue)),
147 ),
148 SizedBox(
149 height: 8,
150 ),
151 Container(
152 height: 50,
153 child: ElevatedButton(
154 onPressed: doQueryMatches,
155 child: Text('Query B'),
156 style: ElevatedButton.styleFrom(primary: Colors.blue)),
157 ),
158 SizedBox(
159 height: 8,
160 ),
161 Container(
162 height: 50,
163 child: ElevatedButton(
164 onPressed: doQueryRelatedTo,
165 child: Text('Query C'),
166 style: ElevatedButton.styleFrom(primary: Colors.blue)),
167 ),
168 SizedBox(
169 height: 8,
170 ),
171 Container(
172 height: 50,
173 child: ElevatedButton(
174 onPressed: doClearResults,
175 child: Text('Clear results'),
176 style: ElevatedButton.styleFrom(primary: Colors.blue)),
177 ),
178 SizedBox(
179 height: 8,
180 ),
181 Text(
182 'Result List: ${results.length}',
183 ),
184 Expanded(
185 child: ListView.builder(
186 itemCount: results.length,
187 itemBuilder: (context, index) {
188 final o = results[index];
189 return Container(
190 padding: const EdgeInsets.all(4),
191 decoration:
192 BoxDecoration(border: Border.all(color: Colors.black)),
193 child: Text('${o.toString()}'),
194 );
195 }),
196 )
197 ],
198 ),
199 ));
200 }
201 }在您的应用程序仪表板中找到您的应用程序 ID 和客户端密钥凭据,网址为 Back4App 网站.
在main.dart中更新您的代码,使用您项目的ApplicationId和ClientKey在Back4app中.
- keyApplicationId = 应用程序 ID
- keyClientKey = 客户端密钥
运行项目,应用程序将如图所示加载。

结论
在本指南的最后,您了解了关系查询在 Parse 上是如何工作的,以及如何在 Flutter 应用程序中在 Back4App 上执行它们。在下一个指南中,您将学习如何在 Parse 中处理用户。