ParseSDK总结.md 24 KB

启动数据库

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 创建对象并保存

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() 批量创建

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:基本查询

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:复杂查询

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)

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:直接更新字段

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)

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:删除单个对象

const query = new Parse.Query(GameScore);
query.get("objectId").then((object) => {
  return object.destroy();
}).then(() => {
  console.log("Object deleted successfully.");
});

方法 2:批量删除

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. 优化性能

    • 使用批量操作(如 saveAlldestroyAll)减少网络请求次数。
    • 在查询中使用 limitskip 实现分页,避免一次性加载过多数据。
  2. 保证数据一致性

    • 在高并发场景下,优先使用原子操作(如 incrementadd 等)。
  3. 实时性需求

    • 如果需要实时更新数据,可以结合 LiveQuery 实现。

详细解释与注释

第一段代码:实时订阅(LiveQuery)

// 创建一个 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)

// 创建一个 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 的原子性操作(如 incrementaddremove 等)是在服务器端实现的。
  • 当客户端调用 increment 时,Parse 会生成一个特殊的更新指令,并将其发送到服务器。
  • 服务器接收到指令后,直接在数据库层面执行原子操作,而不是先读取数据再写入。
  • 这种机制避免了客户端和服务器之间的多次通信,同时也防止了多个请求同时修改同一字段导致的竞争问题。

示例:increment 的底层实现

假设有一个字段 score,当前值为 100,两个用户同时尝试增加 5030

  • 如果没有原子性操作,可能导致以下情况:
    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 示例

// 创建一个 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); // 打印更新后的对象信息
});

原子性操作示例

// 创建一个 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 类创建一个查询对象,并指定目标类(表)。

const query = new Parse.Query("GameScore");

2. 添加查询条件

Parse 提供了多种方法来添加查询条件,例如等于、大于、小于、包含等。

常用条件
  • 等于equalTo

    query.equalTo("playerName", "John");
    
  • 不等于notEqualTo

    query.notEqualTo("playerName", "John");
    
  • 大于greaterThan

    query.greaterThan("score", 1000);
    
  • 小于lessThan

    query.lessThan("score", 500);
    
  • 范围greaterThanOrEqualTolessThanOrEqualTo

    query.greaterThanOrEqualTo("score", 500);
    query.lessThanOrEqualTo("score", 1000);
    
  • 包含在数组中containedIn

    query.containedIn("playerName", ["John", "Alice"]);
    
  • 不在数组中notContainedIn

    query.notContainedIn("playerName", ["Bob"]);
    
  • 存在性检查existsdoesNotExist

    query.exists("score"); // 检查 score 字段是否存在
    query.doesNotExist("score"); // 检查 score 字段是否不存在
    

3. 组合多个条件

可以使用 andor 方法组合多个条件。

AND 条件

默认情况下,所有条件会被视为 AND 关系。

query.equalTo("playerName", "John");
query.greaterThan("score", 1000);
OR 条件

使用 Parse.Query.or 方法创建 OR 条件。

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. 排序和分页

  • 排序

    query.ascending("score"); // 升序
    query.descending("score"); // 降序
    
  • 分页

    query.limit(10); // 每页返回 10 条记录
    query.skip(20);  // 跳过前 20 条记录
    

5. 包含关联对象

如果某个字段是关联对象,可以使用 include 方法加载相关数据。

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. 复杂查询示例

以下是一个复杂查询的完整示例,包含多个条件、排序和分页。

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

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. 分页查询:limitskip

代码解释

query.limit(10); // 每页返回 10 条记录
query.skip(20);  // 跳过前 20 条记录
  • query.limit(10)

    • 设置每次查询最多返回 10 条记录。
    • 这个值决定了每页显示的数据量。
  • query.skip(20)

    • 跳过前 20 条记录。
    • 这个值决定了从哪一条记录开始返回数据。

结合在一起的意思是:跳过前 20 条记录,然后返回接下来的 10 条记录。

分页逻辑

分页的核心思想是通过 skiplimit 控制查询结果的范围。例如:

  • 第一页: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 条。

解决方法

可以通过检查返回结果的长度来确定当前页实际有多少条数据。

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() 方法获取符合条件的总记录数。

const query = new Parse.Query("GameScore");

query.count().then((totalCount) => {
  console.log("Total records:", totalCount);
});

步骤 2:动态设置分页参数

根据用户请求的页码和每页大小,动态设置 skiplimit

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:结合总记录数计算总页数

根据总记录数和每页大小,计算总页数。

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 条记录。
  • 分页逻辑:通过动态调整 skiplimit 实现多页查询。
  • 如何知道每页有多少条数据:检查返回结果的长度(results.length)。

完整分页功能

  • 使用 query.count() 获取总记录数。
  • 动态设置 skiplimit 参数。
  • 计算总页数并实现分页导航。

通过以上方法,你可以灵活地实现分页功能,并高效地操作 Parse 数据库。