LeanCloud基本使用

LeanCloud 数据存储开发指南 — Javascript

数据存储是云服务提供的核心功能之一,可用于存放和查询应用数据。

// 以下示例如何创建一个对象并将其存入云端。
// 声明class 
const Todo = AV.Object.extent('Todo');

// 创建对象
const todo = new Todo();

// 为属性赋值
todo.set('title','工程师周会');
todo.set('content','周二两点,全体成员');

// 将对象保存到云端
todo.save().then((todo) => {
// 成功保存之后,执行其他逻辑
console.log(`保存成功。objectId:${todo.id}`);
},(error) => {
// 异常处理
});

对象

AV.Object

Av.Object 是云服务对复杂对象的封装,每个AV.Object包含与JSON格式兼容的属性值对(也称键值对,key-values pairs)。这个数据是无模式化的,意味着你不需要提前标注每个AV.Object上有哪些key,你只需要随意设置键值对就可以,云端会保存它。
比如说,一个保存着单个Todo的AV.Object可能会包含如下数据:

title:"给小林发邮件确认会议时间",
isComplete:false,
priority:2,
tags:["工作","销售"]
数据类型

AV.Object还支持两种特殊的数据类型Pointer和File,可以分别用来存储指向其他AV.Object的指针以及二进制数据。
AV.Object同时还支持GeoPointer,可以用来存储地理位置信息。示例如下:

// 基本类型
const bool = true;
const number = 2021;
const string = `${number} 流行音乐榜单`;
const date = new Date();
const array = [string,number];
const object = {
number:number,
string:string
};

// 构建对象
const TestObject = AV.Object.extend('TestObject');
const testObject = new TestObject();
testObject.set('testNumber',number);
testObject.set('testString',string);
testObject.set('testDate',date);
testObject.set('testArray',array);
testObject.set('testObject',object);
testObject.save();

不推荐在AV.Object里面存储图片、文档等大型二进制数据。每个AV.Object 的大小不应超过128KB。如需存储大型文件,可创建AV.File实例并将其关联到AV.Object的某个属性上。
注:时间类型在云端将会以UTC时间格式存储,但是客户端在读取之后会转化为本地时间。
云服务控制台 > 数据存储 > 结构化数据 中展示的日期数据也会依据操作系统的时区进行转换。一个例外是当你通过REST API 获得数据时,这些数据将以UTC呈现。你可以手动对她们进行转换。

构建对象

下面的代码构建了一个class为Todo的AV.Object;

// 为AV.Object 创建子类
const Todo = AV.Object.extend('Todo');

// 为该类创建一个新实例
const todo = new Todo();

// 你还可以直接使用AV.Object 的构造器
const todo = new AV.Object('Todo');

在构建对象时,为了使云端知道对象属于那个class,需要将class的名字作为参数传入。你可以将云服务里面的class比作关系数据库里面的表。一个class的名字必须以字母开头,且只能包含数字、字母和下划线。
注意,如果你的应用时不时出现Maximum call stack size exceeded 异常,可能是因为在循环或回调中调用了AV.Object.extend。有两种方法可以避免这种异常:一种是在循环或回调外声明class,确保不会对一个class执行多次AV.Object.extend;另一种是将SDK更换到1.4或更高版本。

如果你使用的是ES6,还可以将extends 关键字来创建AV.Object的子类,然而SDK无法自动识别你创建的子类。你需要通过这种方式手动注册一下。

class Todo extends AV.Object {
// 自定义属性和方法
}
// 注册子类
AV.Object.register(Todo);

这样就能在AV.Object的子类中添加额外的方法和属性了。

保存对象

下面的代码将一个calss 为Todo的对象存入云端:

// 声明class
const Todo = AV.Object.extend('Todo');

// 构建对象
const todo = new Todo();

// 为属性赋值
todo.set('title','马拉松报名');
todo.set('priority',2);

// 将对象保存到云端
todo.save().then((todo) => {
// 成功保存之后,执行其他逻辑
console.log(`保存成功。objectId:${todo.id}`);
}.(error) => {
// 异常处理
});

为了确认对象已经保存成功,我们可以到云服务控制台 > 数据存储 > 结构化数据 > Todo 里面看,应该会有一行新的数据产生。点一下这个数据的ObjectId,应该能看到类似这样的内容:

{
  "title":     "马拉松报名",
  "priority":  2,
  "ACL": {
    "*": {
      "read":  true,
      "write": true
    }
  },
  "objectId":  "582570f38ac247004f39c24b",
  "createdAt": "2021-09-24T07:14:15.549Z",
  "updatedAt": "2017-09-24T07:14:15.549Z"
}

