How can I find results from an ObjectId field? - mongodb

If I have a schema like this:
var Word = new Schema({
name: { type: String, required: true },
language: { type: ObjectId, ref: "Language", required: true }
});
and I try to query it like this
var language = "5078547df6b753bc06000003";
word.find({ language: language }, function (err, words) {
console.log(words);
});
I get an empty result set ([]). I know there exists a word with this language, because if I remove the {language: language} I get this object:
[{
"_id": "5079fd7b6df57b1b64cbf25d",
"name": "dog",
"language": "5078547df6b753bc06000003",
}]
I've tried using
var language = mongoose.Types.ObjectId("5078547df6b753bc06000003");
word.find({ language: language }, //etc.
but it still returns an empty result set.
Why can't mongoose find the word with this language id?

You've declared the language field as an ObjectId in the schema, but in the MongoDB document it's actually a string. So in your find call, Mongoose is going to cast the language variable to an ObjectId and then the query will return any documents where language is an ObjectId with that value (and won't find any because it's not an ObjectId in the database).
You either need to update the document so that language contains an ObjectId instead of a string or change your schema so that language is defined as type: String. If you want to treat language as a reference as you're doing in the schema then you'll need to do the former.

Related

Can database references in mongodb reference two databases?

I am using MongoDB via mongooose. Can database references reference two databases at the same time?
field_name: {
type: mongoose.Schema.Types.ObjectId,
ref: 'collectionA',// can I reference collectionA and collectionB too?
required: true,
},
See code above.
The field, field_name, can be an objectId from collectionA or collectionB. How can I reflect that in my mongoose schema?
I guess you are looking for mongoose dynamic reference via refPath.
const someSchema = new Schema({
field_name: {
type: Schema.Types.ObjectId,
required: true,
refPath: 'onCollection'
},
onCollection: {
type: String,
required: true,
enum: ['collectionA', 'collectionB']
}
});
In this case, Instead of a hardcoded model name in ref, refPath means Mongoose
will look at the onCollection property to find the right model.
For example if we have this document:
{
field_name: some_id,
onCollection: 'collectionA'
}
Collection.find().populate('field_name') will populate the field from collectionA. And if the onCollection field was valued with collectionB, it would have populated it from collectionB.
This scenario only works if you want to reference one collection at a time, but the collection is dynamic.
If you need to reference both collections at the same time, there is no mongoose schema design to support array of references as far as I know.
You can just ignore ref in your schema, and pass in the value of ref when you want to populate:
populate({
path: 'field_name',
model: 'collectionA'
})
Then you can have multiple populates. Same applies for $lookup.

mongoDB dynamically created fields

I've dynamically created some fields (they aren't in my model). I found my collection with COLLECTION.findById.....
If I want to get paths, it doesn't show dynamically created paths.
console.log(result);
{ lv: { name: 'List vlastníctva', exist: false },
projekt: { name: 'Projektová dokumentácia', exist: false },
uzemne_rozhodnutie: { name: 'Územné rozhodnutie', exist: true },
complete: false,
sctcst: { name: 'ščťčšť', exist: false },
ctyzy: { name: 'čťýžý', exist: false },
cssczz: { name: 'čšščžž', exist: false },
__v: 0,
_id: 59b916633d77c1cf2958f007 }
console.log(Object.keys(result.schema.paths));
[ 'lv.name',
'lv.exist',
'projekt.name',
'projekt.exist',
'uzemne_rozhodnutie.name',
'uzemne_rozhodnutie.exist',
'complete',
'_id',
'__v' ]
console.log(result.lv);
{ name: 'List vlastníctva', exist: true }
console.log(result.cssczz);
undefined
in Object.keys aren't dynamically created fields.
command line + Robo 3T
I can't solve it. In DB it looks that it is saved correctly.
Thanks
Try this:
result.get('cssczz', mongoose.Schema.Types.Object)
If you're trying to retrieve field which is not defined in schema you need to tell what it's expected type is.
Alternatively, this could also work:
var resultAsObject = result.toObject();
console.log(resultAsObject.csszz);
Each time you're trying to retrieve some value from mongoose object it checks schema definition to make sure value will have correct type. To retrieve property which is not in schema you either need to tell mongoose what it's type is, or convert mongoose object to plain JS object first.
Docs:
http://mongoosejs.com/docs/api.html#document_Document-get
http://mongoosejs.com/docs/api.html#document_Document-toObject

What is wrong with this mongo $or query

