validate subDocuments in a dictionary in Mongoose - mongodb

I have a userTasteSchema, with a dictionary field favorites composed by
favoriteSchema objects. When I save or update a userTaste, I want to validate that the elements of the dictionary are valid favorite objects.
Is it possible?
thank you
var userTasteSchema = new Schema(
{
favorites : {type: { /* dictionary of favorites */ }, default:{} }
});
var favoriteSchema = new Schema(
{
name : {type:{String}}
});

You have to change your model declaration. According to the docs your code should look like:
var userTasteSchema = new Schema(
{
favorites : [ favoriteSchema ]
});
var favoriteSchema = new Schema(
{
name : {type:String}
});
And that's pretty much it. When you save your parent document, UserTaste, your children validations are run as well. Here is the reference
Validation is asynchronously recursive; when you call Model#save,
sub-document validation is executed as well. If an error occurs, your
Model#save callback receives it

Related

How to populate with a second model if first one is empty

I've three models,
modelA, modelB and ModelC
ModelC's data is here
{
"_id" : ObjectId("586e1661d9903c6027a3b47c"),
"RefModel" : "modelA",
"RefId" : ObjectId("57f37f18517f72bc09ee7632")
},
{
"_id" : ObjectId("586e1661d9903c6027a3b47c"),
"RefModel" : "modelB",
"RefId" : ObjectId("57f37f18517f72bc09ee7698")
},
Howto write a populate query for ModelC, to populate with RefId .
it should populate with modelA or modelB which RefModel refers.
I've tried with
ModelC.find({})
.populate({ path: 'RefId', model: 'modelA' })
.populate({ path: 'RefId', model: 'modelB' })
But taking only the last model.
modelC schema.
new mongoose.Schema({
RefModel: String,
RefId:{ type: Schema.ObjectId}});
I could do it with aggregate, but preferring populate.
Fields' names in your database and schema are very confusing, let me explain on more clear example.
Suppose you have 3 models: User, Article and Comment. Article and Comment belongs only to single User. User can have multiple comments and articles (As you shown in your example).
(More efficient and reccommending way). Store comments and articles ids in User model like:
comments: [{ id: '..', ref: Comment }],
articles: [{ id: '..', ref: Article }]
and populate your document:
User.find({})
.populate('comments')
.populate('articles');
Store User id in Comment and Article models like
user: { id: '...', ref: User }
and use mongoose-reverse-populate module for population, for example for comments model:
var reversePopulate = require('mongoose-reverse-populate');
User.find().exec(function(err, users) {
var opts = {
modelArray: users,
storeWhere: "comments",
arrayPop: true,
mongooseModel: Comments,
idField: "user"
}
reversePopulate(opts, function(err, popUsers) {
// populated users will be populated with comments under .comments property
});
});
Also you dont need to keep RefModel in your database, only in schema.
Here, you can try and populate it inside the callback of find itself.
Try this:
ModelC.find({}).exec(function(err,modelC){
//find returns an array of Object, you will have to populate each Object individually.
var modelCObjects =[];
modelC.forEach(function(tempModelC){
//populate via the Model
ModelC.populate(tempModelC,{path : tempModelC.refId , model :tempModelC.refModel},function(err2,newModelC){
//handle newModelC however you want
//Push it into array of modelC Objects
modelCObjects.push(newModelC);
});
//OR
//Populate directly on the resultObject <-im not so sure about this
tempModelC.populate({path : tempModelC.refId , model :tempModelC.refModel},function(err2,newModelC){
//Push it into array of modelC Objects
modelCObjects.push(newModelC);
})
});
//here you have modelCObjects -> Array of populated modelC Objects
});
Please make sure to handle the errors.

Mongodb, getting the id of the newly pushed embedded object