注意:无需在云服务控制台 > 数据存储 > 结构化数据 里面创建新的Todo class 即可运行前面的代码。如果class不存在,它将自动创建。
以下是一些对象的内置属性,会在对象保存时自动创建,无需手动指定:

  • ObjectId:String(该对象唯一的ID 标识);
  • ACL:AV.ACL (该对象的权限控制,实际上是一个JSON对象,控制台做了展现优化;
  • createdAt:Date(该对象被创建的时间);
  • updatedAt:Date(该对象最后一次被修改的时间)。
    这些属性的值会在对象并不被存入云端时自动填入,代码中尚未保存的AV.object不存在这些属性。
    属性名(keys)只能包含字母,数字,下划线。自定义属性不得以双下划线开头或与任何系统保留字段和内置属性(ACL、class Name、createdAt、objectId、updatedAt)重名,无论大小写。
    属性值(values)可以时字符串、数字、布尔值、数组或字典(任何能以JSON编码的数据)。推荐使用驼峰命名法为类和属性来取名。类,采用大驼峰法,如CustomData。属性,采用小驼峰法,如imageUrl。
获取对象

对于已经保存到云端的AV.Object,可以通过它的objectId将其取回:

const query = new AV.Query('Todo');
query.get('582570f38ac247004f39c24b').then((todo) => {
// const title = todo.get('title');
const priority = todo.get('priority');

// 获取内置属性
const objectId = todo.id;
const updatedAt = todo.updatedAt;
const createdAt = todo.createdAt;
});

对象拿到之后,可以通过get方法来获取各个属性的值。注意objectId,updated和createdAt这三个内置属性不能通过get获取或通过set修改,只能由云端自动进行填充。尚未保存的AV.object不存在这些属性。
如果你试图获取一个不存在的属性,SDK不会报错,而是会返回undefined。
如果需要一次性获取返回对象的所有属性(比如进行数据绑定)而非显式的调用get,可以利用AV.object实例的toJSON方法:

const query = new AV.Query('Todo');
query.get('582570f38ac247004f39c24b').then((todo) => {
console.log(todo.toJSON());
});
// {
  //   createdAt: "2017-03-08T11:25:07.804Z",
  //   objectId: "582570f38ac247004f39c24b",
  //   priority: 2,
  //   title: "工程师周会",
  //   updatedAt: "2017-03-08T11:25:07.804Z"
  // }
同步对象

当云端数据发生改变时,你可以调用fetch方法来刷新对象,使之与云端数据同步:

const todo = AV.Object.creat.createWithoutData('Todo','582570f38ac247004f39c24b');
todo.featch().then((todo) => {
// todo 已刷新
});

刷新操作会强行使用云端属性值覆盖本地的属性。因此如果本地由属性修改,featch操作会丢弃这些修改。为避免这种情况,你可以在刷新时指定需要刷新的属性,这样只有指定的属性会被刷新(包括内置属性objectId,createdAt,updatedAt),其他属性不受影响。

const todo = AV.Object.createWithoutData('Todo','582570f38ac247004f39c24b');
todo.featch({
keys:'priority,location'
}).then((todo) => {
// 只有 priority和location会被获取和刷新
});
更新对象

要更新一个对象,只需指定需要更新的属性名和属性值,然后调用save方法。例如:

const todo = AV。Object.createWithoutData('Todo','582570f38ac247004f39c24b');
todo.set('content','这周周会改到周三下午三点。');
todo.save();

云服务会自动识别需要更新的属性并将对应的数据发往云端,未更新的属性会保持原样。
如果想要查看哪些属性由尚未保存的修改,可以调用dirtykeys方法:

todo.dirtykeys() // ['content']

如果希望撤销尚未保存的修改,可以调用revert方法。直接调用revert()将撤销所有尚未保存的修改,还可以额外传入keys数组作为参数,指定需要撤销的属性,例如:

todo.revert(['content'])
有条件更新对象

通过传入query选项,可以按照指定条件去更新对象——当条件满足时,执行更新;条件不满足时,不执行更新并返回305错误。
例如,用户的账户表Account有一个余额字段balance,同时有多个请求要修改该字段。为避免余额出现负值,只有当余额小于或等于余额的时候才能接受请求:

const account = AV.Object.createWithoutData('Account','5745557f71cfe40068c6abe0');
// 对balance原子减少100
const amount = -100;
account.increment('balance',amount);
account.save(null,{
//设置条件
query:new AV.Query('Account').greaterThanOrEqualTo('balance',-amount),
// 操作结束后,返回最新数据。
// 如果是新对象,则所有属性都会被返回,
// 否则只有更新的属性被返回。
featchWhenSave:true
}).then((account) => {
console.log(`当前余额为:$account.get('balance')}`);
}.(error) => {
if(error.code === 305) {
console.log('余额不足,操作失败');
}
});

query选项只对已存在的对象有效,不适用于尚未存入云端的对象。
query选项在有多个客户端需要更新同一属性的时候非常有用。相比于通过AV.query查询AV.object再对其进行更新的方法,这样做更加简洁,并且能够避免出现差错。

更行计数器

设想我们正在开发一个微博,需要统计一条微博有多少个赞和多少次转发。由于赞和转发的操作可能由多个客户端同时进行,直接在本地更新数字并保存到云端的做法极有可能导致差错。为保证技术的准确性,可以通过原子操作来增加或减少一个属性内保存的数字:

post.increment('likes',1);

可以指定需要增加或减少的值。若为指定,则默认使用1。
注意,虽然原子增减支持浮点数,但因为底层数据库的浮点数存储格式限制,会有舍入误差。因此需要原子增减的字段建议使用整数以避免误差,例如3.14可以存储为314,然后再客户端进行相应的转换。否则,以 比较大小为条件为查询对象的时候,需要特殊处理,< a 需改查 < a + e,> a需改查> a-e,== a 需改查> a - e 且 < a + e,其中 e 为误差范围,据所需精度取值,比如0.0001。

更新数组

更新数组也是原子操作。使用以下方法可以方便的维护数组类型的数据:

  • AV.Object.add(‘arrayKey’,value) 将指定对象附加到数组末尾。
  • AV.Object.addUnique(‘arrayKey’,vallue) 如果数组中不包含指定对象,则将改对象加入数组。对象的插入位置时随机的。
  • AV.Object.remove(‘arrayKey’,value) 从数组字段中删除指定对象的所有实例。
    例如,Todo用一个alarms属性保存所有闹钟的时间。下面的代码将多个时间加入这个属性:
const alarm1 = new Date('2021-09-30T07:10:00');
const alarm2 = new Date('2021-0930T07:20:00');
const alarm3 = new Date('2021-09-30T07:21:00');

const alarms = [alarm1,alarm2,alarm3];

const todo = new AV.Object('Todo');
todo.addUnique('alarms',alarms);
todo.save();
删除对象

下面的代码从云端删除一个Todo对象;

const todo = AV.Object.createWithoutData('Todo','582570f38ac247004f39c24b');
todo.destroy();

如果只删除对象的一个属性,可以用unset:

const todo = AV.Object.createWithoutData('Todo','582570f38ac247004f39c24b');
todo.unset('title'); // title属性会被删除
批量操作

可以在一次请求中包含多个构建、保存、删除和同步对象的操作:

// 创建一个保存所有AV,Object的数组
const object1 = new Av.Object('Todo');
object1.set("content","更新文档");
const object2 = new AV.Object("Todo");
object2.set("content","回复论坛帖子");
const objects = [object1,object2];

// 批量构建和更新
AV.Object.saveAll(objects).then(
function (savedObjects) {
// 成功保存所有对象时进入此resolve函数,saveObjects 是包含所有AV.object的数组。
},
function(error) {
// 只要有一个对象保存错误就会进入此 reject函数
	}
);

// 批量删除
AV.Object.destroyAll(objects).then(
function (deleteObjects) {
// 成功删除所有对象时进入此resolve函数,deleteObjects是包含所有的 AV.Object的数组
	},
	function (error) {
	// 只要有一个对象删除错误就会进入此 reject函数
	}
);
// 批量同步
AV.Object.featchAll(objects).then(
function (featchedObjects) {
	// 成功同步所有对象时进入此resolve函数,featchedObjects是包含所有AV.Object 的数组
	},
	function (error) {
	// 只要有一个对象同步错误就会进入此reject函数
	}
);

下面的代码将所有的Todo的isComplete设为true:

const query = new AV.query('Todo');
query.find().then()todos) => {
// 获取需要更新的todo
todos.set('isComplete',true);
});
// 批量更新
AV.Object.saveAl(todos);
});

