Mongoose中document与object的区别示例详解
前言
本文主要给大家总结介绍了关于Mongoose中document与object区别的相关内容,分享出来供大家参考学习,其实这个问题其实是mongoose非常常见的问题,经常有很多以前没遇到这个问题的人都会被这个问题弄得怀疑人生。
我们先介绍一些问题的背景。
先看下面一段代码:
router.get('/',function(req,res,next){
//res.render('index',{title:'Express'});
constmodel=mongoose.model('realestate');
constqueryCretia={};
model.find(queryCretia,(err,docs)=>{
res.render('index',{
title:'express',
docs:docs
})
})
});
<%=title%> <%=title%>
Welcometo<%=title%>
-
<%docs.forEach(function(doc){%>
- <%=doc.type%> <%})%>
在第一段代码中,通过model.find我们应该能够获取到根据queryCriteria获取的结果,结果应该是一个对象数组,类似于这样:
[{
"_id":ObjectId("59bdeadb2a5c612514ee7970"),
"title":"好楼层,中等装修,满5年,上门实拍",
"type":"2室1厅",
"square":"75.42平",
"direction":"朝南",
"floor":"中区/6层",
"unitPrice":47732,
"totalPrice":360,
"location":null,
"specialExplain":"满五",
"url":"http://sh.lianjia.com//ershoufang/sh4528035.html",
"station":"江杨北路",
"line":"3号线",
"updateTime":"2017-09-1711:24:11"
}
{
"_id":ObjectId("59bdeadb2a5c612514ee7971"),
"title":"南北户型,厨卫全明,高区采光好,装修精美",
"type":"2室2厅",
"square":"90.92平",
"direction":"朝南北",
"floor":"高区/6层",
"unitPrice":46194,
"totalPrice":420,
"location":null,
"specialExplain":"满五",
"url":"http://sh.lianjia.com//ershoufang/sh4546221.html",
"station":"江杨北路",
"line":"3号线",
"updateTime":"2017-09-1711:24:11"
}]
预期index.ejs应该渲染的页面是一个ul渲染的结果,类似于
- 2室1厅
- 2室2厅
当然,理想很丰满,现实很骨感。我就是死活渲染不出来doc.type。照理说应该是不可能的,在index.ejs中doc就是一个对象,我为什么不能获取doc的type属性呢?这不合理,太不合理了!
老实说,这个问题我之间也是遇到过,当初我是想修改这个doc的属性,但是死活没有办法修改,当初也是花了很久找到原因。这次我就把这个问题好好地研究一下。
先说结果,以及解决方法把。我比较喜欢剧透。愿意是因为再次返回的doc是属于Document的实例,而不是一个普通的对象。也就是说它和普通的对象是不一样的,它没有普通对象的一些方法,普通对象也没有它身上的一些方法。
解决方案有几种,不过究其根本都是将这种document转化为普通的对象:
方法1:
利用toObject方法
docs.forEach(doc=>{
returndoc.toObject();
})
方法2:
利用JSON方法,这是我想到的一个方法,具体深层原因在这就不展开了:
docs=JSON.stringify(docs); docs=JSON.parse(docs);
方法3:
利用lean方法:
model.find().lean().exec((err,docs)=>{
....
})
上述的三种方法应该都能成功将find获取的结果转化为普通的对象。
但是我还想知道到底document和一个普通的对象到底有什么区别,区别在哪里呢?
我们假设find获取的结果是docs,转化为普通对象的结果是docs1。现在我们就看一看这二者的区别。理论上docs和docs1都应该是数组,而它们中元素都应该是一个对象,我们先来看看是不是这样呢?
console.log(Object.prototype.toString.call(docs)); consoele.log(Object.prototype.toString.call(docs[0])); console.log(Object.prototype.toString.call(docs1)); console.log(Object.prototype.toString.call(docs1[0]))
我们通过上述方法可以获取docs以及docs1的类型以及其中元素的类型,结果是:
[objectArray] [objectObject] [objectArray] [objectObject]
和我们预想中的一模一样,那问题不在这,那我们就探究探究docs[0]以及docs1[0]的原型把,看看它的原型到底是什么呢?知道JS的人,应该都知道JS中的原型链。在此,我们就通过__proto__来粗暴地获取对象的原型:
console.dir(doc[0].__proto__); console.dir(docs[0].__proto__);
结果是:
model{
db:
NativeConnection{
base:
Mongoose{
connections:[Array],
models:[Object],
modelSchemas:[Object],
options:[Object],
plugins:[Array]},
collections:{realestates:[Object]},
models:{realestate:[Object]},
config:{autoIndex:true},
replica:false,
hosts:null,
host:'127.0.0.1',
port:27017,
user:undefined,
pass:undefined,
name:'real_estate_info',
options:
{db:[Object],
auth:{},
server:[Object],
replset:[Object],
mongos:undefined},
otherDbs:[],
_readyState:1,
_closeCalled:false,
_hasOpened:true,
_listening:false,
db:
Db{
domain:null,
_events:[Object],
_eventsCount:6,
_maxListeners:undefined,
s:[Object],
serverConfig:[Getter],
bufferMaxEntries:[Getter],
databaseName:[Getter],
_listening:true},
_events:
{connected:[Function],
error:[Function:boundboundconsoleCall],
disconnected:[Function:boundboundconsoleCall],
reconnected:[Function:boundboundconsoleCall]},
_eventsCount:4},
discriminators:undefined,
id:[Getter/Setter],
__v:[Getter/Setter],
_id:[Getter/Setter],
schema:
Schema{
obj:undefined,
paths:{_id:[Object],__v:[Object]},
aliases:{},
subpaths:{},
virtuals:{id:[Object]},
singleNestedPaths:{},
nested:{},
inherits:{},
callQueue:[[Array],[Array],[Array],[Array],[Array],[Array]],
_indexes:[],
methods:{},
statics:{},
tree:{_id:[Object],__v:[Function:Number],id:[Object]},
query:{},
childSchemas:[],
plugins:[[Object],[Object],[Object],[Object]],
s:{hooks:[Object],kareemHooks:[Object]},
options:
{retainKeyOrder:false,
typeKey:'type',
id:true,
noVirtualId:false,
_id:true,
noId:false,
validateBeforeSave:true,
read:null,
shardKey:null,
autoIndex:null,
minimize:true,
discriminatorKey:'__t',
versionKey:'__v',
capped:false,
bufferCommands:true,
strict:true,
pluralization:true},
'$globalPluginsApplied':true,
_requiredpaths:[]},
collection:
NativeCollection{
collection:Collection{s:[Object]},
opts:{bufferCommands:true,capped:false},
name:'realestates',
collectionName:'realestates',
conn:
NativeConnection{
base:[Object],
collections:[Object],
models:[Object],
config:[Object],
replica:false,
hosts:null,
host:'127.0.0.1',
port:27017,
user:undefined,
pass:undefined,
name:'real_estate_info',
options:[Object],
otherDbs:[],
_readyState:1,
_closeCalled:false,
_hasOpened:true,
_listening:false,
db:[Object],
_events:[Object],
_eventsCount:4},
queue:[],
buffer:false,
emitter:
EventEmitter{
domain:null,
_events:{},
_eventsCount:0,
_maxListeners:undefined}},
'$__original_save':{[Function]numAsyncPres:0},
save:{[Function:wrappedPointCut]'$originalFunction':'$__original_save','$isWrapped':true},
_pres:
{'$__original_save':[[Object],[Object],[Object]],
'$__original_remove':[[Object]]},
_posts:{'$__original_save':[],'$__original_remove':[]},
'$__original_remove':{[Function]numAsyncPres:1},
remove:
{[Function:wrappedPointCut]
'$originalFunction':'$__original_remove',
'$isWrapped':true},
'$__original_validate':[Function],
validate:
{[Function:wrappedPointCut]
'$originalFunction':'$__original_validate',
'$isWrapped':true}}
以及
{}
很显然,问题就是在这里,docs[0]和docs[0]的原型并不是一个东西。而js中对象通过.或者是[]访问属性都是调用了Object中的某个方法,但具体什么方法我不太记得。然而docs中的原型或者其原型的原型也是没有这个方法的,因此他就没办法去访问这个属性。
其实docs[0].__proto__.__proto__是Model,docs[0].__proto__.__proto__.__proto__是Document,docs[0].__proto__.__proto__.__proto__.__proto__才是{}。
至此,这个问题引起的一系列的探究也是告一段落了。其实Mongoose还有另外一些奇怪的地方,被人所诟病,在此也不一一细数了。从问题的发现,到写这篇文章大概花了大半天的时间,以前遇到问题就找到解决办法就停止了,但是这一次通过这样深入地去发现,可能就会发掘到更多的东西。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。