This query works perfectly
{
$or:[{author:this.userId} , {somethingelse:true} ]
}
But when I try:
{
$or:[{author:this.userId} , {sharedwith[this.userId]:true} ]
}
I receive the message
Errors prevented startup:
While processing files with ecmascript (for target os.linux.x86_64): server/main.js:113:43: Unexpected token, expected
, (113:43)
=> Your application has errors. Waiting for file change.
And thats where the comma , in the $or statement is
Help
I guess that you are trying to retrieve all documents for which the current user is the author, or which have been shared with him/her? And therefore that you have structured your documents with a sharedWith field which is a hash map of userId as keys and boolean as value?
Document structure:
{
author: string,
sharedWith: {
<userId1>: boolean
// <userId2>…
}
}
In that case, your MongoDB query should use the dot notation to specify the value of a nested field within sharedWith field:
{
$or: [{
author: string
}, {
"sharedWith.<userId>": boolean
}]
}
To easily create the query with the interpolated value of <userId>, you can use a computed key in your object (ES6 syntax):
{
$or:[{
author: this.userId
} , {
// The query computed key must be in square brackets.
// Specify the field child key using the dot notation within your query.
["sharedwith." + this.userId]: true
}]
}
Or with good old ES5 syntax, similarly to #MichelFloyd's answer:
var query = {
$or: [{
author: this.userId
}]
};
var newCondition = {};
newCondition["sharedWith." + this.userId] = true;
query.$or.push(newCondition);
Note: the above described document structure could conveniently replace the sharedWith hash map by an array (since having a false value for the boolean could simply be replaced by removing the corresponding userId from the array):
{
author: string,
sharedWith: [
<userId1>
// <userId2>…
]
}
In which case the query would simply become:
{
$or:[{
author: this.userId
} , {
// In MongoDB query, the below selector matches either:
// - documents where `sharedWith` value is a string equal to
// the value of `this.userId`, or
// - documents where `sharedWith` value is an array which contains
// an element with the same value as `this.userId`.
sharedwith: this.userId
}]
}
Try building the query as a variable before running it.
let query = { $or: [{ author: this.userId }]};
const sw = sharedwith[this.userId];
query.$or.push({sw: true});
MyCollection.find(query);

Does using dbref do anything more than just storing an `id`

My Mongoose schema:
// set up the schema
var CategorySubSchema = new Schema({
name: { type: String },
_category_main : { type: String, ref: 'CategoryMain' }
},
And my controller code:
CategorySub.create({
name : req.body.name,
_category_main : req.body.category_main
}, function(err, data){
An entry in my db:
{
"_id": "54dd163434d78ae58f6b1a69",
"name": "Snacks",
"_category_main": "54dcf4a71dfecb4d86ddcb87",
"__v": 0
},
So I used an underscore, because I was following an example. Does this mean anything to the database or is it just convention for references?
Also, instead of passing the entire JSON object in the request - req.body.category_main, why not just pass and id and change my schema to this?:
var CategorySubSchema = new Schema({
name: { type: String },
category_main_id : { type: String }
},
In short, Yes.
The below schema definition is an example of Manual references.
var CategorySubSchema = new Schema({
name: { type: String },
category_main_id : { type: String }
}
where,
you save the _id field of one document in another document as a
reference. Then your application can run a second query to return the
related data. These references are simple and sufficient for most use
cases.
In this case, we need to write explicit application code to fetch the referred document and resolve the reference. Since the driver that we use wouldn't know about the collection in which the referred document is present nor the database in which the referred document is present.
When you define the schema as below, this is an example of storing the details of the referred document .(Database references)
var CategorySubSchema = new Schema({
name: { type: String },
_category_main : { type: String, ref: 'CategoryMain' }
}
They include the name of the collection, and in some cases the
database name, in addition to the value from the _id field.
These details allow various drivers to resolve the references by themselves, since the name of the collection and the database(optional) of the referred document would be contained in the document itself, rather than we writing explicit application code to resolve the references.
So I used an underscore, because I was following an example. Does this mean anything to the database or is it just convention for
references?
Using underscore in the _id field is a valid naming convention, but mongoDb doesn't explicitly mention about the naming convention of other fields which are used to resolve references. You could just use any other field name as long as it conforms to this.

Mongoose OR operator for schema definitions

Does Mongoose support, or is there an available package that supports multiple "options" for the embedded schemas in an array?
For example, the things property can contain only one of two schemas:
new Schema({
things: [{
requiredProp: String,
otherProp: Number
}, {
otherOption: Number
}]
});
In other words, I do not want to just allow anything (AKA Schema.Types.Mixed) to be stored in this property, but only these two possible definitions.
Or, do schema design recommendations exist to avoid this problem?
You should only define one dict in the array type of the schema, and then set if they are required or not with the mongoose schema types logic. Use pre save if you want to do more logic to assure that either one of the fields have been set, like this:
var MySchema = new Schema({
things: [{
requiredProp: {type: String, required: true},
otherProp: Number,
otherOption: Number,
}]
});
MySchema.pre('save', function(next) {
if (!this.otherProp && !this.otherOption) {
next(new Error('Both otherProp and otherOption can\'t be null'))
} else {
next()
}
})
Opon saving the object it will return an error if neither otherProp nor otherOption has been set.