forked from Automattic/mongoose
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsubdocs.html
More file actions
227 lines (202 loc) · 20.6 KB
/
subdocs.html
File metadata and controls
227 lines (202 loc) · 20.6 KB
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><title>Mongoose v5.6.0: SubDocuments</title><link rel="apple-touch-icon" sizes="57x57" href="images/favicon/apple-icon-57x57.png"><link rel="apple-touch-icon" sizes="60x60" href="images/favicon/apple-icon-60x60.png"><link rel="apple-touch-icon" sizes="72x72" href="images/favicon/apple-icon-72x72.png"><link rel="apple-touch-icon" sizes="76x76" href="images/favicon/apple-icon-76x76.png"><link rel="apple-touch-icon" sizes="114x114" href="images/favicon/apple-icon-114x114.png"><link rel="apple-touch-icon" sizes="120x120" href="images/favicon/apple-icon-120x120.png"><link rel="apple-touch-icon" sizes="144x144" href="images/favicon/apple-icon-144x144.png"><link rel="apple-touch-icon" sizes="152x152" href="images/favicon/apple-icon-152x152.png"><link rel="apple-touch-icon" sizes="180x180" href="images/favicon/apple-icon-180x180.png"><link rel="icon" type="image/png" sizes="192x192" href="images/favicon/android-icon-192x192.png"><link rel="icon" type="image/png" sizes="32x32" href="images/favicon/favicon-32x32.png"><link rel="icon" type="image/png" sizes="96x96" href="images/favicon/favicon-96x96.png"><link rel="icon" type="image/png" sizes="16x16" href="images/favicon/favicon-16x16.png"><link rel="stylesheet" href="https://unpkg.com/[email protected]/build/pure-min.css" integrity="sha384-nn4HPE8lTHyVtfCBi5yW9d20FjT8BJwUXyWZT9InLYax14RDjBj46LmSztkmNP9w" crossorigin="anonymous"><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans"><link rel="stylesheet" href="/docs/css/github.css"><link rel="stylesheet" href="/docs/css/mongoose5.css"><link rel="apple-touch-icon" sizes="57x57" href="images/favicon/apple-icon-57x57.png"><link rel="apple-touch-icon" sizes="60x60" href="images/favicon/apple-icon-60x60.png"><link rel="apple-touch-icon" sizes="72x72" href="images/favicon/apple-icon-72x72.png"><link rel="apple-touch-icon" sizes="76x76" href="images/favicon/apple-icon-76x76.png"><link rel="apple-touch-icon" sizes="114x114" href="images/favicon/apple-icon-114x114.png"><link rel="apple-touch-icon" sizes="120x120" href="images/favicon/apple-icon-120x120.png"><link rel="apple-touch-icon" sizes="144x144" href="images/favicon/apple-icon-144x144.png"><link rel="apple-touch-icon" sizes="152x152" href="images/favicon/apple-icon-152x152.png"><link rel="apple-touch-icon" sizes="180x180" href="images/favicon/apple-icon-180x180.png"><link rel="icon" type="image/png" sizes="192x192" href="images/favicon/android-icon-192x192.png"><link rel="icon" type="image/png" sizes="32x32" href="images/favicon/favicon-32x32.png"><link rel="icon" type="image/png" sizes="96x96" href="images/favicon/favicon-96x96.png"><link rel="icon" type="image/png" sizes="16x16" href="images/favicon/favicon-16x16.png"><link rel="manifest" href="images/favicon/manifest.json"><meta name="msapplication-TileColor" content="#ffffff"><meta name="msapplication-TileImage" content="images/favicon/ms-icon-144x144.png"><meta name="theme-color" content="#ffffff"><link rel="stylesheet" href="/docs/css/inlinecpc.css"><script type="text/javascript" src="/docs/js/native.js"></script></head><body><div id="layout"><div id="mobile-menu"><a class="menu-link" id="menuLink" href="#menu"><span></span></a><div id="mobile-logo-container"><a href="/"><img id="logo" src="/docs/images/mongoose5_62x30_transparent.png"><span class="logo-text">mongoose</span></a></div></div><div id="menu"><div class="pure-menu"><div class="pure-menu-heading" id="logo-container"><a href="/"><img id="logo" src="/docs/images/mongoose5_62x30_transparent.png"><span class="logo-text">mongoose</span></a></div><ul class="pure-menu-list" id="navbar"><li class="pure-menu-horizontal pure-menu-item pure-menu-has-children pure-menu-allow-hover version"><a class="pure-menu-link" href="#">Version 5.6.0</a><ul class="pure-menu-children"><li class="pure-menu-item"><a class="pure-menu-link" href="/docs/4.x">Version 4.13.18</a></li><li class="pure-menu-item"><a class="pure-menu-link" href="/docs/3.8.x">Version 3.8.40</a></li></ul></li><li class="pure-menu-item search"><input id="search-input-nav" type="text" placeholder="Search"><button id="search-button-nav"><img src="/docs/images/search.svg"></button></li><li class="pure-menu-item"><a class="pure-menu-link" href="/docs/index.html">Quick Start</a></li><li class="pure-menu-item"><a class="pure-menu-link" href="/docs/guides.html">Guides</a></li><li class="pure-menu-item sub-item"><a class="pure-menu-link" href="/docs/guide.html">Schemas</a></li><li class="pure-menu-item sub-item"><a class="pure-menu-link" href="/docs/schematypes.html">SchemaTypes</a></li><li class="pure-menu-item sub-item"><a class="pure-menu-link" href="/docs/connections.html">Connections</a></li><li class="pure-menu-item sub-item"><a class="pure-menu-link" href="/docs/models.html">Models</a></li><li class="pure-menu-item sub-item"><a class="pure-menu-link" href="/docs/documents.html">Documents</a></li><li class="pure-menu-item sub-item"><a class="pure-menu-link" href="/docs/subdocs.html">Subdocuments</a></li><li class="pure-menu-item sub-item"><a class="pure-menu-link" href="/docs/queries.html">Queries</a></li><li class="pure-menu-item sub-item"><a class="pure-menu-link" href="/docs/validation.html">Validation</a></li><li class="pure-menu-item sub-item"><a class="pure-menu-link" href="/docs/middleware.html">Middleware</a></li><li class="pure-menu-item sub-item"><a class="pure-menu-link" href="/docs/populate.html">Populate</a></li><li class="pure-menu-item sub-item"><a class="pure-menu-link" href="/docs/discriminators.html">Discriminators</a></li><li class="pure-menu-item sub-item"><a class="pure-menu-link" href="/docs/plugins.html">Plugins</a></li><li class="pure-menu-item"><a class="pure-menu-link" href="/docs/api.html">API</a></li><li class="pure-menu-item sub-item"><a class="pure-menu-link" href="/docs/api/mongoose.html">Mongoose</a></li><li class="pure-menu-item sub-item"><a class="pure-menu-link" href="/docs/api/schema.html">Schema</a></li><li class="pure-menu-item sub-item"><a class="pure-menu-link" href="/docs/api/connection.html">Connection</a></li><li class="pure-menu-item sub-item"><a class="pure-menu-link" href="/docs/api/document.html">Document</a></li><li class="pure-menu-item sub-item"><a class="pure-menu-link" href="/docs/api/model.html">Model</a></li><li class="pure-menu-item sub-item"><a class="pure-menu-link" href="/docs/api/query.html">Query</a></li><li class="pure-menu-item sub-item"><a class="pure-menu-link" href="/docs/api/aggregate.html">Aggregate</a></li><li class="pure-menu-item sub-item"><a class="pure-menu-link" href="/docs/api/schematype.html">SchemaType</a></li><li class="pure-menu-item sub-item"><a class="pure-menu-link" href="/docs/api/virtualtype.html">VirtualType</a></li><li class="pure-menu-item"><a class="pure-menu-link" href="/docs/compatibility.html">Version Compatibility</a></li><li class="pure-menu-item"><a class="pure-menu-link" href="/docs/faq.html">FAQ</a></li><li class="pure-menu-item"><a class="pure-menu-link" href="/docs/further_reading.html">Further Reading</a></li></ul><div class="cpc-ad"><script async type="text/javascript" src="//cdn.carbonads.com/carbon.js?zoneid=1673&serve=C6AILKT&placement=mongoosejscom" id="_carbonads_js"></script></div></div></div><div class="container"><div id="content"><h2 id="subdocuments">Subdocuments</h2>
<script>
_native.init("CK7DT53U",{
targetClass: 'native-inline'
});
</script>
<div class="native-inline">
<a href="#native_link#"><span class="sponsor">Sponsor</span> #native_company# — #native_desc#</a>
</div>
<p>Subdocuments are documents embedded in other documents. In Mongoose, this
means you can nest schemas in other schemas. Mongoose has two
distinct notions of subdocuments: arrays of subdocuments and single nested
subdocuments.</p>
<pre><code class="language-javascript"><span class="hljs-keyword">var</span> childSchema = <span class="hljs-keyword">new</span> Schema({ name: <span class="hljs-string">'string'</span> });
<span class="hljs-keyword">var</span> parentSchema = <span class="hljs-keyword">new</span> Schema({
<span class="hljs-comment">// Array of subdocuments</span>
children: [childSchema],
<span class="hljs-comment">// Single nested subdocuments. Caveat: single nested subdocs only work</span>
<span class="hljs-comment">// in mongoose >= 4.2.0</span>
child: childSchema
});</code></pre>
<ul class="toc">
<li><a href="#what-is-a-subdocument-">What is a Subdocument?</a></li>
<li><a href="#finding-a-subdocument">Finding a Subdocument</a></li>
<li><a href="#adding-subdocs-to-arrays">Adding Subdocs to Arrays</a></li>
<li><a href="#removing-subdocs">Removing Subdocs</a></li>
<li><a href="#subdoc-parents">Parents of Subdocs</a></li>
<li><a href="#altsyntax">Alternate declaration syntax for arrays</a></li>
</ul>
<h3 id="what-is-a-subdocument">What is a Subdocument?</h3>
<p>Subdocuments are similar to normal documents. Nested schemas can have
<a href="./middleware.html">middleware</a>, <a href="./validation.html">custom validation logic</a>,
virtuals, and any other feature top-level schemas can use. The major
difference is that subdocuments are
not saved individually, they are saved whenever their top-level parent
document is saved.</p>
<pre><code class="language-javascript"><span class="hljs-keyword">var</span> Parent = mongoose.model(<span class="hljs-string">'Parent'</span>, parentSchema);
<span class="hljs-keyword">var</span> parent = <span class="hljs-keyword">new</span> Parent({ children: [{ name: <span class="hljs-string">'Matt'</span> }, { name: <span class="hljs-string">'Sarah'</span> }] })
parent.children[<span class="hljs-number">0</span>].name = <span class="hljs-string">'Matthew'</span>;
<span class="hljs-comment">// `parent.children[0].save()` is a no-op, it triggers middleware but</span>
<span class="hljs-comment">// does **not** actually save the subdocument. You need to save the parent</span>
<span class="hljs-comment">// doc.</span>
parent.save(callback);</code></pre>
<p>Subdocuments have <code>save</code> and <code>validate</code> <a href="./middleware.html">middleware</a>
just like top-level documents. Calling <code>save()</code> on the parent document triggers
the <code>save()</code> middleware for all its subdocuments, and the same for <code>validate()</code>
middleware.</p>
<pre><code class="language-javascript">childSchema.pre(<span class="hljs-string">'save'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">next</span>) </span>{
<span class="hljs-keyword">if</span> (<span class="hljs-string">'invalid'</span> == <span class="hljs-keyword">this</span>.name) {
<span class="hljs-keyword">return</span> next(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'#sadpanda'</span>));
}
next();
});
<span class="hljs-keyword">var</span> parent = <span class="hljs-keyword">new</span> Parent({ children: [{ name: <span class="hljs-string">'invalid'</span> }] });
parent.save(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err</span>) </span>{
<span class="hljs-built_in">console</span>.log(err.message) <span class="hljs-comment">// #sadpanda</span>
});</code></pre>
<p>Subdocuments' <code>pre('save')</code> and <code>pre('validate')</code> middleware execute
<strong>before</strong> the top-level document's <code>pre('save')</code> but <strong>after</strong> the
top-level document's <code>pre('validate')</code> middleware. This is because validating
before <code>save()</code> is actually a piece of built-in middleware.</p>
<pre><code class="language-javascript"><span class="hljs-comment">// Below code will print out 1-4 in order</span>
<span class="hljs-keyword">var</span> childSchema = <span class="hljs-keyword">new</span> mongoose.Schema({ name: <span class="hljs-string">'string'</span> });
childSchema.pre(<span class="hljs-string">'validate'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">next</span>) </span>{
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'2'</span>);
next();
});
childSchema.pre(<span class="hljs-string">'save'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">next</span>) </span>{
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'3'</span>);
next();
});
<span class="hljs-keyword">var</span> parentSchema = <span class="hljs-keyword">new</span> mongoose.Schema({
child: childSchema,
});
parentSchema.pre(<span class="hljs-string">'validate'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">next</span>) </span>{
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'1'</span>);
next();
});
parentSchema.pre(<span class="hljs-string">'save'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">next</span>) </span>{
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'4'</span>);
next();
});</code></pre>
<h3 id="finding-a-subdocument">Finding a Subdocument</h3><p>Each subdocument has an <code>_id</code> by default. Mongoose document arrays have a
special <a href="./api.html#types_documentarray_MongooseDocumentArray-id">id</a> method
for searching a document array to find a document with a given <code>_id</code>.</p>
<pre><code class="language-javascript"><span class="hljs-keyword">var</span> doc = parent.children.id(_id);</code></pre>
<h3 id="adding-subdocs-to-arrays">Adding Subdocs to Arrays</h3><p>MongooseArray methods such as
<a href="./api.html#mongoosearray_MongooseArray-push">push</a>,
<a href="./api.html#mongoosearray_MongooseArray-unshift">unshift</a>,
<a href="./api.html#mongoosearray_MongooseArray-addToSet">addToSet</a>,
and others cast arguments to their proper types transparently:</p>
<pre><code class="language-javascript"><span class="hljs-keyword">var</span> Parent = mongoose.model(<span class="hljs-string">'Parent'</span>);
<span class="hljs-keyword">var</span> parent = <span class="hljs-keyword">new</span> Parent;
<span class="hljs-comment">// create a comment</span>
parent.children.push({ name: <span class="hljs-string">'Liesl'</span> });
<span class="hljs-keyword">var</span> subdoc = parent.children[<span class="hljs-number">0</span>];
<span class="hljs-built_in">console</span>.log(subdoc) <span class="hljs-comment">// { _id: '501d86090d371bab2c0341c5', name: 'Liesl' }</span>
subdoc.isNew; <span class="hljs-comment">// true</span>
parent.save(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err</span>) </span>{
<span class="hljs-keyword">if</span> (err) <span class="hljs-keyword">return</span> handleError(err)
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Success!'</span>);
});</code></pre>
<p>Subdocs may also be created without adding them to the array by using the
<a href="./api.html#types_documentarray_MongooseDocumentArray.create">create</a>
method of MongooseArrays.</p>
<pre><code class="language-javascript"><span class="hljs-keyword">var</span> newdoc = parent.children.create({ name: <span class="hljs-string">'Aaron'</span> });</code></pre>
<h3 id="removing-subdocs">Removing Subdocs</h3><p>Each subdocument has it's own
<a href="./api.html#types_embedded_EmbeddedDocument-remove">remove</a> method. For
an array subdocument, this is equivalent to calling <code>.pull()</code> on the
subdocument. For a single nested subdocument, <code>remove()</code> is equivalent
to setting the subdocument to <code>null</code>.</p>
<pre><code class="language-javascript"><span class="hljs-comment">// Equivalent to `parent.children.pull(_id)`</span>
parent.children.id(_id).remove();
<span class="hljs-comment">// Equivalent to `parent.child = null`</span>
parent.child.remove();
parent.save(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err</span>) </span>{
<span class="hljs-keyword">if</span> (err) <span class="hljs-keyword">return</span> handleError(err);
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'the subdocs were removed'</span>);
});</code></pre>
<h3 id="subdoc-parents">Parents of Subdocs</h3><p>Sometimes, you need to get the parent of a subdoc. You can access the
parent using the <code>parent()</code> function.</p>
<pre><code class="language-javascript"><span class="hljs-keyword">const</span> schema = <span class="hljs-keyword">new</span> Schema({
docArr: [{ name: <span class="hljs-built_in">String</span> }],
singleNested: <span class="hljs-keyword">new</span> Schema({ name: <span class="hljs-built_in">String</span> })
});
<span class="hljs-keyword">const</span> Model = mongoose.model(<span class="hljs-string">'Test'</span>, schema);
<span class="hljs-keyword">const</span> doc = <span class="hljs-keyword">new</span> Model({
docArr: [{ name: <span class="hljs-string">'foo'</span> }],
singleNested: { name: <span class="hljs-string">'bar'</span> }
});
doc.singleNested.parent() === doc; <span class="hljs-comment">// true</span>
doc.docArr[<span class="hljs-number">0</span>].parent() === doc; <span class="hljs-comment">// true</span></code></pre>
<p>If you have a deeply nested subdoc, you can access the top-level document
using the <code>ownerDocument()</code> function.</p>
<pre><code class="language-javascript"><span class="hljs-keyword">const</span> schema = <span class="hljs-keyword">new</span> Schema({
level1: <span class="hljs-keyword">new</span> Schema({
level2: <span class="hljs-keyword">new</span> Schema({
test: <span class="hljs-built_in">String</span>
})
})
});
<span class="hljs-keyword">const</span> Model = mongoose.model(<span class="hljs-string">'Test'</span>, schema);
<span class="hljs-keyword">const</span> doc = <span class="hljs-keyword">new</span> Model({ level1: { level2: <span class="hljs-string">'test'</span> } });
doc.level1.level2.parent() === doc; <span class="hljs-comment">// false</span>
doc.level1.level2.parent() === doc.level1; <span class="hljs-comment">// true</span>
doc.level1.level2.ownerDocument() === doc; <span class="hljs-comment">// true</span></code></pre>
<h4 id="altsyntax">Alternate declaration syntax for arrays</h4><p>If you create a schema with an array of objects, mongoose will automatically
convert the object to a schema for you:</p>
<pre><code class="language-javascript"><span class="hljs-keyword">var</span> parentSchema = <span class="hljs-keyword">new</span> Schema({
children: [{ name: <span class="hljs-string">'string'</span> }]
});
<span class="hljs-comment">// Equivalent</span>
<span class="hljs-keyword">var</span> parentSchema = <span class="hljs-keyword">new</span> Schema({
children: [<span class="hljs-keyword">new</span> Schema({ name: <span class="hljs-string">'string'</span> })]
});</code></pre>
<h3 id="next">Next Up</h3><p>Now that we've covered Subdocuments, let's take a look at
<a href="./queries.html">querying</a>.</p>
</div></div><script type="text/javascript">var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://g0a3nbw0xa.execute-api.us-east-1.amazonaws.com/prod/track', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onreadystatechange = function() {};
xhr.send(JSON.stringify({
path: window.location.pathname,
hostname: window.location.hostname,
hash: window.location.hash
}));</script><script type="text/javascript" src="/docs/js/navbar-search.js"></script><script type="text/javascript">(function (window, document) {
var layout = document.getElementById('layout'),
menu = document.getElementById('menu'),
menuLink = document.getElementById('menuLink'),
content = document.getElementById('content');
function toggleClass(element, className) {
var classes = element.className.split(/\s+/),
length = classes.length,
i = 0;
for(; i < length; i++) {
if (classes[i] === className) {
classes.splice(i, 1);
break;
}
}
// The className is not found
if (length === classes.length) {
classes.push(className);
}
element.className = classes.join(' ');
}
function toggleAll(e) {
var active = 'active';
e.preventDefault();
toggleClass(layout, active);
toggleClass(menu, active);
toggleClass(menuLink, active);
}
menuLink.onclick = function (e) {
toggleAll(e);
};
content.onclick = function(e) {
if (menu.className.indexOf('active') !== -1) {
toggleAll(e);
}
};
}(this, this.document));</script></div></body></html>