mongoose
- 我们在model文件配置projectInfo的schema,我们都知道,每一个schema实例都映射一个mongodb的表,可以说schema是mongoose操纵mongodb的灵魂所在。
var mongoose = require('mongoose')
mongoose.Promise = require('bluebird')
var schema = mongoose.Schema
var ProjectSchema = new schema({
id: schema.ObjectId,
projectName: {type: String, unique: true},
)
mongoose.model('ProjectInfo', ProjectSchema)
如果这个时候有一个需求,要求读取rojectInfo表中的一个accountPermissions字段,该字段是一个数组。由于不一定所有文档都需要这个字段,所以并没有在model文件中做相关配置。
我们利用find()方法进行查询,代码如下:
await ProjectInfos.find(null, { projectName: 1, accountPermissions: 1 })
.exec((err, docs) => {
projectList = docs
console.log(docs[0].accountPermissions)
})
按照我们的预期,应该是在控制台得到第一个符合文档的accountPermissions数组。现实却十分骨感,居然得到undefined。这个问题让很多比较熟悉js的开发者痛苦不堪,甚至怀疑人生。得到的doc不是一个对象吗?对象中的属性应该可以用obj.property获得才对。
可是我们却得到了undefined,太诡异了吧!
带着这个问题,我们把查询结果第0项,即docs[0]打印出来,控制台却出现了如下一大串:
model {
'$__': InternalCache {
strictMode: true,
selected: { projectName: 1, accountPermissions: 1 },
shardval: undefined,
saveError: undefined,
validationError: undefined,
adhocPaths: undefined,
removing: undefined,
inserting: undefined,
version: undefined,
getters: {},
_id: 5c13064a3ce9e71c5bdedf29,
populate: undefined,
populated: undefined,
wasPopulated: false,
scope: undefined,
activePaths: StateMachine {
paths: [Object],
states: [Object],
stateNames: [Array]
},
pathsToScopes: {},
ownerDocument: undefined,
fullPath: undefined,
emitter: EventEmitter {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: 0
},
'$options': true
},
isNew: false,
errors: undefined,
_doc: {
accountPermissions: [
'5e09a25cd77ad90b2f63cee7',
'5c0f150f9325280349519b20',
'5df2f8b330750c2b8f2b8725'
],
projectName: 'aaa',
_id: 5c13064a3ce9e71c5bdedf29
},
'$init': true
}
没能如愿得到类似如下的结果:
doc = {
_id: 5c13064a3ce9e71c5bdedf29,
projectName: 'aaa',
accountPermissions: [
'5e09a25cd77ad90b2f63cee7',
'5c0f150f9325280349519b20',
'5df2f8b330750c2b8f2b8725'
]
}
看来事情不那么简单,利用js的原型知识,我们对比一下docs[0]和doc的隐式原型。结果docs[0]的隐式原型并不是{},也就是说得到的docs根本就不是一个正常的js对象。
console.log("***************************************************");
console.log(docs[0])
console.log("***************************************************");
console.log(docs[0].__proto__)
console.log("***************************************************");
console.log(docs[0].__proto__.__proto__)
console.log("***************************************************");
console.log(docs[0].__proto__.__proto__.__proto__)
console.log("***************************************************");
console.log(docs[0].__proto__.__proto__.__proto__.__proto__)
如上代码,我们以此打印,结果如下:
***************************************************
model {
'$__': InternalCache {
strictMode: true,
selected: { projectName: 1, accountPermissions: 1 },
shardval: undefined,
saveError: undefined,
validationError: undefined,
adhocPaths: undefined,
removing: undefined,
inserting: undefined,
version: undefined,
getters: {},
_id: 5c13064a3ce9e71c5bdedf29,
populate: undefined,
populated: undefined,
wasPopulated: false,
scope: undefined,
activePaths: StateMachine {
paths: [Object],
states: [Object],
stateNames: [Array]
},
pathsToScopes: {},
ownerDocument: undefined,
fullPath: undefined,
emitter: EventEmitter {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: 0
},
'$options': true
},
isNew: false,
errors: undefined,
_doc: {
accountPermissions: [
'5e09a25cd77ad90b2f63cee7',
'5c0f150f9325280349519b20',
'5df2f8b330750c2b8f2b8725'
],
projectName: 'aaa',
_id: 5c13064a3ce9e71c5bdedf29
},
'$init': true
}
***************************************************
model {
db: NativeConnection {
base: Mongoose {
connections: [Array],
models: [Object],
modelSchemas: [Object],
options: [Object],
plugins: [Array]
},
collections: {
users: [NativeCollection],
projectinfos: [NativeCollection],
histories: [NativeCollection],
ioscerts: [NativeCollection],
androidcerts: [NativeCollection],
androidgitinfos: [NativeCollection],
iosgitinfos: [NativeCollection],
rolepermissions: [NativeCollection],
gitlabaccounts: [NativeCollection],
logosources: [NativeCollection],
npmrepositories: [NativeCollection],
notificationtypes: [NativeCollection]
},
models: {
User: [Function],
ProjectInfo: [Function],
History: [Function],
IOSCert: [Function],
ANDROIDCert: [Function],
ANDROIDGitInfo: [Function],
IOSGitInfo: [Function],
RolePermission: [Function],
GitLabAccount: [Function],
LogoSource: [Function],
NPMRepository: [Function],
NotificationType: [Function]
},
config: { autoIndex: true },
replica: false,
hosts: null,
host: 'localhost',
port: 27017,
user: null,
pass: null,
name: 'db',
options: null,
otherDbs: [],
states: [Object: null prototype] {
'0': 'disconnected',
'1': 'connected',
'2': 'connecting',
'3': 'disconnecting',
'4': 'unauthorized',
'99': 'uninitialized',
disconnected: 0,
connected: 1,
connecting: 2,
disconnecting: 3,
unauthorized: 4,
uninitialized: 99
},
_readyState: 1,
_closeCalled: false,
_hasOpened: true,
_listening: false,
_connectionOptions: {},
db: Db {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
s: [Object],
serverConfig: [Getter],
bufferMaxEntries: [Getter],
databaseName: [Getter]
}
},
discriminators: undefined,
__v: [Getter/Setter],
_id: [Getter/Setter],
history: [Getter/Setter],
RepositoryLink: [Getter/Setter],
APPVersion: [Getter/Setter],
bundleId: [Getter/Setter],
android: [Getter/Setter],
ios: [Getter/Setter],
projectName: [Getter/Setter],
id: [Getter/Setter],
schema: Schema {
obj: {
id: [Function],
projectName: [Object],
ios: [Object],
android: [Object],
bundleId: [Function: String],
APPVersion: [Function: String],
RepositoryLink: [Object],
history: [Array]
},
paths: {
id: [ObjectId],
projectName: [SchemaString],
'ios.git': [ObjectId],
'ios.env': [SchemaArray],
'ios.cert': [SchemaArray],
'ios.workSpaceName': [SchemaString],
'ios.npmModules': [SchemaString],
'ios.releaseMethod': [SchemaString],
'ios.debugShaCode': [SchemaString],
'ios.releaseShaCode': [SchemaString],
'ios.schema': [DocumentArray],
'ios.projectType': [SchemaString],
'ios.p12File': [SchemaString],
'ios.p12Password': [SchemaString],
'ios.p12': [DocumentArray],
'android.git': [ObjectId],
'android.env': [SchemaArray],
'android.cert': [SchemaArray],
'android.schema': [DocumentArray],
'android.npmModules': [SchemaString],
'android.projectType': [SchemaString],
'android.isJPushProject': [SchemaBoolean],
bundleId: [SchemaString],
APPVersion: [SchemaString],
RepositoryLink: [ObjectId],
history: [SchemaArray],
_id: [ObjectId],
__v: [SchemaNumber]
},
aliases: {},
subpaths: {},
virtuals: {},
singleNestedPaths: {},
nested: { ios: true, android: true },
inherits: {},
callQueue: [ [Array], [Array], [Array], [Array], [Array], [Array] ],
_indexes: [],
methods: {},
statics: {},
tree: {
id: [Function],
projectName: [Object],
ios: [Object],
android: [Object],
bundleId: [Function: String],
APPVersion: [Function: String],
RepositoryLink: [Object],
history: [Array],
_id: [Object],
__v: [Function: Number]
},
query: {},
childSchemas: [ [Object], [Object], [Object] ],
plugins: [ [Object], [Object], [Object], [Object] ],
s: { hooks: [Kareem], kareemHooks: [Object] },
_userProvidedOptions: { usePushEach: true },
options: {
usePushEach: true,
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,
'$wasForceClosed': undefined
},
name: 'projectinfos',
collectionName: 'projectinfos',
conn: NativeConnection {
base: [Mongoose],
collections: [Object],
models: [Object],
config: [Object],
replica: false,
hosts: null,
host: 'localhost',
port: 27017,
user: null,
pass: null,
name: 'db',
options: null,
otherDbs: [],
states: [Object: null prototype],
_readyState: 1,
_closeCalled: false,
_hasOpened: true,
_listening: false,
_connectionOptions: {},
db: [Db]
},
queue: [],
buffer: false,
emitter: EventEmitter {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined
}
},
'$__original_save': [Function] { numAsyncPres: 0 },
save: [Function: wrappedPointCut] {
'$originalFunction': '$__original_save',
'$isWrapped': true
},
_pres: {
'$__original_save': [ [Function], [Function], [Function] ],
'$__original_remove': [ [Function] ]
},
_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
}
}
***************************************************
Model {
'$isMongooseModelPrototype': true,
'$__handleSave': [Function],
'$__save': [Function],
save: [Function],
'$__delta': [Function],
'$__version': [Function],
increment: [Function: increment],
'$__where': [Function: _where],
remove: [Function: remove],
model: [Function: model]
}
***************************************************
Document {
on: [Function],
once: [Function],
emit: [Function],
listeners: [Function],
removeListener: [Function],
setMaxListeners: [Function],
removeAllListeners: [Function],
addListener: [Function],
'$__buildDoc': [Function],
toBSON: [Function],
init: [Function],
'$hook': [Function: $hook],
'$pre': [Function: mongoosePreWrapper],
'$post': [Function: post],
removePre: [Function: removePre],
removePost: [Function: removePost],
_lazySetupHooks: [Function: _lazySetupHooks],
update: [Function: update],
'$set': [Function: $set],
set: [Function: $set],
'$__shouldModify': [Function],
'$__set': [Function],
getValue: [Function],
setValue: [Function],
get: [Function],
'$__path': [Function],
markModified: [Function],
unmarkModified: [Function],
'$ignore': [Function],
modifiedPaths: [Function],
isModified: [Function],
'$isDefault': [Function],
'$isDeleted': [Function],
isDirectModified: [Function],
isInit: [Function],
isSelected: [Function: isSelected],
isDirectSelected: [Function: isDirectSelected],
validate: [Function],
'$__validate': [Function],
validateSync: [Function],
invalidate: [Function],
'$markValid': [Function],
'$isValid': [Function],
'$__reset': [Function: reset],
'$__dirty': [Function],
'$__setSchema': [Function],
'$__getArrayPathsToValidate': [Function],
'$__getAllSubdocs': [Function],
'$__handleReject': [Function: handleReject],
'$toObject': [Function],
toObject: [Function],
toJSON: [Function],
inspect: [Function],
toString: [Function],
equals: [Function],
populate: [Function: populate],
execPopulate: [Function],
populated: [Function],
depopulate: [Function],
'$__fullPath': [Function]
}
***************************************************
{}
那么我们可以利用mongoose提供的lean()方法,省略去生成完整文档实例,直接返回一个js对象,代码如下:
await ProjectInfos.find(null, { projectName: 1, accountPermissions: 1 })
.lean()
.exec((err, docs) => {
projectList = docs
console.log("***************************************************");
console.log(docs[0])
console.log("***************************************************");
console.log(docs[0].__proto__)
})
我们再次打印,如愿得到我们要的结果,同时我们打印出docs[0]的原型,是一个空对象,那么可以证明我们的到docs就是一个js数组,我们可以对每一项的对象进行读写。
***************************************************
{
_id: 5c13064a3ce9e71c5bdedf29,
projectName: 'aaa',
accountPermissions: [
'5e09a25cd77ad90b2f63cee7',
'5c0f150f9325280349519b20',
'5df2f8b330750c2b8f2b8725'
]
}
***************************************************
{}
在model文件进行配置
var mongoose = require('mongoose')
mongoose.Promise = require('bluebird')
var schema = mongoose.Schema
var ProjectSchema = new schema({
id: schema.ObjectId,
projectName: {type: String, unique: true},
accountPermissions: Array
)
mongoose.model('ProjectInfo', ProjectSchema)
如果配置完整,我们可以直接得到accountPermissions字段的相关信息,并不需要使用toObject()或者lean()方法。
***************************************************
model {
'$__': InternalCache {
strictMode: true,
selected: { projectName: 1, accountPermissions: 1 },
shardval: undefined,
saveError: undefined,
validationError: undefined,
adhocPaths: undefined,
removing: undefined,
inserting: undefined,
version: undefined,
getters: {},
_id: 5c13064a3ce9e71c5bdedf29,
populate: undefined,
populated: undefined,
wasPopulated: false,
scope: undefined,
activePaths: StateMachine {
paths: [Object],
states: [Object],
stateNames: [Array]
},
pathsToScopes: {},
ownerDocument: undefined,
fullPath: undefined,
emitter: EventEmitter {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: 0
},
'$options': true
},
isNew: false,
errors: undefined,
_doc: {
accountPermissions: [
'5e09a25cd77ad90b2f63cee7',
'5c0f150f9325280349519b20',
'5df2f8b330750c2b8f2b8725',
toBSON: [Function: toBSON],
_atomics: {},
_parent: [Circular],
_cast: [Function: _cast],
_markModified: [Function: _markModified],
_registerAtomic: [Function: _registerAtomic],
'$__getAtomics': [Function: $__getAtomics],
hasAtomics: [Function: hasAtomics],
_mapCast: [Function: _mapCast],
push: [Function: push],
nonAtomicPush: [Function: nonAtomicPush],
'$pop': [Function: $pop],
pop: [Function: pop],
'$shift': [Function: $shift],
shift: [Function: shift],
pull: [Function: pull],
splice: [Function: splice],
unshift: [Function: unshift],
sort: [Function: sort],
addToSet: [Function: addToSet],
set: [Function: set],
toObject: [Function: toObject],
inspect: [Function: inspect],
indexOf: [Function: indexOf],
remove: [Function: pull],
_path: 'accountPermissions',
isMongooseArray: true,
validators: [],
_schema: [SchemaArray]
],
projectName: 'aaa',
_id: 5c13064a3ce9e71c5bdedf29
},
'$init': true
}
***************************************************
[
'5e09a25cd77ad90b2f63cee7',
'5c0f150f9325280349519b20',
'5df2f8b330750c2b8f2b8725',
toBSON: [Function: toBSON],
_atomics: {},
_parent: model {
'$__': InternalCache {
strictMode: true,
selected: [Object],
shardval: undefined,
saveError: undefined,
validationError: undefined,
adhocPaths: undefined,
removing: undefined,
inserting: undefined,
version: undefined,
getters: {},
_id: 5c13064a3ce9e71c5bdedf29,
populate: undefined,
populated: undefined,
wasPopulated: false,
scope: undefined,
activePaths: [StateMachine],
pathsToScopes: {},
ownerDocument: undefined,
fullPath: undefined,
emitter: [EventEmitter],
'$options': true
},
isNew: false,
errors: undefined,
_doc: {
accountPermissions: [Circular],
projectName: 'aaa',
_id: 5c13064a3ce9e71c5bdedf29
},
'$init': true
},
_cast: [Function: _cast],
_markModified: [Function: _markModified],
_registerAtomic: [Function: _registerAtomic],
'$__getAtomics': [Function: $__getAtomics],
hasAtomics: [Function: hasAtomics],
_mapCast: [Function: _mapCast],
push: [Function: push],
nonAtomicPush: [Function: nonAtomicPush],
'$pop': [Function: $pop],
pop: [Function: pop],
'$shift': [Function: $shift],
shift: [Function: shift],
pull: [Function: pull],
splice: [Function: splice],
unshift: [Function: unshift],
sort: [Function: sort],
addToSet: [Function: addToSet],
set: [Function: set],
toObject: [Function: toObject],
inspect: [Function: inspect],
indexOf: [Function: indexOf],
remove: [Function: pull],
_path: 'accountPermissions',
isMongooseArray: true,
validators: [],
_schema: SchemaArray {
casterConstructor: [Function: Mixed] { schemaName: 'Mixed' },
caster: Mixed {
path: 'accountPermissions',
instance: 'Mixed',
validators: [],
setters: [],
getters: [],
options: {},
_index: null
},
'$isMongooseArray': true,
path: 'accountPermissions',
instance: 'Array',
validators: [],
setters: [],
getters: [],
options: { type: [Function: Array] },
_index: null,
defaultValue: [Function]
}
]
在公司实习的第二个任务,成功踩坑,总结一下,还是要多读文档,不要因为之前的思维习惯而影响新知识的学习。