mongoDB使用投影剔除‘额外’字段的操作过程
简介
实际开发过程中,为便于开发人员定位问题,常存在多个额外的字段。例如:增加createdAt、updatedAt字段以查看数据的创建和更改时间。而对于客户端而言,无需知道其存在。针对以上情况,本文详细介绍了“额外”字段的用途以及处理过程。
技术栈
- mongodb4.0.20
- mongoose5.10.7
1 "额外"字段是什么
1.1"额外"是指与业务无关
mongodb中,collection中存储的字段并不仅仅有业务字段。有些情况下,会存储多余的字段,以便于开发人员定位问题、扩展集合等。
额外的含义是指和业务无关、和开发相关的字段。这些字段不需要被用户所了解,但是在开发过程中是至关重要的。
1.2产生原因
产生额外字段的原因是多种多样的。
- 如使用mongoose插件向db中插入数据时,会默认的生成_id、__v字段
- 如软删除,则是通过控制is_deleted实现..
2额外字段的分类
额外字段的产生原因有很多,可以以此进行分类。
2.1_id、__v字段
产生原因:以mongoose为例,通过schema->model->entity向mongodb中插入数据时,该数据会默认的增加_id、__v字段。
_id字段是由mongodb默认生成的,用于文档的唯一索引。类型是ObjectID。mongoDB文档定义如下:
“
MongoDBcreatesauniqueindexonthe_idfieldduringthecreationofacollection.The_idindexpreventsclientsfrominsertingtwodocumentswiththesamevalueforthe_idfield.Youcannotdropthisindexonthe_idfield.<
__v字段是由mongoose首次创建时默认生成,表示该条doc的内部版本号。
“
TheversionKeyisapropertysetoneachdocumentwhenfirstcreatedbyMongoose.Thiskeysvaluecontainstheinternalrevisionofthedocument.TheversionKeyoptionisastringthatrepresentsthepathtouseforversioning.Thedefaultis__v.
2.2createdAt、updatedAt字段
createdAt、updatedAt字段是通过timestamp选项指定的,类型为Date。
“
ThetimestampsoptiontellsmongoosetoassigncreatedAtandupdatedAtfieldstoyourschema.ThetypeassignedisDate.Bydefault,thenamesofthefieldsarecreatedAtandupdatedAt.Customizethefieldnamesbysettingtimestamps.createdAtandtimestamps.updatedAt.
2.3is_deleted字段
is_deleted字段是实现软删除一种常用的方式。在实际业务中,出于各种原因(如删除后用户要求再次恢复等),往往采用的软删除,而非物理删除。
因此,is_deleted字段保存当前doc的状态。is_deleted字段为true时,表示当前记录有效。is_deleted字段为false时,表示当前记录已被删除。
3额外字段相关操作
3.1额外字段生成
_id字段是必选项;__v、createdAt、updatedAt字段是可配置的;status字段直接加在s对应的chema中。相关的schema代码如下:
isdeleted:{
type:String,
default:true,
enum:[true,false],
},
id:{
type:String,
index:true,
unqiue:true,
default:uuid.v4(),
}},
{timestamps:{createdAt:'docCreatedAt',updatedAt:"docUpdatedAt"},versionKey:false});
通过配置schema中的timestamps选项,可以将createdAt和updatedAt字段加入到doc中。在创建和更新doc时,这两个字段无需传入,就会自动变化。
3.2额外字段清理
通过3.1可以明确的产生若干额外字段,但是客户端调用接口并返回时,这些字段是无需得知的。因此需对额外字段进行清理。清理方式分为投影和过滤。
以query、update接口为例。其中query接口用于:1、查询指定字段2、查询全部字段3、分页排序查询。update接口用于更新并返回更新后的数据。
根据是否需要指定字段、和collection中有无内嵌的情况划分,一共有4类。接着针对这4种情况进行分析。
1、有指定字段、无内嵌
2、无指定字段、无内嵌
3、有指定字段、有内嵌
4、无指定字段、有内嵌
3.2.1投影
有指定字段是指在查询时指定查询字段,而无需全部返回。mongo中实现指定的方式是投影(project)。mongo官方文档中定义如下:
“
The$projecttakesadocumentthatcanspecifytheinclusionoffields,thesuppressionofthe_idfield,theadditionofnewfields,andtheresettingofthevaluesofexistingfields.Alternatively,youmayspecifytheexclusionoffields.
$project可以做3件事:
1.指定包含的字段、禁止_id字段、添加新字段
2.重置已存在字段的值
3.指定排除的字段
我们只需关注事情1、事情3。接着查看mongoose中对project的说明:
“
Whenusingstringsyntax,prefixingapathwith-willflagthatpathasexcluded.Whenapathdoesnothavethe-prefix,itisincluded.Lastly,ifapathisprefixedwith+,itforcesinclusionofthepath,whichisusefulforpathsexcludedattheschemalevel.Aprojectionmustbeeitherinclusiveorexclusive.Inotherwords,youmusteitherlistthefieldstoinclude(whichexcludesallothers),orlistthefieldstoexclude(whichimpliesallotherfieldsareincluded).The_idfieldistheonlyexceptionbecauseMongoDBincludesitbydefault.
注意:此处指query"projection"
mongoose表明:投影要么是全包含,要么是全剔除。不允许包含和剔除同时存在。但由于
_id是MongoDB默认包含的,因此_id是个例外。
selectproject投影语句组装代码:
/**
*添加通用筛选条件
*@param{*}stat已装配的筛选语句
*@param{*}collectioncollectionName
*@return{*}组装完成的语句
*/
functionaddCommonSelectCond(stat,collection)
{
if(typeof(stat)!="object")return;
stat["_id"]=0;
stat["__v"]=0;
stat["status"]=0;
stat["docCreatedAt"]=0;
stat["docUpdatedAt"]=0;
varembeddedRes=hasEmbedded(collection);
if(embeddedRes["isEmbedded"])
{
for(varitemofembeddedRes["embeddedSchema"])
{
stat[item+"._id"]=0;
stat[item+".__v"]=0;
stat[item+".status"]=0;
stat[item+".docCreatedAt"]=0;
stat[item+".docUpdatedAt"]=0;
}
}
returnstat;
}
3.2.2过滤
通过findOneAndupdate、insert、query等返回的doc对象中(已经过lean或者toObject处理),是数据库中真实状态。因此需要对产生的doc进行过滤,包括doc过滤和内嵌文档过滤。
/**
*处理自身及内嵌的表
*@param{*}collection查询的表
*@param{*}doc已查询出的结果
*@returnsdoc清理后的结果
*/
staticclearDoc(collection,doc){
if(doc===undefined||doc===null||typeofdoc!="object")returnnull;
doc=this.clearExtraField(doc);
varres=hasEmbedded(collection);
if(res["isEmbedded"])
{
letarr=res["embeddedSchema"];
for(varitemofarr){
if(doc[item])
doc[item]=this.clearArray(doc[item]);
}
}
returndoc;
}
staticclearExtraField(doc){
if(doc===null||typeofdoc!="object")
return;
vardel=deletedoc["docCreatedAt"]&&
deletedoc["docUpdatedAt"]&&
deletedoc["_id"]&&
deletedoc["__v"]&&
deletedoc["status"];
if(!del)returnnewError("删除额外字段出错");
returndoc;
}
staticclearArray(arr)
{
if(!Array.isArray(arr))return;
varclearRes=newArray();
for(varitemofarr){
clearRes.push(this.clearExtraField(item));
}
returnclearRes;
}
细心的读者已经发现了,投影和过滤的字段内容都是额外字段。那什么情况下使用投影,什么情况下使用过滤呢?
关于这个问题,笔者的建议是如果不能确保额外字段被剔除掉,那就采取双重认证:查询前使用投影,查询后使用过滤。
4总结
本文介绍了实际业务中往往会产生额外字段。而在mongoDB中,"消除"额外字段的手段主要是投影、过滤。
以使用频率最高的查询接口为例,整理如下:
| 指定选项 | 内嵌选项 | 查询前投影 | 查询后过滤 |
|---|---|---|---|
| 有指定 | 无内嵌 | × | √ |
| 有指定 | 有内嵌 | × | √ |
| 无指定 | 无内嵌 | √ | × |
| 无指定 | 有内嵌 | √ | × |
因此,笔者建议无论schema中是否配置了options,在查询时组装投影语句,查询后进行结果过滤。这样保证万无一失,
额外字段才不会漏到客户端**。
到此这篇关于mongoDB使用投影剔除‘额外'字段的文章就介绍到这了,更多相关mongoDB用投影剔除额外字段内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!