I have embedded comments in a posts model. I am using mongoosejs. After pushing a new comment in a post, I want to access the id of the newly added embedded comment. Not sure how to get it.
Here is how the code looks like.
var post = Post.findById(postId,function(err,post){
if(err){console.log(err);self.res.send(500,err)}
post.comments.push(comment);
post.save(function(err,story){
if(err){console.log(err);self.res.send(500,err)}
self.res.send(comment);
})
});
In the above code, the id of the comment is not returned. Note there is a _id field which is created in the db.
The schema looks like
var CommentSchema = new Schema({
...
})
var PostSchema = new Schema({
...
comments:[CommentSchema],
...
});
A document's _id value is actually assigned by the client, not the server. So the _id of the new comment is available right after you call:
post.comments.push(comment);
The embedded doc pushed to post.comments will have its _id assigned as it's added, so you can pull it from there:
console.log('_id assigned is: %s', post.comments[post.comments.length-1]._id);
You can manually generate the _id then you don't have to worry about pulling it back out later.
var mongoose = require('mongoose');
var myId = mongoose.Types.ObjectId();
// then set the _id key manually in your object
_id: myId
// or
myObject[_id] = myId
// then you can use it wherever
_id field is generated at client side, you can get the id of the embedded document by comment.id
sample
> var CommentSchema = new Schema({
text:{type:String}
})
> var CommentSchema = new mongoose.Schema({
text:{type:String}
})
> var Story = db.model('story',StorySchema)
> var Comment = db.model('comment',CommentSchema)
> s= new Story({title:1111})
{ title: '1111', _id: 5093c6523f0446990e000003, comments: [] }
> c= new Comment({text:'hi'})
{ text: 'hi', _id: 5093c65e3f0446990e000004 }
> s.comments.push(c)
> s.save()
verify in mongo db shell
> db.stories.findOne()
{
"title" : "1111",
"_id" : ObjectId("5093c6523f0446990e000003"),
"comments" : [
{
"_id" : ObjectId("5093c65e3f0446990e000004"),
"text" : "hi"
}
],
"__v" : 0
}

Nested document insert into MongoDB with C#

I am trying to insert nested documents in to a MongoDB using C#. I have a collection called categories. In that collection there must exist documents with 2 array, one named categories and one named standards. Inside those arrays must exist new documents with their own ID's that also contain arrays of the same names listed above. Below is what I have so far but I am unsure how to proceed. If you look at the code what I want to do is add the "namingConventions" document nested under the categories array in the categories document however namingConventions must have a unique ID also.
At this point I am not sure I have done any of this the best way possible so I am open to any and all advice on this entire thing.
namespace ClassLibrary1
{
using MongoDB.Bson;
using MongoDB.Driver;
public class Class1
{
public void test()
{
string connectionString = "mongodb://localhost";
MongoServer server = MongoServer.Create(connectionString);
MongoDatabase standards = server.GetDatabase("Standards");
MongoCollection<BsonDocument> categories = standards.GetCollection<BsonDocument>("catagories");
BsonDocument[] batch = {
new BsonDocument { { "categories", new BsonArray {} },
{ "standards", new BsonArray { } } },
new BsonDocument { { "catagories", new BsonArray { } },
{ "standards", new BsonArray { } } },
};
categories.InsertBatch(batch);
((BsonArray)batch[0]["categories"]).Add(batch[1]);
categories.Save(batch[0]);
}
}
}
For clarity this is what I need:
What I am doing is building a coding standards site. The company wants all the standards stored in MongoDB in a tree. Everything must have a unique ID so that on top of being queried as a tree it can be queried by itself also. An example could be:
/* 0 */
{
"_id" : ObjectId("4fb39795b74861183c713807"),
"catagories" : [],
"standards" : []
}
/* 1 */
{
"_id" : ObjectId("4fb39795b74861183c713806"),
"categories" : [{
"_id" : ObjectId("4fb39795b74861183c713807"),
"catagories" : [],
"standards" : []
}],
"standards" : []
}
Now I have written code to make this happen but the issue seems to be that when I add object "0" to the categories array in object "1" it is not making a reference but instead copying it. This will not due because if changes are made they will be made to the original object "0" so they will not be pushed to the copy being made in the categories array, at least that is what is happening to me. I hope this clears up what I am looking for.
So, based on your latest comment, it seems as though this is the actual structure you are looking for:
{
_id: ObjectId(),
name: "NamingConventions",
categories: [
{
id: ObjectId(),
name: "Namespaces",
standards: [
{
id: ObjectId(),
name: "TitleCased",
description: "Namespaces must be Title Cased."
},
{
id: ObjectId().
name: "NoAbbreviations",
description: "Namespaces must not use abbreviations."
}
]
},
{
id: ObjectId(),
name: "Variables",
standards: [
{
id: ObjectId(),
name: "CamelCased",
description: "variables must be camel cased."
}
]
}
]
}
Assuming this is correct, then the below is how you would insert one of these:
var collection = db.GetCollection("some collection name");
var root = new BsonDocument();
root.Add("name", "NamingConventions");
var rootCategories = new BsonArray();
rootCategories.Add(new BsonDocument
{
{ "id": ObjectId.GenerateNewId() },
{ "name", "Namespaces" },
{ "standards", new BsonArray() }
});
root.Add("categories", rootCategories);
//etc...
collection.Save(root);
Hope that helps, if not, I give up :).
So, I guess I'm confused by what you are asking. If you just want to store the namingConventions documents inside the array, you don't need a collection for them. Instead, just add them to the bson array and store them.
var categoriesCollection = db.GetCollection<BsonDocument>("categories");
var category = new BsonDocument();
var namingConventions = new BsonArray();
namingConventions.Add(new BsonDocument("convention1", "value"));
category.Add("naming_conventions", namingConventions);
categoriesCollection.Insert(category);
This will create a new document for a category, create an array in it called naming_conventions with a single document in it with an element called "convention1" and a value of "value".
I also am not quite sure what you are trying to accomplish. Perhaps if you posted some sample documents in JSON format we could show you the C# code to write documents that match that.
Alternatively, if you wish to discuss your schema, that could also be better done in the context of JSON rather than C#, and once a schema has been settled on then we can discuss how to write documents to that schema in C#.
One thing that didn't sound right in your original description was the statement "in that collection must exist 2 arrays". A collection can only contain documents, not arrays. The documents themselves can contain arrays if you want.
var filter = Builders<CollectionDefination>.Filter.Where(r => r._id== id);
var data = Builders<CollectionDefination>.Update.Push(f=>
f.categories,categoriesObject);
await _dbService.collection.UpdateOneAsync( filter,data);
Note: Make sure embedded document type [Categories] is array.