虽然上述方法可以在一次请求中包含多个操作,每一个分别的保存或同步操作在计费时依然会被算作一次请求,而所有的删除操作则会被合并为一次请求。

数据模型

对象之间可以产生关联。拿一个博客应用来说,一个Post对象可以与Comment对象产生关联。云服务支持三种关系:一对一,一对多,多对多。

一对一、一对多关系

一对一、一对多关系可以通过将AV.Object保存为另一个对象的属性值的方式产生。比如说,让博客应用中的一个Comment 指向一个Post。
下面的代码会创建一个包含单个Comment的Post:

// 创建Post 
const post = new AV.Object('Post');
post.set('title','好饿');
post.set('content','中午吃啥?');

// 创建comment 
const comment = new AV.Object('Comment');
comment.set('content','吃饭');

// 将post 设为comment的一个属性值
comment.set('parent',post);

// 保存comment 会同时保存post
comment.save();

云端存储时,会将被指向的对象用Pointer的形式存起来。你也可以用objectId来指向一个对象:

const post = AV.Object.createwithoutData('Post','57328ca079bc44005c2472d0');
comment.set('post',post);
多对多关系

想要建立多对多关系,最简单的办法就是使用数组。在大多数情况下,使用数组可以有效减少查询的次数,提升程序的运行效率。但如果有额外的属性需要附着于两个class之间的关联,那么使用中间表可能是更好的方式。注意这里说到的额外的属性是用来描述class 之间的关系的,而不是任何单一的class的。建议在任何一个class 的对象数量超过100的时候考虑使用中间表。

