1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
|
Populate - DBRef-like behavior (experimental)
=============================================
`ObjectIds` can now refer to another document in a
collection within our database and be populated when
querying. An example is helpful:
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var PersonSchema = new Schema({
name : String
, age : Number
, stories : [{ type: Schema.ObjectId, ref: 'Story' }]
});
var StorySchema = new Schema({
_creator : { type: Schema.ObjectId, ref: 'Person' }
, title : String
, fans : [{ type: Schema.ObjectId, ref: 'Person' }]
});
var Story = mongoose.model('Story', StorySchema);
var Person = mongoose.model('Person', PersonSchema);
So far we've created two models. Our `Person` model has it's `stories` field
set to an array of `ObjectId`s. The `ref` option is what tells Mongoose in which
model to look, in our case the `Story` model. All `_id`s we
store here must be document `_id`s from the `Story` model. We also added
a `_creator` `ObjectId` to our `Story` schema which refers to a single `Person`.
## Saving a ref (to the parent)
Below you can see how we "save" a ref in 'story1' back to the _creator. This
is usually all you need to do.
var aaron = new Person({ name: 'Aaron', age: 100 });
aaron.save(function (err) {
if (err) ...
var story1 = new Story({
title: "A man who cooked Nintendo"
, _creator: aaron._id
});
story1.save(function (err) {
if (err) ...
});
})
## Populating the refs (to the parent)
So far we haven't done anything special. We've merely created a `Person` and
a `Story`. Now let's take a look at populating our story's `_creator`:
Story
.findOne({ title: /Nintendo/i })
.populate('_creator') // <--
.run(function (err, story) {
if (err) ..
console.log('The creator is %s', story._creator.name);
// prints "The creator is Aaron"
})
Yup that's it. We've just queried for a `Story` with the term Nintendo in it's
title and also queried the `Person` collection for the story's creator. Nice!
Arrays of `ObjectId` refs work the same way. Just call the `populate` method on the query and
an array of documents will be returned in place of the `ObjectId`s.
What if we only want a few specific fields returned for the query? This can
be accomplished by passing an array of field names to the `populate` method:
Story
.findOne({ title: /Nintendo/i })
.populate('_creator', ['name']) // <-- only return the Persons name
.run(function (err, story) {
if (err) ..
console.log('The creator is %s', story._creator.name);
// prints "The creator is Aaron"
console.log('The creators age is %s', story._creator.age)
// prints "The creators age is null'
})
Now this is much better. The only property of the creator we are using
is the `name` so we only returned that field from the db. Efficiency FTW!
## References to the children
You may find however, if you use the `aaron` object, you are unable to get
a list of the `stories`. This is because no `story` objects were ever 'pushed'
on to `aaron.stories`.
There are two perspectives to this story. First, it's nice to have aaron know
which are his stories.
aaron.stories.push(story1);
aaron.save();
This allows you do a find & populate like:
Person
.findOne({ name: 'Aaron' })
.populate('stories') // <-- only works if you pushed refs to children
.run(function (err, person) {
if (err) ..
console.log('JSON for person is: ', person);
})
However, it is debatable that you really want two sets of pointers as they
may get out of sync. So you could instead merely find() the documents you
are interested in.
Story
.find({ _creator: aaron._id })
.populate('_creator') // <-- not really necessary
.run(function (err, stories) {
if (err) ..
console.log('The stories JSON is an array: ', stoires);
})
## Updating
Now that we have a story we realized that the `_creator` was incorrect. We can
update `ObjectId` refs the same as any other property through the magic of Mongooses
internal casting:
var guille = new Person({ name: 'Guillermo' });
guille.save(function (err) {
if (err) ..
story._creator = guille; // or guille._id
story.save(function (err) {
if (err) ..
Story
.findOne({ title: /Nintendo/i })
.populate('_creator', ['name'])
.run(function (err, story) {
if (err) ..
console.log('The creator is %s', story._creator.name)
// prints "The creator is Guillermo"
})
})
})
### Note:
The documents returned from calling `populate` become fully functional,
`remove`able, `save`able documents. Do not confuse them with embedded
docs. Take caution when calling its `remove` method because
you'll be removing it from the database, not just the array.
|