Is it possible to extract only embedded documents from a Model in Mongoose?

I'd like to run a query on a Model, but only return embedded documents where the query matches. Consider the following...
var EventSchema = new mongoose.Schema({
typ : { type: String },
meta : { type: String }
});
var DaySchema = new mongoose.Schema({
uid: mongoose.Schema.ObjectId,
events: [EventSchema],
dateR: { type: Date, 'default': Date.now }
});
function getem() {
DayModel.find({events.typ : 'magic'}, function(err, days) {
// magic. ideally this would return a list of events rather then days
});
}
That find operation will return a list of DayModel documents. But what I'd really like is a list of EventSchemas alone. Is this possible?
It's not possible to fetch the Event objects directly, but you can restrict which fields your query returns like this:
DayModel.find({events.typ : 'magic'}, ['events'], function(err, days) {
...
});
You will still need to loop through the results to extract the actual embedded fields from the documents returned by the query, however.

Mongoose/MongoDB - Saving a record to overwrite current data:

So I have a object in the DB that is basically blog posts, there is a array of ObjectID's in there that reference the Categories collection.
So
Posts = {
title: String,
Content: String,
Categories: [{
type: ObjectID,
ref: 'Categories'
}]
I can create the posts fine, problem comes when I try to update them:
post.title = 'hi';
post.content = 'content';
post.categories = ['44523452525','4e1342413421342'];
post.save(function(){});
For some reason it will ADD those 2 categories instead of wiping the categories array and inserting those.
How do I get it to remove those and insert new ones?
I tried to reproduce this behavior but I couldn't - here's the code sample I ran:
var mongoose = require('mongoose')
var Schema = mongoose.Schema
mongoose.connect('mongodb://localhost/testjs');
PostSchema = new Schema({
title:String,
content:String,
cats:[]
});
var Post = mongoose.model('Post', PostSchema);
var mypost = new Post()
mypost.title = "x"
mypost.content = "y"
mypost.cats = ['a','b','c']
mypost.save(
function(err){
mypost.cats = ['d','e']
mypost.save()
}
);
After the second call to save() the array contains only what it was set to ("d","e"). Can you try it and see if you get the same result? Maybe it's related to mongoose version, or something.