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
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
|
---
title: 'Учебник Express часть 3: Использование базы данных (с помощью Mongoose)'
slug: Learn/Server-side/Express_Nodejs/mongoose
translation_of: Learn/Server-side/Express_Nodejs/mongoose
---
<div>{{LearnSidebar}}</div>
<div>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/skeleton_website", "Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs")}}</div>
<p class="summary">В этой статье дается краткое введение в базы данных, и методика их использования в приложениях Node/Express. Затем мы покажем, как можно использовать <a href="http://mongoosejs.com/">Mongoose</a> для доступа к базе данных веб-сайта <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">LocalLibrary</a>. Мы объясним, как объявляются схемы и модели объектов, укажем основные типы полей, и методику базовой валидации. В статье также кратко показаны основные методы доступа к данным модели.</p>
<table class="learn-box standard-table">
<tbody>
<tr>
<th scope="row">Предварительные сведения:</th>
<td><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">Express Tutorial Part 2: Creating a skeleton website</a></td>
</tr>
<tr>
<th scope="row">Цель:</th>
<td>Уметь спроектировать и создать свои модели, используя Mongoose.</td>
</tr>
</tbody>
</table>
<h2 id="Обзор">Обзор</h2>
<p>Сотрудники библиотеки будут использовать сайт Local Library для хранения информации о книгах и абонентах, а абоненты библиотеки будут использовать его для просмотра и поиска книг, для получения информации о доступных копиях, для резервирования или одалживания книг. Чтобы эффективно хранить и извлекать информацию, мы будем хранить ее в базе данных.</p>
<p>Express-приложения могут использовать различные базы данных, и есть несколько подходов, которые можно использовать для выполнения операций <strong>C</strong>reate, <strong>R</strong>ead, <strong>U</strong>pdate and <strong>D</strong>elete (CRUD) (создать, прочесть, обновить, удалить). В руководстве дан краткий обзор некоторых доступных опций, и детально рассмотрены некоторые механизмы работы.</p>
<h3 id="Какие_базы_данных_можно_использовать">Какие базы данных можно использовать?</h3>
<p><em>Express-</em>приложение может использовать любые базы данных, поддерживаемые <em>Node</em> (сам по себе Express не определяет каких-либо конкретных дополнительных свойств и требований для управления базами данных). Есть <a href="https://expressjs.com/en/guide/database-integration.html">много популярных </a>вариантов -- PostgreSQL, MySQL, Redis, SQLite, и MongoDB.</p>
<p>При выборе базы данных следует учитывать такие факторы как время разработки, время обучения, простота репликации и копирования, расходы, поддержка сообщества и т. д. Хотя нет единственной "лучшей" базы данных, почти любое из популярных решений будет приемлемым для сайта малого и среднего размера, такого как наша Local Library.</p>
<p>Более подробно о вариантах смотрите в: <a href="https://expressjs.com/en/guide/database-integration.html">Database integration</a> (Express docs).</p>
<h3 id="Каков_наилучший_способ_взаимодействия_с_базой_данных">Каков наилучший способ взаимодействия с базой данных?</h3>
<p>Существует два подхода при работе с базой данных:</p>
<ul>
<li>Использование родного языка запросов баз данных (т.е. SQL)</li>
<li>Использование объектной модели данных (ODM) или объектно-реляционной модели (ORM). ODM / ORM представляют данные веб-сайта как объекты JavaScript, которые затем отображаются на поддерживающую базу данных. Некоторые ORM привязаны к определенной базе данных, тогда как другие не зависят от конкретной базы данных.</li>
</ul>
<p>Наилучшую производительность можно получить с помощью SQL или другого языка запросов, поддерживаемого базой данных. Объектные модели (ODM) часто медленнее, потому что требуют перевода объектов в формат базы данных, при этом не обязательно будут использованы наиболее эффективные запросы к базе данных (особенно, если ODM предназначена для различных баз данных и должна идти на большие компромиссы в смысле поддержки тех или иных функций базы данных).</p>
<p>Преимущество применения ORM состоит в том, что программисты могут сосредоточиться на объектах JavaScript, а не на семантике базы данных — особенно, если требуется работать с разными базами данных (на одном или разных веб-сайтах). Они также дают очевидное место для валидации и проверки данных.</p>
<div class="note">
<p>Совет: Применение ODM / ORMs часто приводит к снижению затрат на разработку и обслуживание! Если Вы не очень хорошо знакомы с родным языком запросов или если производительность имеет первостепенное значение, следует серьезно рассмотреть возможность применения ODM.</p>
</div>
<h3 id="Какую_модель_ORMODM_следует_использовать">Какую модель ORM/ODM следует использовать?</h3>
<p>Есть много ODM/ORM доступных решений на сайте менеджера пакетов NPM (проверьте теги по подгруппе <a href="https://www.npmjs.com/browse/keyword/odm">odm</a> и <a href="https://www.npmjs.com/browse/keyword/orm">orm</a>).</p>
<p>Популярные решения на момент написания статьи:</p>
<ul>
<li><a href="https://www.npmjs.com/package/mongoose">Mongoose</a>: -- это средство моделирование объектов базы данных <a href="https://www.mongodb.org/">MongoDB</a>, предназначенное для асинхронной работы.</li>
<li><a href="https://www.npmjs.com/package/waterline">Waterline</a>: ORM фреймворка <a href="http://sailsjs.com/">Sails</a> (основан на Express) . Она предоставляет единый API для доступа к множеству баз данных, в том числе Redis, mySQL, LDAP, MongoDB, и Postgres.</li>
<li><a href="https://www.npmjs.com/package/bookshelf">Bookshelf</a>: поддерживает как promise- так и традиционные callback- интерфейсы, поддержка транзакций, eager/nested-eager relation loading, полиморфные ассоциации, и поддержка, один к одному, один ко многим, и многие ко многим. Работает с PostgreSQL, MySQL, и SQLite3.</li>
<li><a href="https://www.npmjs.com/package/objection">Objection</a>: Делает настолько легким, насколько возможно, использование всей мощи SQL и движка базы данных ( поддерживает SQLite3, Postgres, и MySQL).</li>
<li><a href="https://www.npmjs.com/package/sequelize">Sequelize</a>: Основанная на промисах ORM для Node.js и <a href="https://ru.wikipedia.org/wiki/Io.js">io.js</a>. Поддерживает диалекты PostgreSQL, MySQL, MariaDB, SQLite и MSSQL, обладает надежной поддержкой транзакций, отношений, чтения копий и т.д.</li>
<li>
<p><a href="https://node-orm.readthedocs.io/en/latest/"><font color="#3d7e9a"><font face="Arial, x-locale-body, sans-serif"><font size="3">Node ORM2</font></font></font></a><font color="#333333"><font face="Arial, x-locale-body, sans-serif"><font size="3"> -- это OR менеджер для NodeJS. Поддерживает MySQL, SQLite и Progress, помогает работать с БД, используя объектный подход.</font></font></font></p>
</li>
<li>
<p><a href="http://1602.github.io/jugglingdb/"><font color="#3d7e9a"><font face="Arial, x-locale-body, sans-serif"><font size="3">JugglingDB</font></font></font></a><font color="#333333"><font face="Arial, x-locale-body, sans-serif"><font size="3"> -- это кросс-ДБ ORM для NodeJS, обеспечивающая общий интерфейс для доступа к наиболее популярным форматам БД. Поддерживает MySQL, SQLite3, Postgres, MongoDB, Redis и хранение данных в памяти js (собственный движок, только для тестирования).</font></font></font></p>
</li>
</ul>
<p>Как правило, при выборе решения следует учитывать как предоставляемые функции, так и "деятельность сообщества" ( загрузки, вклад, отчеты об ошибках, качество документации, и т.д. ) . На момент написания статьи Mongoose являлась очень популярной ORM, и разумно, если вы выбрали MongoDB.</p>
<h3 id="Применение_Mongoose_и_MongoDb_для_LocalLibrary">Применение Mongoose и MongoDb для LocalLibrary</h3>
<p><span id="result_box" lang="ru"><span class="alt-edited">В примере LocalLibrary (и до конца раздела) мы будем использовать Mongoose ODM для доступа к данным </span></span><span lang="ru"><span class="alt-edited">нашей библиотеки.</span> <span class="alt-edited">Mongoose является интерфейсом для MongoDB, NoSQL-базы данных с открытым исходным кодом, в которой использована документов-ориентированная модель данных.</span> В <span class="alt-edited">MongoDB </span><span class="alt-edited">«коллекции» и «документы» -- это аналоги «таблиц» и «строк» в реляционных БД</span></span>.</p>
<p><span id="result_box" lang="ru"><span>Это сочетание ODM и БД весьма популярно в сообществе Node, частично потому, что система хранения документов и запросов очень похожа на JSON и поэтому знакома разработчикам JavaScript</span></span>.</p>
<div class="note">
<p><span id="result_box" lang="ru"><span><strong>Совет</strong>: Не обязательно знать MongoDB, чтобы использовать Mongoose, хотя <a href="http://mongoosejs.com/docs/guide.html"> документацию Mongoose</a> легче использовать и понимать, если вы уже знакомы с MongoDB.</span></span></p>
</div>
<p><span id="result_box" lang="ru"><span>Далее показано, как определить и получить доступ к схеме и моделям Mongoose для примера веб-сайта LocalLibrary.</span></span></p>
<h2 id="Проектирование_моделей_LocalLibrary">Проектирование моделей LocalLibrary</h2>
<p> </p>
<p>Прежде чем начинать писать код моделей, стоит обдумать, какие данные нам нужно хранить, и каковы отношения между разными объектами.</p>
<p>Мы знаем, что нужно хранить информацию о книгах (название, резюме (краткое описание), автор, жанр, ISBN (Международный стандартный книжный номер) ) и что может быть несколько доступных экземпляров (с уникальными идентификаторами, статусом наличия и т. д.). Может потребоваться хранить больше информации об авторе (не только имя, т.к. могут быть авторы с одинаковыми или похожими именами). Мы хотим иметь возможность сортировать данные по названиям книг, по авторам, по жанрам и категориям.</p>
<p>При проектировании моделей имеет смысл иметь отдельные модели для каждого «объекта» (группы связанных данных). В этом случае очевидными объектами являются книги, экземпляры книг и авторы.</p>
<p>Можно также использовать модели для представления параметров списка выбора (например, как выпадающий список вариантов), вместо жесткого кодирования выбора на самом веб-сайте - рекомендуется, когда не все параметры известны или могут быть изменены. Явный кандидат для модели такого типа -- это жанр книги (например, «Научная фантастика», «Французская поэзия» и т.д.),</p>
<p>Как только мы определились с моделями и полями, следует подумать об отношениях между ними.</p>
<p>С учетом сказанного, UML-диаграмма связей (см. ниже) показывает модели, которые представлены как прямоугольники. Мы решили, что создадим модели для книги (общие сведения о книге), для экземпляра книги (состояние отдельных физических копий книги, доступных в системе) и для автора. Кроме того, у нас будет модель для жанра, чтобы эти значения можно было создавать динамически. Решено не создавать модель для <code>BookInstance:status</code> — мы пропишем в коде необходимые значения, потому что не ожидаем их изменения. На элементах диаграммы показаны имя модели, имена и типы полей, имена методов и типы их результатов .</p>
<p>Также показаны отношения между моделями, включая множественные отношения. Числа на линиях связи показывают максимум и минимум моделей, участвующих отношении. Например, линия между <code>Book</code> и <code>Genre</code> показывает, что <code>Book</code> и <code>Genre</code> связаны. Числа на этой линии рядом с моделью <code>Book</code> показывают, что жанр может быть связан с любым количеством книг, а числа на другом конце линии рядом с <code>Genre</code> отмечают, что книга может быть связана с любым количеством жанров.</p>
<div class="note">
<p><strong>Заметка</strong>: Как показано в примере<a href="#related_documents">Mongoose primer</a> ниже, часто лучше иметь поле, определяющее отношение между документами (моделями), только в одной модели (обратное отношение можно найти по присвоенному идентификатору <code>_id</code> в другой модели). Ниже мы предпочли задать отношения между Book/Genre и между Book/Author в схеме Book, а отношение между Book/BookInstance -- в схеме BookInstance. Этот выбор в некотором смысле был произвольным -- таким же хорошим мог бы быть выбор другого поля в другой схеме.</p>
</div>
<p><img alt="Mongoose Library Model with correct cardinality" src="https://mdn.mozillademos.org/files/15645/Library%20Website%20-%20Mongoose_Express.png" style="height: 620px; width: 737px;"></p>
<div class="note">
<p><strong>Заметка</strong>: В следующем разделе дан базовый пример, в котором объясняется, как задавать и как использовать модели. При чтении обратите внимание, как будут создаваться модели, приведенные на диаграмме.</p>
</div>
<h2 id="Mongoose_Справочник">Mongoose Справочник</h2>
<p>В этом разделе кратко описано как подключиться к базе MongoDB с помощью Mongooseе, как определить схемы и модели, как сформировать основные запросы.</p>
<div class="note">
<p><strong> Примечание: </strong>На этот пример значительно повлияли документы <a href="https://www.npmjs.com/package/mongoose">Mongoose <font color="#3d7e9a"><font face="x-locale-heading-primary, zillaslab, Palatino, Palatino Linotype, x-locale-heading-secondary, serif"><font size="3">quick start</font></font></font></a> на npm и <a href="http://mongoosejs.com/docs/guide.html">официальная документация.</a></p>
</div>
<h3 id="Установка_Mongoose_и_MongoDB">Установка Mongoose и MongoDB</h3>
<p>Mongoose устанавливается в ваш проект (<strong>package.json</strong>) как и другие зависимости - при помощи NPM. Команда установки (выполняется из каталога проекта): </p>
<pre class="brush: bash"><code>npm install mongoose</code>
</pre>
<p>Установка <em>Mongoose </em>добавит все зависимости, включая драйвер MongoDB, но не установит саму базу данных. При желании установить сервер MongoDB локально, можно <a href="https://www.mongodb.com/download-center">скачать установочный файл здесь</a> для своей операционной системы и установить его. Также можно использовать облако MongoDB.</p>
<div class="note">
<p><strong>Примечание:</strong> В примере для хранения базы данных мы используем облачный сервис <a href="https://mlab.com/plans/pricing/">sandbox tier</a> ("песочницу"). This is suitable for development, and makes sense for the tutorial because it makes "installation" operating system independent (database-as-a-service is also one approach you might well use for your production database).</p>
</div>
<h3 id="Подключение_к_MongoDB">Подключение к MongoDB</h3>
<p><em>Mongoose </em>требует подключение к MongoDB. Вы можете использовать require() и подключится к локальной БД при помощи <code>mongoose.connect(),</code> как показано ниже.</p>
<pre class="brush: js">// Импортировать модуль mongoose
var mongoose = require('mongoose');
// Установим подключение по умолчанию
var mongoDB = 'mongodb://127.0.0.1/my_database';
mongoose.connect(mongoDB);
// Позволим Mongoose использовать глобальную библиотеку промисов
mongoose.Promise = global.Promise;
// Получение подключения по умолчанию
var db = mongoose.connection;
// Привязать подключение к событию ошибки (получать сообщения об ошибках подключения)
db.on('error', console.error.bind(console, 'MongoDB connection error:'));
</pre>
<p>При помощи <code>mongoose.connection</code> можно получить стандартный объект <code>Connection</code>. После подключения в экземпляре <code>Connection</code> возникает событие open (открыт).</p>
<div class="note">
<p><strong>Tip:</strong> Если необходимо создать дополнительные подключения, можно использовать <code>mongoose.createConnection()</code>. При этом будут применены те же БД URI (хост, БД, порт, опции и т.д.), что и в <code>connect()</code> и будет возвращен объект <code>Connection</code>.</p>
</div>
<h3 id="Определение_и_создание_моделей">Определение и создание моделей</h3>
<p>Модели можно создать при помощи интерфейса <code>Schema</code> . Schema позволяет указать поля, которые будут в каждом документе, значения полей по умолчанию и требования по валидации. Кроме того, можно задать статические методы и методы-хелперы (от help), облегчающие работу с вашими типами данных, а также задать виртуальные свойства, которые можно использовать как и обычные поля, но без влияния на данные в самой базе данных.</p>
<p>Схемы "компилируются " в окончательную модель методом <code>mongoose.model()</code>. После создания модели ее можно использовать для поиска, создания, обновления и удаления объектов данного типа.</p>
<div class="note">
<p><strong>Заметка:</strong> Каждой модели соответствует <em>коллекция</em> <em>документов</em> в ДБ MongoDB. Документы будут содержать поля тех типов, которые заданы в модели <code>Schema</code>.</p>
</div>
<h4 id="Определение_схем_данных">Определение схем данных</h4>
<p>Код ниже показывает, как можно задать простую схему. Сначала при помощи <code>require()</code> создается объект mongoose, затем конструктор Schema создает новый экземпляр схемы, при этом различные поля задаются как параметры конструктора.</p>
<pre class="brush: js">//Требуется Mongoose
var mongoose = require('mongoose');
//Определяем схему
var Schema = mongoose.Schema;
var SomeModelSchema = new Schema({
a_string: String,
a_date: Date
});
</pre>
<p>В примере созданы два поля, типа String и типа Date. В следующем разделе будут примеры полей других типов, их валидации и примеры других методов.</p>
<h4 id="Создание_модели">Создание модели</h4>
<p>Модели создаются из схем методом <code>mongoose.model()</code>:</p>
<pre class="brush: js">// Определяем схему
var Schema = mongoose.Schema;
var SomeModelSchema = new Schema({
a_string: String,
a_date: Date
});
<strong>// Компилируем модель из схемы
var SomeModel = mongoose.model('SomeModel', SomeModelSchema );</strong></pre>
<p>Первый аргумент - уникальное имя создаваемой для модели коллекции(Mongoose создаст коллекцию для модели <em>SomeModel</em>), второй аргумент - схема, которая используется для создания модели.</p>
<div class="note">
<p><strong>Заметка:</strong> После создания классов модели они могут применяться для создания, обновления или удаления записей в базе, для выполнения запросов по всем записям или по их подмножествам. Как это делать, будет показано в разделе <a href="#Using_models">Использование моделей</a>, и когда будут создаваться представления.</p>
</div>
<h4 id="Типы_схемы_(поля)">Типы схемы (поля)</h4>
<p>Схема может содержать любое количество полей, причем каждое поле будет полем документа, хранимого в БД <em>MongoDB</em>. Схема-пример содержит определения многих широко используемых типов полей.</p>
<pre class="brush: js">var schema = new Schema(
{
name: <strong>String</strong>,
binary: <strong>Buffer</strong>,
living: <strong>Boolean</strong>,
updated: { type: <strong>Date</strong>, default: Date.now },
age: { type: <strong>Number</strong>, min: 18, max: 65, required: true },
mixed: <strong>Schema.Types.Mixed</strong>,
_someId: <strong>Schema.Types.ObjectId</strong>,
array: <strong>[]</strong>,
ofString: [<strong>String</strong>], // You can also have an array of each of the other types too.
nested: { stuff: { type: <strong>String</strong>, lowercase: true, trim: true } }
})</pre>
<p>Большинство типов в <a href="http://mongoosejs.com/docs/schematypes.html">SchemaTypes</a> (указаны после “type:” или после имен полей) достаточно очевидны. Исключения:</p>
<ul>
<li><code>ObjectId</code>: Представляет отдельный экземпляр модели в БД. Например, book может ссылаться на объект- автора. Поле будет содержать уникальный идентификатор (<code>_id</code>) отдельного объекта. При необходимости использования этой информации применяют метод <code>populate()</code>.</li>
<li><a href="http://mongoosejs.com/docs/schematypes.html#mixed">Mixed</a>: Произвольный тип в схеме.</li>
<li><font face="Consolas, Liberation Mono, Courier, monospace">[]</font>: Массив элементов. В таких моделях можно выполнять JavaScript-операции для массивов (push, pop, unshift, etc.). Выше показан пример массивы объектов неопределенного типа и массив строк, но можно использовать массив объектов любого типа.</li>
</ul>
<p>Код содержит также два способа объявления полей:</p>
<ul>
<li><em>Имя</em> и <em>тип</em>поля как пара "ключ-значение" (поля <code>name</code>, <code>binary и</code> <code>living</code>).</li>
<li><em>Имя</em> поля, после которого указывается объект, определяющий <em>тип</em> и другие возможности поля, такие как:
<ul>
<li>значения по умолчанию.</li>
<li>встроенные валидаторы (например, значения max и min) и функции-валидаторы пользователя.</li>
<li>Является ли поле обязательным</li>
<li>Должны ли строковые поля автоматически преобразовываться в нижний или верхний регистр, удалять ли ведущие и хвостовые пробелы (<code>пример:</code> <code>{ type: <strong>String</strong>, lowercase: true, trim: true }</code>)</li>
</ul>
</li>
</ul>
<p>Дополнительная информация - в <a href="http://mongoosejs.com/docs/schematypes.html">SchemaTypes</a> (документация Mongoose).</p>
<h4 id="Валидация_(проверка_допустимости)">Валидация (проверка допустимости)</h4>
<p>Mongoose предусматривает встроенные валидаторы, валидаторы пользователя, синхронные и асинхронные валидаторы. Во всех случаях можно задать допустимые диапазоны или значения, а также сообщения об ошибках при нарушении условий валидации.</p>
<p>Встроенные валидаторы включают:</p>
<ul>
<li>Все <a href="http://mongoosejs.com/docs/schematypes.html">SchemaTypes</a> имеют встроенный валидатор <a href="http://mongoosejs.com/docs/api.html#schematype_SchemaType-required">required</a>, который определяет, должно ли поле быть заданным перед сохранением документа.</li>
<li><a href="http://mongoosejs.com/docs/api.html#schema-number-js">Числа</a> имеют валидаторы <a href="http://mongoosejs.com/docs/api.html#schema_number_SchemaNumber-min">min</a> и <a href="http://mongoosejs.com/docs/api.html#schema_number_SchemaNumber-max">max</a>.</li>
<li><a href="http://mongoosejs.com/docs/api.html#schema-string-js">Строки</a> имеют:
<ul>
<li><a href="http://mongoosejs.com/docs/api.html#schema_string_SchemaString-enum">enum</a> (перечисления): задают множество допустимых для поля значений.</li>
<li><a href="http://mongoosejs.com/docs/api.html#schema_string_SchemaString-match">match</a> (соответствия)): задают регулярное выражение, которому должна соответствовать строка.</li>
<li><a href="http://mongoosejs.com/docs/api.html#schema_string_SchemaString-maxlength">maxlength</a>, <a href="http://mongoosejs.com/docs/api.html#schema_string_SchemaString-minlength">minlength</a> -максимальная и минимальная длина строки.</li>
</ul>
</li>
</ul>
<p>Пример ниже (с небольшими изменениями из документации Mongoose) показывает, как задать некоторые валидаторы и сообщения об ошибках:</p>
<pre class="brush: js"><code>
var breakfastSchema = new Schema({
eggs: {
type: Number,
min: [6, 'Too few eggs'],
max: 12
required: [true, 'Why no eggs?']
},
drink: {
type: String,
enum: ['Coffee', 'Tea', 'Water',]
}
});
</code></pre>
<p>Подробная информация по валидации полей - в разделе <a href="http://mongoosejs.com/docs/validation.html">Validation</a> (документация Mongoose).</p>
<h4 id="Виртуальные_свойства">Виртуальные свойства</h4>
<p>Виртуальные свойства - это свойства документа, которые можно читать (get) и задавать (set), но которые не хранятся в MongoDB. Методы "геттеры" полезны для форматирования и соединения полей, а "сеттеры" применяют для декомпозиции отдельных значений на несколько частей перед сохранением в БД. Пример из документации собирает (и разбирает) виртуальное свойство "полное имя" из полей "имя" и "фамилия", что удобнее, чем конструировать полное имя каждый раз, когда оно используется в шаблоне.</p>
<div class="note">
<p><strong>Замечание:</strong> В библиотеке виртуальное свойство будет применено для определения уникального URL каждой записи в модели по пути и по значению <code>_id</code> записи.</p>
</div>
<p>Подробная информация - в разделе <a href="http://mongoosejs.com/docs/guide.html#virtuals">Virtuals</a> (документация Mongoose).</p>
<h4 id="Методы_и_помощники_запросов">Методы и помощники запросов</h4>
<p>В схеме можно также задать методы экземпляра (<a href="http://mongoosejs.com/docs/guide.html#methods">instance methods</a>), статические (<a href="http://mongoosejs.com/docs/guide.html#statics">static</a>) методы и <a href="http://mongoosejs.com/docs/guide.html#query-helpers">помощники запросов</a>. Статические методы и методы экземпляра аналогичны, но различаются тем, что методы экземпляра связаны с конкретной записью и имеют доступ к текущему объекту. Помощники запросов позволяют расширить <a href="http://mongoosejs.com/docs/queries.html"> API построителя цепочек запросов</a> (например, можно добавить запрос "byName" ("по имени") в дополнение к методам <code>find()</code>, <code>findOne()</code> и <code>findById()</code>).</p>
<h3 id="Применение_моделей">Применение моделей</h3>
<p>Подготовленную схему можно использовать для создания моделей. Модель представляет коллекцию документов в базе данных, в которой можно выполнять поиск, тогда как экземпляры модели представляют отдельные документы, которые можно сохранять и извлекать.</p>
<p>Ниже предлагается краткий обзор. Более подробно смотрите в <a href="http://mongoosejs.com/docs/models.html">Models</a> (документация Mongoose).</p>
<h4 id="Создание_и_изменение_документов">Создание и изменение документов</h4>
<p>Чтобы создать запись, следует определить экземпляр модели и вызвать метод <code>save()</code>. В примере ниже SomeModel -- это модель с единственным полем "name", которую мы создадим из нашей схемы.</p>
<pre class="brush: js"><code>// Создать экземпляр модели SomeModel
var awesome_instance = new </code>SomeModel<code>({ name: 'awesome' });
// Сохранить новый экземпляр, передав callback
awesome_instance.save(function (err) {
if (err) return handleError(err);
// сохранили!
});
</code></pre>
<p>Создание записей (а также обновления, удаления и запросы) - это асинхронные операции, поэтому следует предусмотреть колбэк-функцию, которая будет вызвана при завершении операции. В API используется соглашение о первом аргументе, согласно которому первый аргумент колбэк-функции должен быть значением ошибки (или null). Если API возвращает некоторый результат, он должен быть вторым аргументом.</p>
<p>Можно использовать метод <code>create()</code> для создании экземпляра модели при его сохранении. Тогда колбэк-функция вернет ошибку (или null) как первый аргумент и только что созданный экземпляр как второй аргумент.</p>
<pre class="brush: js">SomeModel<code>.create({ name: 'also_awesome' }, function (err, awesome_instance) {
if (err) return handleError(err);
// сохранили!
});</code></pre>
<p>Каждая модель ассоциирована с соединением (с соединением по умолчанию, если используется <code>mongoose.model()</code>). Следует создать новое соединение и вызвать для него <code>.model()</code>, чтобы создать документ в другой базе данных.</p>
<p>Поля в новой записи могут быть получены и изменены с применением dot (точка)-синтаксиса. Для сохранения изменений служат методы <code>save()</code> и <code>update()</code>.</p>
<pre class="brush: js">// Доступ к полям модели в dot-нотации
console.log(<code>awesome_instance.name</code>); //вывод в консоль '<code>also_awesome</code>'
// Изменить запись, модифицируя поля, потом вызвать save().
<code>awesome_instance</code>.name="New cool name";
<code>awesome_instance.save(function (err) {
if (err) return handleError(err); // сохранили!
});</code>
</pre>
<h4 id="Поиск_записей">Поиск записей</h4>
<p>При поиске записей методами запросов, условия поиска следует задавать как документ JSON. Приведенный фрагмент кода (ниже) показывает, как в БД найти имена (<em>name</em>) и возраст (<em>age</em>) всех спортсменов-теннисистов. Соответствие будет определяться по одному полю (sport), но можно добавить критерии поиска, задав, например, регулярное выражение, или удалить все критерии, чтобы получить список всех спортсменов.</p>
<pre class="brush: js"><code>var Athlete = mongoose.model('Athlete', yourSchema);
// найти всех теннисистов, выбирать поля 'name' и 'age'
Athlete.find({ 'sport': 'Tennis' }, 'name age', function (err, athletes) {
if (err) return handleError(err);
// 'athletes' содержит список спортсменов, соответствующих критерию.
})</code></pre>
<p>Если задать колбэк-функцию так, как показано выше, запрос будет выполнен немедленно. Однако колбэк-функция будет вызвана только после завершения поиска.</p>
<div class="note">
<p><strong>Заметка:</strong> Все колбэк-функции в Mongoose используют образец <code>callback(error, result)</code>. Если при выполнении запроса возникает ошибка, параметр <code>error</code> будет содержать объект error, а <code>result</code> будет null. При успешном запросе параметр <code>error</code> будет null, а <code>result</code> будет содержать результат запроса.</p>
</div>
<p>Если не задать колбэк-функцию, API вернет переменную типа <a href="http://mongoosejs.com/docs/api.html#query-js">Query</a>. Можно использовать объект запроса, чтобы создать и выполнить свой запрос (с колбэк-функцией) позже, при помощи метода <code>exec()</code>.</p>
<pre class="brush: js"><code>// найти всех теннисистов
var query = Athlete.find({ 'sport': 'Tennis' });
// выбрать поля 'name' и 'age'
query.select('name age');
// ограничить результат 5 элементами
query.limit(5);
// сортировать по возрасту
query.sort({ age: -1 });
// выполнить запрос позже
query.exec(function (err, athletes) {
if (err) return handleError(err);
// athletes содержит упорядоченный список из 5 теннисистов
})</code></pre>
<p>Выше условия поиска были заданы в методе <code>find()</code>. Можно также использовать функцию <code>where()</code>, кроме того, можно соединить все части в одном запросе применением оператора dot (.) вместо того, чтобы выполнять их раздельно. Фрагмент кода (см. ниже) выполняет тот же запрос, что и предыдущий фрагмент, но с дополнительным условием для возраста. </p>
<pre><code>Athlete.
find().
where('sport').equals('Tennis').
where('age').gt(17).lt(50). //Дополнительное условие
limit(5).
sort({ age: -1 }).
select('name age').
exec(callback); // callback - имя нашей колбэк-функции.</code></pre>
<p>Метод <a href="http://mongoosejs.com/docs/api.html#query_Query-find">find()</a> находит все записи, удовлетворяющие условию, но часто требуется найти только одну из таких записей. Вот методы для поиска одной записи:</p>
<ul>
<li><code><a href="http://mongoosejs.com/docs/api.html#model_Model.findById">findById()</a></code>: Находит документ по заданному идентификатору <code>id</code> (каждый документ имеет уникальный идентификатор <code>id</code>).</li>
<li><code><a href="http://mongoosejs.com/docs/api.html#query_Query-findOne">findOne()</a></code>: Находит один документ, удовлетворяющий заданному критерию.</li>
<li><code><a href="http://mongoosejs.com/docs/api.html#model_Model.findByIdAndRemove">findByIdAndRemove()</a></code>, <code><a href="http://mongoosejs.com/docs/api.html#model_Model.findByIdAndUpdate">findByIdAndUpdate()</a></code>, <code><a href="http://mongoosejs.com/docs/api.html#query_Query-findOneAndRemove">findOneAndRemove()</a></code>, <code><a href="http://mongoosejs.com/docs/api.html#query_Query-findOneAndUpdate">findOneAndUpdate()</a></code>: Находит один документ по <code>id</code> или по критерию, а затем или обновляет, или удаляет его. Эти методы весьма полезны для обновления или удаления записей.</li>
</ul>
<div class="note">
<p><strong>Заметка:</strong> Есть также метод <code><a href="http://mongoosejs.com/docs/api.html#model_Model.count">count()</a></code>, который определяет количество записей, соответствующих условию. Он полезен при выполнении подсчетов без фактического извлечения записей.</p>
</div>
<p>Запросы полезны и во многих других случаях. Дополнительная информация - в <a href="http://mongoosejs.com/docs/queries.html">Queries</a> (документация Mongoose).</p>
<h4 id="Работа_со_связанными_документами_—_загрузка">Работа со связанными документами — загрузка</h4>
<p>Один документ (экземпляр модели) может ссылаться на другой документ при помощи поля <code>ObjectId</code> схемы, или на много других документов, используя массив идентификаторов <code>ObjectIds</code>. Идентификатор соответствующей модели хранится в поле. При необходимости получить действительное содержимое связанного документа, следует использовать в запросе метод <code><a href="http://mongoosejs.com/docs/api.html#query_Query-populate">populate()</a></code>, который заменит идентификатор в запросе действительными данными.</p>
<p>Например, в следующей схеме определены авторы и рассказы. У каждого автора может быть несколько рассказов, которые представим массивом ссылок of <code>ObjectId</code>. У каждого рассказа может быть только один автор. Ссылка "ref" (выделена жирным) указывает в схеме, какая модель должна быть связана с этим полем.</p>
<pre class="brush: js"><code>var mongoose = require('mongoose')
, Schema = mongoose.Schema
var authorSchema = Schema({
name : String,
stories : [{ type: Schema.Types.ObjectId, <strong>ref</strong>: 'Story' }]
});
var storySchema = Schema({
author : { type: Schema.Types.ObjectId, <strong>ref</strong>: 'Author' },
title : String
});
var Story = mongoose.model('Story', storySchema);
var Author = mongoose.model('Author', authorSchema);</code></pre>
<p>Можно сохранить ссылки в связанном документе, используя значение идентификатора <code>_id</code>. Ниже создается автор, затем рассказ, и значение идентификатора id автора сохраняется в поле "author" рассказа.</p>
<pre class="brush: js"><code>var bob = new Author({ name: 'Bob Smith' });
bob.save(function (err) {
if (err) return handleError(err);
//автор Bob создан, создаем рассказ
var story = new Story({
title: "Bob goes sledding",
author: bob._id // присваиваем полю значение идентификатора Боба. Идентификатор создается по умолчанию!
});
story.save(function (err) {
if (err) return handleError(err);
// У Боба теперь есть рассказ!
});
});</code></pre>
<p>Теперь документ "story" ссылается на автора по идентификатору документа "author". Для получения информации об авторе применяется метод <code>populate()</code> (показано ниже).</p>
<pre class="brush: js"><code>Story
.findOne({ title: 'Bob goes sledding' })
.populate('author') //подменяет идентификатор автора информацией об авторе!
.exec(function (err, story) {
if (err) return handleError(err);
console.log('The author is %s', story.author.name);
// выводит "The author is Bob Smith"
});</code></pre>
<div class="note">
<p><strong>Заметка:</strong> Внимательные читатели заметили, что автор добавлен к рассказу, но ничего не сделано, чтобы добавить рассказ к массиву рассказов <code>stories</code> автора. Как же тогда получить список всех рассказов конкретного автора? Один из возможных вариантов - добавить автора в массив рассказов, но при этом пришлось бы хранить данные об авторах и рассказах в двух местах и поддерживать их актуальность.</p>
<p>Лучше получить <code>_id</code> нашего автора <em>author</em>, и применить <code>find()</code> для поиска этого идентификатора в поле "author" всех рассказов.</p>
<pre class="brush: js"><code>Story
.find({ author : bob._id })
.exec(function (err, stories) {
if (err) return handleError(err);
// возвращает все рассказы, у которых идентификатор Боба.
});</code>
</pre>
</div>
<p>Это почти все, что следует знать для работы со связанными данными <em>в нашем руководстве</em>. Более полную информацию можно найти в <a href="http://mongoosejs.com/docs/populate.html">Population</a> (документация Mongoose).</p>
<h3 id="Одна_схема_(модель)_-_один_файл">Одна схема (модель) - один файл</h3>
<p>Можно использовать любую структуру файлов при создании схем и моделей, однако мы настоятельно рекомендуем определять каждую схему модели в отдельном модуле (файле), экспортируя метод для создания модели. Пример приведен ниже:</p>
<pre class="brush: js"><code>// Файл: ./models/somemodel.js
//Требуется Mongoose
var mongoose = require('mongoose');
//Определяем схему
var Schema = mongoose.Schema;
var SomeModelSchema = new Schema({
a_string : String,
a_date : Date,
});
<strong>//экспортируется функция для содания класса модели "SomeModel"
module.exports = mongoose.model('SomeModel', SomeModelSchema );</strong></code></pre>
<p>You can then require and use the model immediately in other files. Below we show how you might use it to get all instances of the model.</p>
<pre class="brush: js"><code>//Создаем модель SomeModel просто вызовом модуля из файла
var SomeModel = require('../models/somemodel')
// Используем объект SomeModel (модель) для поиска всех записей в SomeModel
SomeModel.find(callback_function);</code></pre>
<h2 id="Установка_базы_данных_MongoDB">Установка базы данных MongoDB</h2>
<p>Мы уже немного понимаем, что может делать Mongoose и как следует проектировать модели. Теперь самое время начать работу над сайтом <em>LocalLibrary</em>. Самое первое, что мы должны сделать - установить базу данных MongoDb, в которой будут храниться данные нашей библиотеки.</p>
<p>В этом руководстве мы будем использовать базу данных в "песочнице" ("<a href="https://mlab.com/plans/pricing/">sandbox</a>") - бесплатный облачный сервис, предоставляемый <a href="https://mlab.com/welcome/">mLab</a>. Такая база не очень подходит для промышленных веб-сайтов, поскольку не имеет избыточности, но она очень удобна для разработки и прототипирования. Мы используем ее, так как она бесплатна, ее легко установить, и потому что mLab - популярный поставщик <em>базы данных как сервиса, </em>и это может быть разумным выбором для промышленной базы данных (на данный момент другие известные возможности включают <a href="https://www.compose.com/">Compose</a>, <a href="https://scalegrid.io/pricing.html">ScaleGrid</a> и <a href="https://www.mongodb.com/cloud/atlas">MongoDB Atlas</a>).</p>
<div class="note">
<p><strong>Заметка:</strong> При желании можно установить БД MongoDb локально, загрузив и установив <a href="https://www.mongodb.com/download-center">подходящие для вашей системы двоичные файлы</a>. В этом случае приводимые ниже инструкции не изменятся, за исключением URL базы данных, который нужно будет задать для установки соединения.</p>
</div>
<p>Первым делом надо <a href="https://mlab.com/signup/">создать аккаунт</a> на mLab (это бесплатно, требует только основных контактных данных и ознакомления с условиями обслуживания). </p>
<p>После входа в систему вы увидите главную страницу <a href="https://mlab.com/home">home</a>:</p>
<ol>
<li>Щелкните <strong>Create New</strong> в разделе <em>MongoDB Deployments</em> для создания новой БД.<img alt="" src="https://mdn.mozillademos.org/files/14446/mLabCreateNewDeployment.png" style="height: 415px; width: 1000px;"></li>
<li>Откроется экран <em>Cloud Provider Selection - раздела провайдера облака</em>.<br>
<img alt="MLab - screen for new deployment" src="https://mdn.mozillademos.org/files/15661/mLab_new_deployment_form_v2.png" style="height: 931px; width: 1297px;"><br>
<ul>
<li>Выберите план SANDBOX (Free) из раздела Plan Type (тип плана). </li>
<li>Выберите любого провайдера в разделе <em>Cloud Provider (провайдер облака)</em>. Разные провайдеры обслуживают разные регионы (показаны под выбранным типом плана).</li>
<li>Щелкните кнопку <strong>Continue</strong>.</li>
</ul>
</li>
<li>Откроется экран выбора региона <em>Select Region</em>.
<p><img alt="Select new region screen" src="https://mdn.mozillademos.org/files/15662/mLab_new_deployment_select_region_v2.png" style="height: 570px; width: 1293px;"></p>
<ul>
<li>
<p>Выберите ближайший к Вам регион и щелкните кнопку <strong>Continue</strong>.</p>
</li>
</ul>
</li>
<li>
<p>Откроется экран <em>Final Details </em>для ввода названия БД.</p>
<ul>
<li>
<p>Введите имя новой базы - <code>local_library</code> и нажмите <strong>Continue</strong>.</p>
</li>
</ul>
</li>
<li>
<p>Откроется экран подтверждения заказа <em>Order Confirmation</em>.<br>
<img alt="Order confirmation screen" src="https://mdn.mozillademos.org/files/15664/mLab_new_deployment_order_confirmation.png" style="height: 687px; width: 1290px;"></p>
<ul>
<li>
<p>Щелкните <strong>Submit Order</strong> (подтвердить заказ), чтобы создать БД.</p>
</li>
</ul>
</li>
<li>
<p>Вы вернетесь на главный (home) экран. Щелкните по вновь созданной базе, чтобы открыть экран с детальной информацией. Как видно, в БД нет коллекций (данных).<br>
<img alt="mLab - Database details screen" src="https://mdn.mozillademos.org/files/15665/mLab_new_deployment_database_details.png" style="height: 700px; width: 1398px;"><br>
<br>
На форме выше обведен URL для соединения с вашей БДthat you need to use to access your database is displayed on the form above (shown for this database circled above). Чтобы его использовать, необходимо создать пользователя БД, который позже введет этот URL.</p>
</li>
<li>Щелкните по вкладке <strong>Users</strong> и выберите кнопку <strong>Add database user </strong>(добавить пользователя БД).</li>
<li>Введите имя пользователя и пароль (дважды), затем нажмите <strong>Create </strong>(создать). Не отмечайте <em>Make read only </em>(только для чтения)!<br>
<img alt="" src="https://mdn.mozillademos.org/files/14454/mLab_database_users.png" style="height: 204px; width: 600px;"></li>
</ol>
<p>Теперь БД создана, и для доступа к ней есть URL, имя пользователя и пароль. Это должно выглядеть примерно так: <code>mongodb://your_user_namer:your_password@ds119748.mlab.com:19748/local_library</code>.</p>
<h2 id="Установка_Mongoose">Установка Mongoose</h2>
<p>Откройте окно команд и перейдите в каталог, в котором создан <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">каркас веб-сайта Local Library</a>. Введите команду install, чтобы установить Mongoose (и ее зависимости), а также добавьте ее в файл <strong>package.json</strong>, если вы еще не сделали этого ранее, при чтении примера <a href="#Installing_Mongoose_and_MongoDB">Mongoose Primer</a>.</p>
<pre class="brush: bash">npm install mongoose
</pre>
<h2 id="Подключение_к_MongoDB">Подключение к MongoDB</h2>
<p>Откройте <strong>/app.js</strong> (в корне проекта) и скопируйте приведенный ниже текст, в котором объявляется <em>объект приложения</em> <em>Express</em> (после строки <code>var app = express();</code>). Замените строку url БД ('<em>insert_your_database_url_here</em>') тем URL, который представляет вашу БД (т.е. используйте информацию, полученную от<a href="#Setting_up_the_MongoDB_database"> mLab</a>).</p>
<pre class="brush: js">//Устанавливаем соединение с mongoose
var mongoose = require('mongoose');
var mongoDB = '<em>insert_your_database_url_here</em>';//замените url!!!
mongoose.connect(mongoDB);
mongoose.Promise = global.Promise;
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'MongoDB connection error:'));</pre>
<p>Как указано ранее в примере <a href="#Connecting_to_MongoDB">Mongoose primer</a>, этот код задает соединение по умолчанию с привязкой события ошибки error (так что ошибки будут выведены в консоль). </p>
<h2 id="Определение_схемы_LocalLibrary">Определение схемы LocalLibrary</h2>
<p>Мы определим отдельный модуль для каждой модели как уже обсуждалось <a href="#One_schemamodel_per_file">выше</a>. Начнем с создания каталога для моделей в корне проекта (<strong>/models</strong>), после чего создадим отдельные файлы для каждой модели:</p>
<pre>/express-locallibrary-tutorial //the project root
<strong>/models</strong>
<strong>author.js</strong>
<strong>book.js</strong>
<strong>bookinstance.js</strong>
<strong>genre.js</strong>
</pre>
<h3 id="Модель_автора_Author">Модель автора Author</h3>
<p>Скопируйте код схемы автора <code>Author</code> (приведен ниже) в файл <strong>./models/author.js</strong> . В схеме определено, что у автора есть обязательные поля имени и фамилии типа <code>String</code> длиной не более 100 символов, и поля типа <code>Date</code> дата рождения и дата смерти.</p>
<pre class="brush: js">var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var AuthorSchema = new Schema(
{
first_name: {type: String, required: true, max: 100},
family_name: {type: String, required: true, max: 100},
date_of_birth: {type: Date},
date_of_death: {type: Date},
}
);
<strong>// Виртуальное свойство для полного имени автора
AuthorSchema
.virtual('name')
.get(function () {
return this.family_name + ', ' + this.first_name;
});</strong>
// Виртуальное свойство - URL автора
AuthorSchema
.virtual('url')
.get(function () {
return '/catalog/author/' + this._id;
});
//Export model
module.exports = mongoose.model('Author', AuthorSchema);
</pre>
<p>Мы объявим также в схеме AuthorSchema <a href="#Virtual_properties">виртуальное</a> свойство "url" , которое позволит получить абсолютный URL конкретного экземпляра модели — используем это свойство в шаблонах, если потребуется получить связь с конкретным автором.</p>
<div class="note">
<p><strong>Заметка:</strong> Объявить в схеме URL как виртуальные свойства - хорошая идея, т.к. URL отдельного элемента при необходимости изменения требует коррекции только в одном месте.<br>
Сейчас связь при помощи этого URL еще не работает, так как у нас еще нет кода, поддерживающего маршруты для экземпляров модели. Мы построим его в следующей статье!</p>
</div>
<p>В конце модуля экспортируется модель.</p>
<h3 id="Модель_книги_Book">Модель книги Book</h3>
<p>Скопируйте код схемы <code>Book</code> (приведен ниже) в файл <strong>./models/book.js</strong>. Большая часть кода подобна коду для модели автора — объявляется схема с рядом строковых полей, с виртуальным свойством URL для получения URL конкретных книг, затем модель экспортируется.</p>
<pre class="brush: js">var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var BookSchema = new Schema(
{
title: {type: String, required: true},
<strong> author: {type: Schema.ObjectId, ref: 'Author', required: true},</strong>
summary: {type: String, required: true},
isbn: {type: String, required: true},
<strong> genre: [{type: Schema.ObjectId, ref: 'Genre'}]</strong>
}
);
// Virtual for book's URL
BookSchema
.virtual('url')
.get(function () {
return '/catalog/book/' + this._id;
});
//Export model
module.exports = mongoose.model('Book', BookSchema);
</pre>
<p>Основное отличие в том, что созданы две ссылки на другие модели:</p>
<ul>
<li>author - это ссылка на единственный объект модели <code>Author</code> , обязательный элемент.</li>
<li>genre (жанр) - ссылка на массив объектов модели <code>Genre</code>. Эта модель еще не объявлена!</li>
</ul>
<h3 id="Модель_экземпляра_книги_BookInstance">Модель экземпляра книги BookInstance</h3>
<p>Наконец, скопируйте код схемы <code>BookInstance</code> (приведен ниже) в файл <strong>./models/bookinstance.js</strong>. Схема <code>BookInstance</code> представляет конкретный экземпляр книги, которую можно одолжить на время, и содержит информацию о доступности экземпляров книги, о дате возврата одолженной книги, о деталях версии или печатного экземпляра.</p>
<pre class="brush: js">var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var BookInstanceSchema = new Schema(
{
book: { type: Schema.ObjectId, ref: 'Book', required: true }, //ссылка на книгу
imprint: {type: String, required: true},
status: {type: String, required: true, <strong>enum: ['Available', 'Maintenance', 'Loaned', 'Reserved']</strong>, <strong>default: 'Maintenance'</strong>},
due_back: {type: Date, <strong>default: Date.now</strong>}
}
);
// Virtual for bookinstance's URL
BookInstanceSchema
.virtual('url')
.get(function () {
return '/catalog/bookinstance/' + this._id;
});
//Export model
module.exports = mongoose.model('BookInstance', BookInstanceSchema);</pre>
<p>В этой схеме новыми являются опции поля:</p>
<ul>
<li><code>enum</code>: Позволяет указать допустимые значения строки. В нашем случае используются, чтобы задать статус доступности книги (применение enum (перечисления) означает, что мы ходим предотвратить ошибочное написание и произвольные значения статуса)</li>
<li><code>default</code>: определяет значение статуса по умолчанию (maintenance) при создании экземпляра книги, и дату <code>due_back </code>возврата книги (<code>now,</code> сейчас). Отметьте, как используется функция Date при установке даты!</li>
</ul>
<p>Все остальное знакомо по предыдущим схемам.</p>
<h3 id="Модель_жанра_Genre_-_проверьте_себя!">Модель жанра Genre - проверьте себя!</h3>
<p>Откройте файл <strong>./models/genre.js</strong> и создайте схему для хранения жанра (категории книги, т.е. художественная или научная, романтика или военная история и т.д.).</p>
<p>Определение будет подобно другим моделям:</p>
<ul>
<li>В модели должно быть поле <code>name</code> типа <code>String</code> для указания жанра.</li>
<li>Это поле должно быть обязательным, допустимый размер - от 3 до 100 символов.</li>
<li>Объявите виртуальное (<a href="#Virtual_properties">virtual</a>) свойство с именем <code>url</code> для URL жанра.</li>
<li>Экспортируйте модель.</li>
</ul>
<h2 id="Тестирование_—_создаем_элементы_БД">Тестирование — создаем элементы БД</h2>
<p>Вот так. У нас теперь есть все модели для создания сайта!</p>
<p>Для тестирования моделей (и для создания примеров книг и других элементов, которые потребуются в следующих статьях) выполним <em>независимый </em>скрипт, который создаст элементы каждого типа:</p>
<ol>
<li>Загрузите (или создайте) файл <a href="https://raw.githubusercontent.com/hamishwillee/express-locallibrary-tutorial/master/populatedb.js">populatedb.js</a> в каталоге <em>express-locallibrary-tutorial</em> (на том же уровне, что и <code>package.json</code>).
<div class="note">
<p><strong>Заметка:</strong> Не обязательно понимать, как работает <a href="https://raw.githubusercontent.com/hamishwillee/express-locallibrary-tutorial/master/populatedb.js">populatedb.js</a>; он просто помещает некоторые данные в базу данных.</p>
</div>
</li>
<li>Введите в корне проекта команду для установки модуля <em>async, </em>который потребуется скрипту populatedb.js (роль async обсудим в следующих руководствах)
<pre class="brush: bash">npm install async</pre>
</li>
<li>Запустите скрипт из командной строки, используя node. В качестве параметра укажите URL вашей БД <em>MongoDB</em> (тот самый, которым вы ранее заменили <em>insert_your_database_url_here </em>в<em> </em>файле <code>app.js</code> ):
<pre class="brush: bash">node populatedb <your mongodb url></pre>
</li>
<li>Скрипт должен выполниться до конца, выводя в терминал создаваемые элементы.</li>
</ol>
<div class="note">
<p><strong>Совет:</strong> Откройте свою базу на <a href="https://mlab.com/home">Lab</a>. Вы сможете увидеть коллекции Book, Author, Genre, BookInstance (книги, авторы, жанры, экземпляры книг) и просмотреть содержащиеся в них документы.</p>
</div>
<h2 id="Итог">Итог</h2>
<p>В этой статье мы познакомились с БД и ОРМ (объектно-реляционными моделями) в системе Node/Express, узнали, как определяются схемы и модели Mongoose. Мы применили эти знания при проектировании и реализации моделей <code>Book</code>, <code>BookInstance</code>, <code>Author</code> и <code>Genre</code> для веб-сайта <em>LocalLibrary</em>.</p>
<p>В конце мы испытали свои модели путем создания ряда элементов (при помощи автономного скрипта). В следующей статье мы рассмотрим создание страниц, на которых будут показаны эти элементы.</p>
<h2 id="Смотрите_также">Смотрите также</h2>
<ul>
<li><a href="https://expressjs.com/en/guide/database-integration.html">Database integration</a> Интеграция БД (документация Express)</li>
<li><a href="http://mongoosejs.com/">Mongoose website</a> Веб-сайт Mongoose (документация Mongoose)</li>
<li><a href="http://mongoosejs.com/docs/guide.html">Mongoose Guide</a> Справочник Mongoose (документация Mongoose)</li>
<li><a href="http://mongoosejs.com/docs/validation.html">Validation</a> Валидация (документация Mongoose)</li>
<li><a href="http://mongoosejs.com/docs/schematypes.html">Schema Types</a> Типы в схемах (документация Mongoose)</li>
<li><a href="http://mongoosejs.com/docs/models.html">Models</a> Модели (документация Mongoose)</li>
<li><a href="http://mongoosejs.com/docs/queries.html">Queries</a> Запросы (документация Mongoose)</li>
<li><a href="http://mongoosejs.com/docs/populate.html">Population</a> Пополнение БД (документация Mongoose)</li>
</ul>
<p>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/skeleton_website", "Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs")}}</p>
<p> </p>
<h2 id="In_this_module">In this module</h2>
<ul>
<li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction">Express/Node introduction</a></li>
<li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment">Setting up a Node (Express) development environment</a></li>
<li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">Express Tutorial: The Local Library website</a></li>
<li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">Express Tutorial Part 2: Creating a skeleton website</a></li>
<li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">Express Tutorial Part 3: Using a Database (with Mongoose)</a></li>
<li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/routes">Express Tutorial Part 4: Routes and controllers</a></li>
<li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express Tutorial Part 5: Displaying library data</a></li>
<li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms">Express Tutorial Part 6: Working with forms</a></li>
<li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/deployment">Express Tutorial Part 7: Deploying to production</a></li>
</ul>
<p> </p>
|