Query Mongoose Schema by ObjectId - node.js

Going to need your help again, hopefully for this project, the answer to what I'm having here will be the last. I've seen it's a fairly commonly asked question, but I've tried the tips on another Stack Overflow post, and one by the Google Group, but the solutions haven't worked for me.
My code being a little bit like:
mongoose = require('mongoose');
Schema = mongoose.Schema;
mongoose.connect(MONGO_SERVER);
ObjectId = Schema.ObjectId;
var RacesSchema = new Schema({
venue_id : { type: mongoose.Schema.ObjectId, ref: 'Venues' },
racetype : String
});
var races = mongoose.model('Races', RacesSchema );
function test() {
var MyObjectId = require('mongoose').Types.ObjectId;
queryVenue = new MyObjectId("50e3dcdbf30375180c078f64");
races.find({venue_id: queryVenue, racetype:'Test'})
.exec(function(err,data) {
}
test();
But I get no results, which I know there is.
Many thanks in advance!
UPDATE
Have minimized the above code sample, this test works if I query the string value on its own, just querying for an ObjectId is where it fails, and I know it exists.
JSON UPDATE
Here is what I am looking for:
{
"_id" : ObjectId("50e3dcddf30375180c078f85"),
"venue_id" : "50e3dcdbf30375180c078f64",
"racetype" : "A"
}
And all of a sudden, I believe my answer has become clear to me. Is it simply because the venue_id is actually a string? And if so, can I keep my mongoose schema the way it is, and do casting on the query at the point of doing the find of being a string? Or should I change the way these values are being stored (from a separate .net application I developed) to be inserted as ObjectId's?
Currently right now for another query, the current mongoose schema and the way the database is [actually set up], using populate() works quite well to fill the results of the referenced table (venue_id) with the way this model is currently set up, only difference being on the above query, I don't specify the venue_id...
Thanks.

Right, the problem is happening because the data type of venue_id in the schema (ObjectId) doesn't match the one in the doc (String). So the find is looking for ObjectId values but doesn't find a match because it's a string in the doc.
The right fix for this is to write a little program to update the venue_id values in your docs to be ObjectIds instead of strings and then your query will work. That will also shrink the size of those fields from 24 bytes to 12.

Related

Using mongoose, how to add object propety to an object stored in mongodb that is not included in the schema?

I want to be able to add a new object property to an object in a document stored in a mongodb database where the model schema does not include that property. I've seen all sorts of examples on google and none of them seem to work so I want to ask this question myself and get some direct answers.
For example take this scheme
const MapSchema = new mongoose.Schema({
name:{
type: String
}
})
const ListMap = mongoose.model("listmap", MapSchema);
Now I want to add a new property to it using its id
model.ListMap.findByIdAndUpdate(_id, {newprop:"whatever");
model.ListMap.findByIdAndUpdate(_id, {name:"test");
Using those two commands, I can add name:"test" to it but I can not add newprop:"whatever" to it. What I want to do is be able to add a new property without having to declare it in the schema first. I know this seems like it has been asked before but I've googled it all and I don't believe anyone has answered it. They either didn't understand the question or their code doesn't actually work.
Also bonus question, why does mongodb always add an s to collection names? like the above would show up in collection "listmaps", assuming I used .create() to add the first object.
For your first question, you can not add a property to your schema without declaring it first.
you can define a generic property like this:
const MapSchema = new mongoose.Schema({
property:{
title: String,
value: String
}
})
const ListMap = mongoose.model("listmap", MapSchema);
and for your second question you can refer to this answer:
Why does mongoose always add an s to the end of my collection name
-------SOLVED-------------
I am adding this here for others because it was extremely difficult for me to google this. This answer is from another stackexchange link
How to add a new key:value pair to existing sub object of a mongoDB document
based on this, this is how to do what I proposed earlier
const commentMapSchema = new mongoose.Schema({
List:{}
})
const CommentsMap = mongoose.model("commentmap", commentMapSchema);
const update = {$set: {["List."+"test2"] : "data2"}};
CommentsMap.findByIdAndUpdate(id, update);
First you need to declare the property in the schema. This property can hold more properties exactly like how it works with javascript objects. Then use the update method with mongoose with the query that combines the property name with a new property via dot notation. However when adding a property this way, the key must be a string. This works exactly like it does in javascript. You can just create a new name and it'll add to the object properties.
I hope this helps anyone else who landed here.

Dynamic Mongoose Field Type

I am developing an app where a user could store his model on a database using mongoDB and mongoose. Taken from mongoose tutorial the type of the field has to be defined. For example here we have to define that the name is a string.
const personSchema = new mongoose.Schema({
name: String
});
const Person = mongoose.model('Person', personSchema);
Is there any way to make it dynamic to user's input. I want to create a form where a user will enter a field name and select one of the field types that Mongoose offers [String,Number,Date etc], but I cannot figure any way to implement it. To be honest I don't know even if this is a good approach. An alternative would be to pass everything as a String and serialise the input in order to store it. I want to achieve something like that:
const {fieldName,fieldType} = userInput;
const customSchema = new mongoose.Schema({
fieldName: fieldType
});
const CustomModel = mongoose.model('CustomSchema', customSchema);
Is this possible or should I implement another approach? An alternative would be to pass everything as a String and serialise the input in order to store it.
Thank you in advance!
If I understand you correctly it should work like that:
User defines the model to store
Schema is created using the data provided by the user
User can pass the data to store using the previously created model which will validate the user's input later
In fact, I'm working on a project that has the same functionality. Here is how we did it.
A user sends the model and we store it as a string since we need to have the ability to create the model once again.
When the user passes new data to store using the created model we get the string from mongo and parse it to create the schema. This operation is relatively easy (but depends on what you want to achieve as it can get tricky if you want to have some advanced validation) as you have to just create an object with correct values from mongoose. Something like this for every field that the user has defined.
export const fieldConverter = ({name, type}) => {
switch (type) {
case 'String':
return { [name]: String };
case 'Number':
return { [name]: Number };
...
}
When you have your object ready then you can create a model out of it.
The line with accessing your model from mongoose.models is important as the mongoose will cache the model and throw an error if you try to create it once again.
const DatasetModel =
mongoose.models["your-model-name"] ??
mongoose.model("your-model-name", new mongoose.Schema(schema));
Now when you have the model the rest is just like with the normally created one.
This approach worked for us so I'm adding this as inspiration maybe it will help you. If you have any specific questions about the implementation feel free to ask I will be happy to help.
There is also a Mixed type in mongoose if you don't need the validation later. You can check it here: https://mongoosejs.com/docs/schematypes.html#mixed
You can use Schema.Types.Mixed, An "anything goes" SchemaType. Mongoose will not do any casting on mixed paths.
let customSchema = new Schema({custom: Schema.Types.Mixed})
Read more about it here
After some research I figure at that mongoose type can also be strings. For example
const personSchema = new mongoose.Schema({
name: "String"
});
const Person = mongoose.model('Person', personSchema);
Mongoose will handle it

Converting string to ObjectId is failing in mongoose 4.6.0

I am trying to convert a string to ObjectId using
var body={};
var objId="57b40595866fdab90268321e";
body.id=mongoose.Types.ObjectId(objId);
myModel.collection.insert(body,function(err,data){
//causing err;
});
the above code is working fine when mongoose 4.4.16 is used, but if i update my mongoose to latest version(4.6.0) then problem occurs.
Err
object [
{
"_bsontype":"ObjectID",
"id:{"0":87,"1":180,"2":5,"3":235,"4":134,"5":111,"6":218,"7":185,"8":2,"9":104,"10":50,"11":111}
}
]
is not a valid ObjectId
The right way to insert new document is-
var newDocument = new myModel({
_id: mongoose.Types.ObjectId("57b40595866fdab90268321e")
});
newDocument.save();
In you case-
It stops working because the differences between versions of mongoose and mongo native drivers.
although, you are able to perform this by the example above, or, if you still want to use insert, you can use the myModel.insertMany (by passing object instead of array)
look here
http://mongoosejs.com/docs/api.html#model_Model.insertMany
I don't have the time to spike it, but if I remember correctly id is a simple string and _id is the ObjectId, i.e. either
body.id="57b40595866fdab90268321e"
or
body._id=mongoose.Types.ObjectId("57b40595866fdab90268321e");
That said, does it have to be that specific id? If not, you can use new myModel() and an id will be automatically created.

Unable to get the value of particular key in mongoose if that key is not present in Schema

UserEventsInfo = new mongoose.Schema({
name: String,
username: String,
event_movie:[String],
event_tour:[String],
event_restaurant:[String],
event_lifetimeevents:[String]
},{strict : false});
I am able to insert new key-value pair other than defined in the schema
but when I try to read the value of that key. I can't. I am using the following code.
UserEventsDetails.find({username:username},function(err,docs){
if(!docs.length)
{
res.send('datanotavailable');
}
else{
res.send(docs[0][eventname]);
}
});
Here eventname is a variable.
When I add that key in the schema it returns the value i.e. work's fine.
Otherwise it is not returning any value.
Looks like there was an issue submitted like this to mongoose. Here is there response:
The benefit we see in a schemaless database is the ability for our data model to evolve as fast as our features require it, without a linear impact on performance and slower deployment cycles with needless migrations.
If you don't want your data to be normalized and validated prior to saving, then you don't need a tool like Mongoose, you can use the driver directly.
After a little digging there is a way to do this, but you will need to have a field with type Schema.Types.Mixed. So it would look like this:
var schema = new Schema({
mixed: Schema.Types.Mixed,
});
var Thing = mongoose.model('Thing', schema);
var m = new Thing;
m.mixed = { any: { thing: 'i want' } };
m.save(callback);
To do a find on a mixed this SO question answers that.
****EDIT
forgot to link the documentation of mixed types

Add a new attribute to existing json object in node.js

I have an object like this
==================records=========={ Id: 5114a3c21203e0d811000088,
userId: 'test',
sUserId: test,
userName: 'test',
url: 'test',
Title: 'test'
}
I need to add a new field Name : 'test' to the above record, I tried giving records.Name = name, it didn't work.
Helps please
Thanks,
Prats
I am assuming you are trying to add a property to a returned Mongoose Document to reuse it somewhere else. Documents returned by Mongoose are not JSON objects directly, you'll need to convert them to an object to add properties to them. The following should work:
//... record is a mongoose Document
var r = record.toObject();
r.Name = 'test';
console.log("Record ",r);
Those finding this problem, OP mentioned in a comment below the original question that the solution for this problem is:
records.set('Name', 'test')
This adds a new attribute called Name having value test.
Just use,
var convertedJSON = JSON.parse(JSON.stringify(mongooseReturnedDocument);
and Then,
convertedJSON.newProperty = 'Hello!'
'Hello!' can be anything, a number, a object or JSON Object Literal.
Cheers! :)
I experienced a similar problem, and hope that my hours of existential frustration help others in the same situation. My inclination was to believe that documents returned via Mongoose are read-only. This is actually not true.
However, you cannot assign a property to your document that is not also in your Schema.
So, unless your schema has the following:
{
Name: {String}
}
you will be constantly frustrated by trying to assign Name to your document.
Now, there are workarounds in the answers above that also worked for me, but they do not get to the root of the problem:
myDocument.toObject();
JSON.parse(JSON.stringify(myDocument);
These will work, but in my opinion they just hide the problem. The real issue is that Mongoose is smarter than we realized and is enforcing your schema, as it should.
You could also use the lean() method, e.g. const users = await Users.find().lean().exec()
From the mongoose documentation:
Documents returned from queries with the lean option enabled are plain
javascript objects, not MongooseDocuments. They have no save method,
getters/setters or other Mongoose magic applied
My variant.
If schema defined with {strict:false} you can simply add new property by
recorcd.newProp = 'newvalue';
If schema defined with {strict:true} you can either convert Mongoose object to object as mentioned earlier or use command
record.set('newProp','newValue',{strict:false})
See http://mongoosejs.com/docs/api.html#document_Document-schema
If you have loaded this object into records, both records.Name = "test" or records['Name'] = "test" will work. You have either not loaded the object correctly, or are inserting an undefined value into it.
To test: add console.log(records.userId), this should print 'test' to the terminal.
Also add console.log(name). If you get ReferenceError: name is not defined, you obviously cannot do: records.Name = name

Resources