序列化和反序列化

在实际的开发中,把AV.Object当作参数传递的时候,会涉及到复杂对象的拷贝问题,因此AV.Object也提供了序列化和反序列化:

序列化
const todo = new AV.Object('Todo');
todo.set('title','国庆假期'); // 设置名称
todo.set('priority',2);// 设置优先级
todo.set('owner',AV.User.current()); // 这里就是一个Pointer 类型,指向当前登录的用户

 // 将AV.Object 对象序列化成JSON对象
 const json = todo.toFullJSON();
 // 将JSON 对象序列化为字符串
 const serializedString = JSON.stringfy(json);

AV.Object 还提供了另一种方法toJSON()。他们的区别是toJSON()得到的对象仅包含对象的payload,一般用于展示,而toFullJSON()得到的对象包含了元数据,一般用于传输。在使用时请注意区分。

反序列化
// 将字符串反序列化为JSON对象
const json = JSON.parse(serializedString);
// 将JSON对象反序列化成AV.Object对象
const todo = AV.parseJSOn(json);

查询

如何从云端一次性获取多个符合特定条件的AV.Object的需求,这时候及需要用到AV.Query了。

基础查询

执行一次基础查询通常包括这些步骤:
1.构建AV.Query();
2.向其添加查询条件;
3.执行查询并获取包含满足条件的对象的数组。
下面的代码获取所有lastName为Smithde student:

const query = new AV.Query('Student');
query.equalTo('lastName',"Smith");
query.find().then((students) => {
// students 是包含满足条件的Student 对象的数组
});
查询条件

可以给AV.Object添加不同的条件来改变获取到的结果。
下面的代码查询所有firstName不为Jack 的对象

query.notEqulTo('firstName','Jack');

对能够排序的属性(比如数字、字符串),可以进行比较查询:

// 限制 age < 18 
query.lessThan('age',18);

// 限制 age <= 18
query.lessThanOrEqualTo('age',18);

// 限制 age > 18 
query.greaterThan('age',18);

// 限制 age >= 18
query.greaterThanOrEqualTo('age',18);

可以在同一个查询中设置多个条件,这样可以获取满足所有条件的结果。可以理解为所有的条件是AND的关系:

query.equalTo('firstName','Jack');
query.greaterThan('age',18);

可以通过指定limit限制返回结果的数量(默认为 100):

// 最多获取10 条结果
query.limit(10);

由于性能原因,limit最大只能设置为1000 .即使将其设为大于1000的数,云端也只会返回1000条结果。
如果只需要一条结果,可以直接用first:

const query = new AV.Query('Todo');
query.equalTo('priority',2);
query.first().then((todo) => {
// todo 是第一个满足条件的Todo对象
});

可以通过设置skip来跳过一定数量的结果:

// 跳过前20 条结果
query.skip(20);

把skip和limit结合起来,就能实现翻页功能:

const query = new Av。Query('Todo');
query.equalTo('priority',2);
query.limit(10);
query.skip(20);

