|
@@ -0,0 +1,721 @@
|
|
|
+### 启动数据库
|
|
|
+
|
|
|
+```cmd
|
|
|
+parse-dashboard --appId ncloudmaster --masterKey "SnkK12*&sunq2#@20!" --serverURL https://server.fmode.cn/parse --appName NovaCloud
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+# 不同的版本
|
|
|
+test&&server
|
|
|
+```
|
|
|
+
|
|
|
+```
|
|
|
+薛连丽 4/23 09:20:35
|
|
|
+https://cloud.fmode.cn/common/manage/_User;equalTo=type:user;rid=uhzm9anHNN
|
|
|
+
|
|
|
+薛连丽 4/23 09:20:47
|
|
|
+parse-dashboard --appId ncloudmaster --masterKey "NCM_@)@#" --serverURL https://server.fmode.cn/parse --appName NovaCloud
|
|
|
+The dashboard is now available at http://0.0.0.0:4040/
|
|
|
+
|
|
|
+薛连丽 4/23 09:30:29
|
|
|
+https://cloud.fmode.cn/这是总部后台
|
|
|
+
|
|
|
+Parse SDK 是一个强大的后端服务工具,用于快速构建应用程序的后端逻辑。它提供了对数据的增删改查(CRUD)操作接口,并支持多种实现方式。以下是 Parse SDK 中实现增删改查的详细说明,以及它们之间的区别和适用场景。
|
|
|
+```
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### **1. 增加数据(Create)**
|
|
|
+#### **方法 1:使用 `Parse.Object` 创建对象并保存**
|
|
|
+```javascript
|
|
|
+const GameScore = Parse.Object.extend("GameScore");
|
|
|
+const gameScore = new GameScore();
|
|
|
+
|
|
|
+gameScore.set("score", 1337);
|
|
|
+gameScore.set("playerName", "Sean Plott");
|
|
|
+
|
|
|
+gameScore.save().then((object) => {
|
|
|
+ console.log("New object created with objectId: " + object.id);
|
|
|
+}, (error) => {
|
|
|
+ console.error("Failed to create object: " + error.message);
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+#### **方法 2:使用 `Parse.Object.saveAll()` 批量创建**
|
|
|
+```javascript
|
|
|
+const objects = [
|
|
|
+ new GameScore({ score: 100, playerName: "Alice" }),
|
|
|
+ new GameScore({ score: 200, playerName: "Bob" })
|
|
|
+];
|
|
|
+
|
|
|
+Parse.Object.saveAll(objects).then((savedObjects) => {
|
|
|
+ console.log("Batch creation successful.");
|
|
|
+}, (error) => {
|
|
|
+ console.error("Batch creation failed: " + error.message);
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+#### **区别与适用场景**
|
|
|
+- **单条记录保存**:适用于需要逐条插入数据的场景。
|
|
|
+- **批量保存**:适合一次性插入多条数据,减少网络请求次数,提高性能。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### **2. 查询数据(Read)**
|
|
|
+#### **方法 1:基本查询**
|
|
|
+```javascript
|
|
|
+const query = new Parse.Query(GameScore);
|
|
|
+query.equalTo("playerName", "Sean Plott");
|
|
|
+
|
|
|
+query.find().then((results) => {
|
|
|
+ console.log("Found " + results.length + " results.");
|
|
|
+}, (error) => {
|
|
|
+ console.error("Error: " + error.message);
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+#### **方法 2:复杂查询**
|
|
|
+```javascript
|
|
|
+const query = new Parse.Query(GameScore);
|
|
|
+query.greaterThan("score", 1000);
|
|
|
+query.limit(10); // 限制返回结果数量
|
|
|
+query.skip(20); // 分页跳过前 20 条
|
|
|
+
|
|
|
+query.find().then((results) => {
|
|
|
+ console.log("Complex query results:", results);
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+#### **方法 3:实时订阅(LiveQuery)**
|
|
|
+```javascript
|
|
|
+const subscription = await new Parse.Query(GameScore)
|
|
|
+ .subscribe();
|
|
|
+
|
|
|
+subscription.on('create', (object) => {
|
|
|
+ console.log("New object created:", object);
|
|
|
+});
|
|
|
+
|
|
|
+subscription.on('update', (object) => {
|
|
|
+ console.log("Object updated:", object);
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+#### **区别与适用场景**
|
|
|
+- **基本查询**:适用于简单的条件查询。
|
|
|
+- **复杂查询**:适合需要过滤、排序、分页等操作的场景。
|
|
|
+- **实时订阅**:适合需要实时更新数据的应用,例如聊天应用或实时仪表盘。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### **3. 更新数据(Update)**
|
|
|
+#### **方法 1:直接更新字段**
|
|
|
+```javascript
|
|
|
+const query = new Parse.Query(GameScore);
|
|
|
+query.get("objectId").then((object) => {
|
|
|
+ object.set("score", 2000);
|
|
|
+ return object.save();
|
|
|
+}).then((updatedObject) => {
|
|
|
+ console.log("Updated object:", updatedObject);
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+#### **方法 2:原子操作(Atomic Operations)**
|
|
|
+```javascript
|
|
|
+const query = new Parse.Query(GameScore);
|
|
|
+query.get("objectId").then((object) => {
|
|
|
+ object.increment("score", 50); // 原子性增加分数
|
|
|
+ return object.save();
|
|
|
+}).then((updatedObject) => {
|
|
|
+ console.log("Incremented score:", updatedObject);
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+#### **区别与适用场景**
|
|
|
+- **直接更新**:适用于明确知道要更新哪些字段的场景。
|
|
|
+- **原子操作**:适合并发环境下需要保证数据一致性的场景,例如计数器、库存管理等。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### **4. 删除数据(Delete)**
|
|
|
+#### **方法 1:删除单个对象**
|
|
|
+```javascript
|
|
|
+const query = new Parse.Query(GameScore);
|
|
|
+query.get("objectId").then((object) => {
|
|
|
+ return object.destroy();
|
|
|
+}).then(() => {
|
|
|
+ console.log("Object deleted successfully.");
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+#### **方法 2:批量删除**
|
|
|
+```javascript
|
|
|
+const query = new Parse.Query(GameScore);
|
|
|
+query.find().then((objects) => {
|
|
|
+ return Parse.Object.destroyAll(objects);
|
|
|
+}).then(() => {
|
|
|
+ console.log("Batch deletion successful.");
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+#### **区别与适用场景**
|
|
|
+- **单条删除**:适用于逐条删除数据的场景。
|
|
|
+- **批量删除**:适合需要一次性删除多个对象的场景,减少网络请求次数。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### **总结:什么时候用什么方式更合适?**
|
|
|
+
|
|
|
+| 操作类型 | 方法 | 适用场景 |
|
|
|
+| ---------- | -------- | ---------------------------------------------------------- |
|
|
|
+| **Create** | 单条保存 | 插入少量数据时使用,逻辑简单。 |
|
|
|
+| | 批量保存 | 需要一次性插入大量数据时使用,提升性能。 |
|
|
|
+| **Read** | 基本查询 | 简单查询条件,如按某个字段筛选。 |
|
|
|
+| | 复杂查询 | 需要分页、排序、多条件组合查询时使用。 |
|
|
|
+| | 实时订阅 | 数据需要实时更新时使用,如聊天消息、实时通知等。 |
|
|
|
+| **Update** | 直接更新 | 明确知道要更新哪些字段时使用。 |
|
|
|
+| | 原子操作 | 并发环境下需要保证数据一致性时使用,如计数器、库存管理等。 |
|
|
|
+| **Delete** | 单条删除 | 删除少量数据时使用,逻辑简单。 |
|
|
|
+| | 批量删除 | 需要一次性删除多个对象时使用,提升性能。 |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### **最佳实践**
|
|
|
+1. **优化性能**:
|
|
|
+ - 使用批量操作(如 `saveAll` 和 `destroyAll`)减少网络请求次数。
|
|
|
+ - 在查询中使用 `limit` 和 `skip` 实现分页,避免一次性加载过多数据。
|
|
|
+
|
|
|
+2. **保证数据一致性**:
|
|
|
+ - 在高并发场景下,优先使用原子操作(如 `increment`、`add` 等)。
|
|
|
+
|
|
|
+3. **实时性需求**:
|
|
|
+ - 如果需要实时更新数据,可以结合 LiveQuery 实现。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### **详细解释与注释**
|
|
|
+
|
|
|
+#### **第一段代码:实时订阅(LiveQuery)**
|
|
|
+```javascript
|
|
|
+// 创建一个 Parse.Query 对象,用于查询 GameScore 表
|
|
|
+const subscription = await new Parse.Query(GameScore)
|
|
|
+ .subscribe(); // 订阅该查询的实时更新
|
|
|
+
|
|
|
+// 监听 'create' 事件,当有新的对象被创建时触发
|
|
|
+subscription.on('create', (object) => {
|
|
|
+ console.log("New object created:", object); // 打印新创建的对象信息
|
|
|
+});
|
|
|
+
|
|
|
+// 监听 'update' 事件,当有对象被更新时触发
|
|
|
+subscription.on('update', (object) => {
|
|
|
+ console.log("Object updated:", object); // 打印更新后的对象信息
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+##### **详细解释**
|
|
|
+1. **Parse.Query**:
|
|
|
+ - `Parse.Query` 是 Parse SDK 提供的一个类,用于构建对数据库的查询。
|
|
|
+ - 在这里,我们创建了一个针对 `GameScore` 表的查询。
|
|
|
+
|
|
|
+2. **`.subscribe()`**:
|
|
|
+ - LiveQuery 是 Parse 提供的一种实时数据同步机制。
|
|
|
+ - 调用 `.subscribe()` 方法后,客户端会与服务器建立 WebSocket 连接,监听指定查询范围内的数据变化。
|
|
|
+ - 当符合条件的数据发生变化(如新增、更新或删除)时,客户端会收到通知。
|
|
|
+
|
|
|
+3. **事件监听**:
|
|
|
+ - `subscription.on('create', callback)`:监听新对象的创建事件。当有新对象插入到 `GameScore` 表中且符合查询条件时,触发回调函数。
|
|
|
+ - `subscription.on('update', callback)`:监听对象更新事件。当已存在的对象被修改且符合查询条件时,触发回调函数。
|
|
|
+
|
|
|
+4. **适用场景**:
|
|
|
+ - 实时聊天应用:当用户发送消息时,其他用户可以立即看到。
|
|
|
+ - 实时仪表盘:例如股票价格变化、在线状态更新等。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+#### **第二段代码:原子性操作(Atomic Operations)**
|
|
|
+```javascript
|
|
|
+// 创建一个 Parse.Query 对象,用于查询 GameScore 表
|
|
|
+const query = new Parse.Query(GameScore);
|
|
|
+
|
|
|
+// 根据 objectId 查询特定的对象
|
|
|
+query.get("objectId").then((object) => {
|
|
|
+ // 使用原子操作 increment 增加 score 字段的值
|
|
|
+ object.increment("score", 50); // 原子性增加分数
|
|
|
+ return object.save(); // 保存更新后的对象
|
|
|
+}).then((updatedObject) => {
|
|
|
+ // 打印更新后的对象信息
|
|
|
+ console.log("Incremented score:", updatedObject);
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+##### **详细解释**
|
|
|
+1. **Parse.Query**:
|
|
|
+ - 同样使用 `Parse.Query` 构建查询,目标是获取 `GameScore` 表中某个特定的对象。
|
|
|
+
|
|
|
+2. **`.get("objectId")`**:
|
|
|
+ - 根据 `objectId` 获取指定的对象。`objectId` 是 Parse 数据库中每个对象的唯一标识符。
|
|
|
+
|
|
|
+3. **原子性操作 `increment`**:
|
|
|
+ - `increment` 是 Parse 提供的一种原子操作方法,用于在服务器端直接对字段进行增加或减少操作。
|
|
|
+ - 它的作用是保证在并发环境下不会出现竞争条件(Race Condition)。例如,多个用户同时对同一个字段进行操作时,`increment` 可以确保结果正确。
|
|
|
+
|
|
|
+4. **`.save()`**:
|
|
|
+ - 调用 `save()` 方法将更新后的对象保存到数据库。
|
|
|
+
|
|
|
+5. **适用场景**:
|
|
|
+ - 高并发场景:例如游戏分数更新、库存管理等。
|
|
|
+ - 需要保证数据一致性的场景。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### **原子性底层实现原理**
|
|
|
+
|
|
|
+#### **1. 原子性操作的工作原理**
|
|
|
+- Parse 的原子性操作(如 `increment`、`add`、`remove` 等)是在服务器端实现的。
|
|
|
+- 当客户端调用 `increment` 时,Parse 会生成一个特殊的更新指令,并将其发送到服务器。
|
|
|
+- 服务器接收到指令后,直接在数据库层面执行原子操作,而不是先读取数据再写入。
|
|
|
+- 这种机制避免了客户端和服务器之间的多次通信,同时也防止了多个请求同时修改同一字段导致的竞争问题。
|
|
|
+
|
|
|
+#### **示例:`increment` 的底层实现**
|
|
|
+假设有一个字段 `score`,当前值为 `100`,两个用户同时尝试增加 `50` 和 `30`:
|
|
|
+- 如果没有原子性操作,可能导致以下情况:
|
|
|
+ 1. 用户 A 读取 `score` 的值为 `100`。
|
|
|
+ 2. 用户 B 也读取 `score` 的值为 `100`。
|
|
|
+ 3. 用户 A 将 `score` 更新为 `150`。
|
|
|
+ 4. 用户 B 将 `score` 更新为 `130`。
|
|
|
+ 5. 最终结果为 `130`,丢失了用户 A 的更新。
|
|
|
+- 使用 `increment` 操作时:
|
|
|
+ 1. 用户 A 发送 `increment(50)` 指令。
|
|
|
+ 2. 用户 B 发送 `increment(30)` 指令。
|
|
|
+ 3. 服务器按顺序处理这两个指令,最终结果为 `180`。
|
|
|
+
|
|
|
+#### **2. 能否调整原子性实现方式?**
|
|
|
+- **不能直接调整底层实现**:
|
|
|
+ - Parse 的原子性操作是由服务器端实现的,开发者无法直接修改其底层逻辑。
|
|
|
+ - 这是为了保证操作的安全性和一致性。
|
|
|
+
|
|
|
+- **可以通过其他方式模拟类似行为**:
|
|
|
+ 如果需要自定义逻辑,可以通过事务(Transactions)或其他数据库工具实现类似效果。例如:
|
|
|
+ - 使用 MongoDB 的 `$inc` 操作符手动实现类似的原子性操作。
|
|
|
+ - 使用分布式锁(Distributed Locks)来控制并发访问。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### **完整注释版代码**
|
|
|
+
|
|
|
+#### **LiveQuery 示例**
|
|
|
+```javascript
|
|
|
+// 创建一个 Parse.Query 对象,用于查询 GameScore 表
|
|
|
+const subscription = await new Parse.Query(GameScore)
|
|
|
+ .subscribe(); // 订阅该查询的实时更新
|
|
|
+
|
|
|
+// 监听 'create' 事件
|
|
|
+subscription.on('create', (object) => {
|
|
|
+ console.log("New object created:", object); // 打印新创建的对象信息
|
|
|
+});
|
|
|
+
|
|
|
+// 监听 'update' 事件
|
|
|
+subscription.on('update', (object) => {
|
|
|
+ console.log("Object updated:", object); // 打印更新后的对象信息
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+#### **原子性操作示例**
|
|
|
+```javascript
|
|
|
+// 创建一个 Parse.Query 对象,用于查询 GameScore 表
|
|
|
+const query = new Parse.Query(GameScore);
|
|
|
+
|
|
|
+// 根据 objectId 查询特定的对象
|
|
|
+query.get("objectId").then((object) => {
|
|
|
+ // 使用原子操作 increment 增加 score 字段的值
|
|
|
+ object.increment("score", 50); // 原子性增加分数
|
|
|
+ return object.save(); // 保存更新后的对象
|
|
|
+}).then((updatedObject) => {
|
|
|
+ // 打印更新后的对象信息
|
|
|
+ console.log("Incremented score:", updatedObject);
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### **总结**
|
|
|
+1. **LiveQuery**:
|
|
|
+ - 适合实时数据同步场景,通过 WebSocket 实现高效的数据推送。
|
|
|
+2. **原子性操作**:
|
|
|
+ - 由服务器端实现,保证高并发环境下的数据一致性。
|
|
|
+ - 开发者无法直接调整底层实现,但可以通过其他方式模拟类似效果。
|
|
|
+3. **选择合适的工具**:
|
|
|
+ - 根据业务需求选择 LiveQuery 或原子性操作,或者结合两者使用,以满足不同的功能需求。
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+## PARSE SDK 是什么
|
|
|
+
|
|
|
+### **Parse SDK 是什么?**
|
|
|
+
|
|
|
+**Parse SDK** 是一个开源的后端服务平台,旨在帮助开发者快速构建 Web 和移动应用程序。它提供了一系列工具和服务,包括数据存储、用户认证、推送通知、文件存储等。Parse 的核心功能是通过其强大的 API 和 SDK 来实现的,支持多种编程语言(如 JavaScript、Java、Python 等)。
|
|
|
+
|
|
|
+Parse 的主要特点:
|
|
|
+1. **无需搭建后端**:Parse 提供了一个托管的后端服务,开发者无需关心服务器配置和维护。
|
|
|
+2. **灵活的数据模型**:支持类似 NoSQL 数据库的操作,数据以 JSON 格式存储。
|
|
|
+3. **实时功能**:通过 LiveQuery 支持实时数据同步。
|
|
|
+4. **跨平台支持**:支持多种客户端语言,便于开发跨平台应用。
|
|
|
+5. **易于扩展**:可以通过云代码(Cloud Code)编写自定义逻辑。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### **Parse SDK 定义的一些函数**
|
|
|
+
|
|
|
+Parse SDK 提供了许多内置函数来简化常见的操作,例如增删改查(CRUD)、查询、关系管理等。以下是一些常用的函数:
|
|
|
+
|
|
|
+#### **1. 数据操作**
|
|
|
+- `Parse.Object.extend(className)`:创建一个新的类(表)。
|
|
|
+- `object.set(key, value)`:设置对象的字段值。
|
|
|
+- `object.save()`:保存对象到数据库。
|
|
|
+- `object.destroy()`:删除对象。
|
|
|
+- `Parse.Object.saveAll(objects)`:批量保存对象。
|
|
|
+- `Parse.Object.destroyAll(objects)`:批量删除对象。
|
|
|
+
|
|
|
+#### **2. 查询操作**
|
|
|
+- `new Parse.Query(className)`:创建一个查询对象。
|
|
|
+- `query.get(objectId)`:根据 objectId 获取单个对象。
|
|
|
+- `query.find()`:执行查询并返回结果列表。
|
|
|
+- `query.first()`:返回查询结果中的第一个对象。
|
|
|
+- `query.equalTo(key, value)`:添加等于条件的过滤器。
|
|
|
+- `query.greaterThan(key, value)`:添加大于条件的过滤器。
|
|
|
+- `query.limit(n)`:限制返回结果的数量。
|
|
|
+- `query.skip(n)`:跳过前 n 条记录(用于分页)。
|
|
|
+- `query.include(key)`:包含关联对象的字段。
|
|
|
+- `query.subscribe()`:订阅实时更新(LiveQuery)。
|
|
|
+
|
|
|
+#### **3. 用户管理**
|
|
|
+- `Parse.User.signUp(username, password, attrs)`:注册新用户。
|
|
|
+- `Parse.User.logIn(username, password)`:用户登录。
|
|
|
+- `Parse.User.current()`:获取当前登录用户。
|
|
|
+- `Parse.User.logOut()`:登出用户。
|
|
|
+
|
|
|
+#### **4. 文件存储**
|
|
|
+- `new Parse.File(name, fileData)`:创建文件对象。
|
|
|
+- `file.save()`:上传文件到服务器。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### **如何自定义查询语句?**
|
|
|
+
|
|
|
+Parse SDK 提供了灵活的查询接口,允许开发者通过组合多个条件来自定义查询语句。以下是自定义查询的详细步骤和示例。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+#### **1. 创建查询对象**
|
|
|
+使用 `Parse.Query` 类创建一个查询对象,并指定目标类(表)。
|
|
|
+
|
|
|
+```javascript
|
|
|
+const query = new Parse.Query("GameScore");
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+#### **2. 添加查询条件**
|
|
|
+Parse 提供了多种方法来添加查询条件,例如等于、大于、小于、包含等。
|
|
|
+
|
|
|
+##### **常用条件**
|
|
|
+- **等于**:`equalTo`
|
|
|
+ ```javascript
|
|
|
+ query.equalTo("playerName", "John");
|
|
|
+ ```
|
|
|
+
|
|
|
+- **不等于**:`notEqualTo`
|
|
|
+ ```javascript
|
|
|
+ query.notEqualTo("playerName", "John");
|
|
|
+ ```
|
|
|
+
|
|
|
+- **大于**:`greaterThan`
|
|
|
+ ```javascript
|
|
|
+ query.greaterThan("score", 1000);
|
|
|
+ ```
|
|
|
+
|
|
|
+- **小于**:`lessThan`
|
|
|
+ ```javascript
|
|
|
+ query.lessThan("score", 500);
|
|
|
+ ```
|
|
|
+
|
|
|
+- **范围**:`greaterThanOrEqualTo` 和 `lessThanOrEqualTo`
|
|
|
+ ```javascript
|
|
|
+ query.greaterThanOrEqualTo("score", 500);
|
|
|
+ query.lessThanOrEqualTo("score", 1000);
|
|
|
+ ```
|
|
|
+
|
|
|
+- **包含在数组中**:`containedIn`
|
|
|
+ ```javascript
|
|
|
+ query.containedIn("playerName", ["John", "Alice"]);
|
|
|
+ ```
|
|
|
+
|
|
|
+- **不在数组中**:`notContainedIn`
|
|
|
+ ```javascript
|
|
|
+ query.notContainedIn("playerName", ["Bob"]);
|
|
|
+ ```
|
|
|
+
|
|
|
+- **存在性检查**:`exists` 和 `doesNotExist`
|
|
|
+ ```javascript
|
|
|
+ query.exists("score"); // 检查 score 字段是否存在
|
|
|
+ query.doesNotExist("score"); // 检查 score 字段是否不存在
|
|
|
+ ```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+#### **3. 组合多个条件**
|
|
|
+可以使用 `and` 或 `or` 方法组合多个条件。
|
|
|
+
|
|
|
+##### **AND 条件**
|
|
|
+默认情况下,所有条件会被视为 AND 关系。
|
|
|
+
|
|
|
+```javascript
|
|
|
+query.equalTo("playerName", "John");
|
|
|
+query.greaterThan("score", 1000);
|
|
|
+```
|
|
|
+
|
|
|
+##### **OR 条件**
|
|
|
+使用 `Parse.Query.or` 方法创建 OR 条件。
|
|
|
+
|
|
|
+```javascript
|
|
|
+const query1 = new Parse.Query("GameScore");
|
|
|
+query1.equalTo("playerName", "John");
|
|
|
+
|
|
|
+const query2 = new Parse.Query("GameScore");
|
|
|
+query2.greaterThan("score", 1000);
|
|
|
+
|
|
|
+const mainQuery = Parse.Query.or(query1, query2);
|
|
|
+mainQuery.find().then((results) => {
|
|
|
+ console.log("Results:", results);
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+#### **4. 排序和分页**
|
|
|
+- **排序**:
|
|
|
+ ```javascript
|
|
|
+ query.ascending("score"); // 升序
|
|
|
+ query.descending("score"); // 降序
|
|
|
+ ```
|
|
|
+
|
|
|
+- **分页**:
|
|
|
+ ```javascript
|
|
|
+ query.limit(10); // 每页返回 10 条记录
|
|
|
+ query.skip(20); // 跳过前 20 条记录
|
|
|
+ ```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+#### **5. 包含关联对象**
|
|
|
+如果某个字段是关联对象,可以使用 `include` 方法加载相关数据。
|
|
|
+
|
|
|
+```javascript
|
|
|
+const query = new Parse.Query("Post");
|
|
|
+query.include("author"); // 加载 Post 表中的 author 字段(假设是一个 Pointer 类型)
|
|
|
+query.find().then((posts) => {
|
|
|
+ posts.forEach((post) => {
|
|
|
+ console.log(post.get("author").get("username")); // 访问关联对象的字段
|
|
|
+ });
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+#### **6. 复杂查询示例**
|
|
|
+以下是一个复杂查询的完整示例,包含多个条件、排序和分页。
|
|
|
+
|
|
|
+```javascript
|
|
|
+const query = new Parse.Query("GameScore");
|
|
|
+
|
|
|
+// 添加查询条件
|
|
|
+query.equalTo("playerName", "John");
|
|
|
+query.greaterThan("score", 1000);
|
|
|
+query.lessThan("score", 5000);
|
|
|
+
|
|
|
+// 排序
|
|
|
+query.descending("score");
|
|
|
+
|
|
|
+// 分页
|
|
|
+query.limit(10);
|
|
|
+query.skip(20);
|
|
|
+
|
|
|
+// 执行查询
|
|
|
+query.find().then((results) => {
|
|
|
+ console.log("Found " + results.length + " results.");
|
|
|
+ results.forEach((result) => {
|
|
|
+ console.log(result.get("score"));
|
|
|
+ });
|
|
|
+}, (error) => {
|
|
|
+ console.error("Error:", error.message);
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### **总结**
|
|
|
+- Parse SDK 提供了丰富的内置函数,可以轻松实现增删改查、查询、用户管理和文件存储等功能。
|
|
|
+- 自定义查询语句时,可以通过组合多种条件(如等于、大于、包含等)来满足复杂的业务需求。
|
|
|
+- 如果需要更高级的功能(如联合查询、事务等),可以通过云代码(Cloud Code)实现自定义逻辑。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### **1. `query.include` 的作用与使用方法**
|
|
|
+
|
|
|
+#### **作用**
|
|
|
+`query.include` 用于在查询中加载关联对象的数据。Parse 数据库支持指针(Pointer)和关系(Relation)类型字段,这些字段通常指向其他表中的对象。默认情况下,查询结果只会返回指针的 `objectId`,而不会自动加载关联对象的详细信息。使用 `query.include` 可以在一次查询中同时加载关联对象的完整数据。
|
|
|
+
|
|
|
+#### **使用场景**
|
|
|
+- 当某个字段是 Pointer 类型,并且需要访问该字段指向的对象时。
|
|
|
+- 避免多次查询,提高性能。
|
|
|
+
|
|
|
+#### **示例**
|
|
|
+假设有一个 `Post` 表和一个 `User` 表,`Post` 表中的 `author` 字段是一个指向 `User` 表的指针。如果想在查询 `Post` 时同时获取 `author` 的详细信息(如用户名、邮箱等),可以使用 `include`。
|
|
|
+
|
|
|
+```javascript
|
|
|
+const query = new Parse.Query("Post");
|
|
|
+query.include("author"); // 加载 author 字段指向的 User 对象
|
|
|
+
|
|
|
+query.find().then((posts) => {
|
|
|
+ posts.forEach((post) => {
|
|
|
+ const author = post.get("author"); // 获取关联的 User 对象
|
|
|
+ console.log("Post Title:", post.get("title"));
|
|
|
+ console.log("Author Name:", author.get("username")); // 访问 User 对象的字段
|
|
|
+ });
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### **2. 分页查询:`limit` 和 `skip`**
|
|
|
+
|
|
|
+#### **代码解释**
|
|
|
+```javascript
|
|
|
+query.limit(10); // 每页返回 10 条记录
|
|
|
+query.skip(20); // 跳过前 20 条记录
|
|
|
+```
|
|
|
+
|
|
|
+- **`query.limit(10)`**:
|
|
|
+ - 设置每次查询最多返回 10 条记录。
|
|
|
+ - 这个值决定了每页显示的数据量。
|
|
|
+
|
|
|
+- **`query.skip(20)`**:
|
|
|
+ - 跳过前 20 条记录。
|
|
|
+ - 这个值决定了从哪一条记录开始返回数据。
|
|
|
+
|
|
|
+结合在一起的意思是:跳过前 20 条记录,然后返回接下来的 10 条记录。
|
|
|
+
|
|
|
+#### **分页逻辑**
|
|
|
+分页的核心思想是通过 `skip` 和 `limit` 控制查询结果的范围。例如:
|
|
|
+- 第一页:`skip(0)`,`limit(10)` → 返回第 1 到第 10 条记录。
|
|
|
+- 第二页:`skip(10)`,`limit(10)` → 返回第 11 到第 20 条记录。
|
|
|
+- 第三页:`skip(20)`,`limit(10)` → 返回第 21 到第 30 条记录。
|
|
|
+
|
|
|
+因此,`query.limit(10)` 和 `query.skip(20)` 的组合表示:**跳过前 20 条记录,返回接下来的 10 条记录**。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### **3. 如何知道每页有多少条数据?**
|
|
|
+
|
|
|
+#### **问题分析**
|
|
|
+- `query.limit(10)` 表示每页最多返回 10 条记录。
|
|
|
+- 实际返回的记录数可能少于 10 条,例如最后一页可能不足 10 条。
|
|
|
+
|
|
|
+#### **解决方法**
|
|
|
+可以通过检查返回结果的长度来确定当前页实际有多少条数据。
|
|
|
+
|
|
|
+```javascript
|
|
|
+query.limit(10);
|
|
|
+query.skip(20);
|
|
|
+
|
|
|
+query.find().then((results) => {
|
|
|
+ console.log("Number of records on this page:", results.length); // 当前页的记录数
|
|
|
+ results.forEach((result) => {
|
|
|
+ console.log(result.id); // 打印每条记录的 objectId
|
|
|
+ });
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### **4. 如何实现完整的分页功能?**
|
|
|
+
|
|
|
+为了实现完整的分页功能,你需要以下几个步骤:
|
|
|
+
|
|
|
+#### **步骤 1:计算总记录数**
|
|
|
+使用 `query.count()` 方法获取符合条件的总记录数。
|
|
|
+
|
|
|
+```javascript
|
|
|
+const query = new Parse.Query("GameScore");
|
|
|
+
|
|
|
+query.count().then((totalCount) => {
|
|
|
+ console.log("Total records:", totalCount);
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+#### **步骤 2:动态设置分页参数**
|
|
|
+根据用户请求的页码和每页大小,动态设置 `skip` 和 `limit`。
|
|
|
+
|
|
|
+```javascript
|
|
|
+const pageSize = 10; // 每页显示 10 条记录
|
|
|
+const currentPage = 3; // 当前页码(从 1 开始)
|
|
|
+
|
|
|
+const query = new Parse.Query("GameScore");
|
|
|
+query.limit(pageSize);
|
|
|
+query.skip((currentPage - 1) * pageSize); // 跳过前面的记录
|
|
|
+
|
|
|
+query.find().then((results) => {
|
|
|
+ console.log("Records on current page:", results);
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+#### **步骤 3:结合总记录数计算总页数**
|
|
|
+根据总记录数和每页大小,计算总页数。
|
|
|
+
|
|
|
+```javascript
|
|
|
+const pageSize = 10;
|
|
|
+let totalCount = 0;
|
|
|
+
|
|
|
+const query = new Parse.Query("GameScore");
|
|
|
+
|
|
|
+// 获取总记录数
|
|
|
+query.count().then((count) => {
|
|
|
+ totalCount = count;
|
|
|
+ const totalPages = Math.ceil(totalCount / pageSize); // 总页数
|
|
|
+ console.log("Total pages:", totalPages);
|
|
|
+
|
|
|
+ // 查询当前页数据
|
|
|
+ const currentPage = 3;
|
|
|
+ query.limit(pageSize);
|
|
|
+ query.skip((currentPage - 1) * pageSize);
|
|
|
+
|
|
|
+ return query.find();
|
|
|
+}).then((results) => {
|
|
|
+ console.log("Records on current page:", results);
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### **5. 总结**
|
|
|
+
|
|
|
+#### **`query.include`**
|
|
|
+- 用于加载关联对象的详细信息。
|
|
|
+- 使用场景:当需要访问指针字段指向的对象时。
|
|
|
+- 示例:`query.include("author")`。
|
|
|
+
|
|
|
+#### **分页**
|
|
|
+- `query.limit(n)`:设置每页返回的记录数。
|
|
|
+- `query.skip(n)`:跳过前 n 条记录。
|
|
|
+- 分页逻辑:通过动态调整 `skip` 和 `limit` 实现多页查询。
|
|
|
+- 如何知道每页有多少条数据:检查返回结果的长度(`results.length`)。
|
|
|
+
|
|
|
+#### **完整分页功能**
|
|
|
+- 使用 `query.count()` 获取总记录数。
|
|
|
+- 动态设置 `skip` 和 `limit` 参数。
|
|
|
+- 计算总页数并实现分页导航。
|
|
|
+
|
|
|
+通过以上方法,你可以灵活地实现分页功能,并高效地操作 Parse 数据库。
|