需要注意的是,skip的值越高,查询所需的时间就越长。作为替代方案,可以通过设置createdAt 或 updatedAt的范围来实现更高效的翻页,因为他们都自带索引。同理,也可以通过设置自增字段的范围来实现翻页。
对能够排序的属性,可以指定结果的排序规则:

 // 按createdAt 升序排列
 query.ascending('createdAt');
// 按createdAt 降序排列
query.descending('createdAt');

还可以为同一个查询添加多个排序规则;

query.addAscending('priority');
query.addDdescending('createdAt');

下面的代码可用于查找包含或不包含某一属性的对象:

 // 查找包含’imags'的对象
 query.exisits('images');
// 查找不包含‘images'的对象
query.doesNotExist('images');

可以用matchesKeyInQuery查找某一属性值为另一查询返回结果的对象。比如说,你有一个用于存储国家和语言对应关系的Country class,还有一个用于存储学生国籍的student class :
LeanCloud基本使用_第1张图片
下面的代码可以找到所有来自英语国家的学生:

const studentQuery = new AV.query('Student');
const countryQuery = new AV.Query('Country');
// 获取所有的英语国家
countryQuery.equalTo('language','English');
// 把Student 的nationality 和 Country的name关联起来
studentQuery.matchesKeyInQuery('nationallity','name',countryQuery);
studentQuery.find().then((studens) => {
// studens 包含John Doe 和 Tom Sawyer
});

可以通过select指定需要返回的属性。下面的代码只获取每个对象的title 和 content(包含内置属性):

const query = new AV.Query('Todo');
query.select(['title','content']);
query.first().then((todo) => {
const title = todo.get('title'); // 能获取到
const content = todo.get('content'); // 能获取到
const notes = todo.get('notes'); // undefined
});

select支持点号(author.firstName),另外,字段名前添加减号前缀表示反向选择,例如-auhtor 表示不返回author 字段。反向选择同样适用于内置字段,比如-ObjectId,也可以和点号使用,比如-PubUser.createdAt。对于未获取的属性,可以通过对结果中的对象进行featch 操作来获取。

字符串查询

可以用startWith来查找某一属性值以特定字符串开头的对象。和SQL中的LIKE一样,也可以利用索引带来的优势:

const query = new AV.Query('Todo');
// 相当于SQL中的title LIKE ’llunch%'
query.startsWith('title','lunch');

可以用contains来查找某一属性值包含特定字符串的对象:

const query = new AV.Query('Todo');
// 相当于SQL中的title LIKE ’%lunch%'
query.contains('title','lunch');

和startsWith不同,contains无法利用索引,因此不建议用于大型数据集。注意startsWith 和contains 都是区分大小写的,所以上述查询会忽略lunch、LUNCH等字符串。如果想查找某一属性值不包含特定字符串的对象,可以使用matches进行基于正则表达式的查询:

const query = new AV.Query('Todo');
// 'title' 不包含'ticket'(不区分大小写)
const regExp = new regEXp('^((?!titck).)*$','i');
query.matches('title',regExp);

不过不推荐大量使用这类查询,尤其是对于抱哈超过100,000个对象的class,因为这类查询无法利用索引,实际操作中云端会遍历所有对象来获取结果。如果有进行全文搜索的需求,可以使用全文搜索服务。

关系查询

查询关联数据有很多种方式,常用的一种是查询某一属性值为特定AV.Object的对象,这时可以像其他查询一样直接用equalTo。比如说,如果每一条博客评论Comment都有一个post属性用来存放原文Post,则可以用下面的方法获取所有与某一Post相关联的评论:

const post = AV.Object.createWithoutData('Post','57328ca079bc44005c2472d0');
const query = new AV.Query('Comment');
query.equalTo('post',post);
query.find().then((comments) => {
// comments 包含与post 相关联的评论
});

如需获取某一属性值不是另一查询结果中任一AV.Object的对象,则使用doesNotMatchQuery。
有时候可能需要获取来自另一个class的数据而不想进行额外的查询,此时可以在同一个查询上使用include。下面的代码查找最新发布的10条评论,并包含各自对应的博客文章:

const query = new AV.Query('Comment');
// 获取最新发布的
query.descending('createAt');
// 只能获取10条
query.limit(10);
// 同时包含博客文章
query.include('post');
query.find().then((comments) => {
// comments 包含最新发布的10条评论,包含各自对应的博客文章
comments.forEach((comment) => {
// 该操作无需网络连接
const post = comment.get('post');
});

可以用dot符号(.) 来获取多级关系,例如post.author。可以在同一查询上应用多次include 以包含多个属性。通过这种方法获取到的对象同样接受first、get等AV.Query辅助方法。
通过include 进行多级查询的方式不适用于数组属性内部的AV.Object,只能包含到数组本身。

关系查询的注意事项

云端使用的并非关系型数据库,无法做到真正的联表查询,所以实际的处理方式是:先执行内嵌查询/子查询(和普通查询一样,limit默认为 100,最大为1000) 然后将子查询的结果填入主查询的对应位置,再执行主查询。如果子查询匹配到的记录数量超过limit,且主查询有其他查询条件,那么可能会出现没有结果或结果不全的情况,因为只有limit 数量以内的而结果会被填入主查询。

建议采用以下方案进行改进:
  • 确保子查询的结果在100条以下,如果在100至1000条之间的话请将子查询的limit 设为1000。
  • 将需要查询的字段冗余到主查询所在的表上。
  • 进行多次查询,每次在子查询上设置不同的skip值来遍历所有记录(注意skip 的值较大时可能会引发性能问题,因此不是很推荐)。
统计总数量

如果只需要知道有多少对象匹配查询条件而无需获取对象本身,可使用count 来代替find。比如说,查询有多少个已完成的todo:

const query = new AV.Query('Todo');
query.equalTo('isComplete',true);
query,count().then((count) => {
console.log(`${count} 个todo已完成。`);
组合查询

组合查询就是把诸多查询条件用一定的逻辑合并到一起(OR 或 AND)再交给云端去查询。组合查询不支持在子查询中包含GeoPoint 或其他非过滤性的限制(例如 near、withinGeoBox、limit、skip、ascending、include)。

OR 查询

OR 操作表示多个查询条件符合其中任意一个即可。例如,查询条件优先级大于等于 3 或者已经完成了的todo:

const priorityQuery = new AV.Query('Todo');
priorityQuery.greateThanOrEqualTo('priority',3);
const isCompleteQuery = new AV.Query('Todo');
isCompleteQuery.EqualTo('isComplete',true);
const query = AV.Query.or(priorityQuery,isCompleteQuery);

使用 OR 查询时,子查询不能包含GeoPoint相关的查询。

AND查询

使用 AND 查询的效果等同于往 AV.Query 添加多个条件。下面的代码构建的查询会查找创建时间在2021-10-12 和 2021-11-12之间的todo:

const startDateQuery = new AV.Query('Todo');
startDateQuery.greaterThanOrEqualTo('createdAt',new Date('2021-10-12 00:00:00'));

const endDateQuery = new AV.query('Todo');
endDateQuery.lessThan('createdAt',new Date('2021-11-12 00:00:00'))

const query = AV.Query.and(startDateQuery,endDateQuery);

单独使用 AND 查询跟使用基础查询相比并没有什么不同,不过当查询条件中包含不止一个 OR 查询时,就必须使用 AND 查询:

const createdAtQuery = new AV.Query('Todo');
createdAtQuery.greaterThanOrEqualTo('createdAt',new Date('2021-10-12')); // 大于等于2021-10-12 
createdAtQuery.lessThan('createdAt',new Date('2021-11-12'));  // 小于2021-11-12

const locationQuery = new AV.Query('Todo');
locationQuery.doesNotExist('location'); //不存在

const priority2Query = new AV.Query('Todo');
priority2Query.equalTo('priority',2);

const priority3Query = new AV.Query('Todo');
priority3Query.equalTo('priority',3);
const priorityQuery = AV.Query.or('locationQuery,createdAt);
const timeLocationQuery = AV.Query.or(locationQuery,createdAtQuery);
const query = AV.Query.and(priorityQuery,timeLocationQuery);
查询性能优化

影响查询性能的因素很多。特别是当查询结果的数量超过10万,查询性能可能会显著下降或出现瓶颈。以下列举一些容易降低性能查询问题,可据此进行又针对性的调整和优化,或尽量避免使用。

  • 不等于和不包含查询(无法使用索引)
  • 通配符在前面的字符串查询(无法使用索引)
  • 有条件的count (需扫描所有的数据)
  • skip 跳过较多的行数(相当于需要先查出被跳过的那些行)
  • 无索引的排序(另外除非复合索引同时覆盖了查询和排序,否则只有其中一个能使用索引)
  • 无索引的查询(另外除非复合索引同时覆盖了所有条件,否则未覆盖的条件无法使用索引,如果未覆盖的条件区分度较低将会扫描较多的数据)

学习笔记,如有错误,欢迎指正…

你可能感兴趣的:(javascript,前端)