Mongoose find in referenced document properties - node.js

I'm going nuts on a query to find a match based on referenced document properties.
I've defined my schema like this:
mongoose.model('Route', new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
}));
mongoose.model('Match', new mongoose.Schema({
route: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Route'
}
}));
So when I'm searching for a route from a specific user in the Match model, I'd do something like (also tried without the '_id' property):
match.find({'route.user._id': '53a821577a24cbb86cd290d0'}, function(err, docs){});
But unfortunately it doesn't give me any results. I've also tried to populate the model:
match.find({'route.user._id': '53a821577a24cbb86cd290d0'}).populate('route').exec(function(err, docs){});
But this doesn't make a difference. The solutions I'm aware of (but don't think they're the neatest):
Querying all the results and iterate through them, filtering by code
Saving the nested documents as an array (so not a reference) inside the route model
Anyone suggestions? Many thanks in advance!
Related questions (but not a working solution offered):
Mongodb/Mongoose in Node.js. Finding by id of the nested document
Use mongoose to find by a nested documents properties

I'm going nuts on a query to find a match based on nested document properties
You don't have nested documents. You have references, which are just IDs that point to documents that reside in other collections. This is your basic disconnect. If you really had nested documents, your query would match.
You are encountering the "mongodb does not do joins" situation. Each MongoDB query can search the documents within one and only one collection. Your "match" model points to a route, and the route points to a user, but the match does not directly know about the user, so your schema does not support the query you want to do. You can search the "routes" collection first and use the result of that query to find the corresponding "match" document, or you can de-normalize your schema and store both the routeId and userId directly on the match document, in which case you could then use a single query.
Based on your question title, it seems like you want nested documents but you are defining them in mongoose as references instead of real nested schemas. Use full nested schemas and fix your data, then your queries should start matching.

Related

How to Mongoose populate a subdoc to another subdoc in the same root doc

What would be the best practice with Mongoose to populate a nested document with data from a different document which is part of the same root document?
Let's say you have this schema:
{
name:"template-1",
formulas: [
{_id:"randomId-345",name:"random-formula-name-1"},
{_id:"randomId-234",name:"random-formula-name-2"},
{_id:"randomId-234",name:"random-formula-name-2"},
],
widgets:[{
{_id:"randomId-1",name:"random-widget-name-1", formulaDoc:"randomId-234"}
{_id:"randomId-2",name:"random-widget-name-2", formulaDoc:"randomId-345"}
}]
}
(This is a simplified example, the real schema is quite larger)
Is it possible to use the .populate function to populate the id of formulaDoc with the corresponding document which is in the formulas array?
I'm aware of using ref's. But as far as I this only works for multiple collection. I can't find how to achieve the same with a setup like this.

query filter for mongodb using node js

I have two collection one is questions which stores _id, title, options, result, feedback and second is a child in the child I have store question_id, score. And I have filter the _id from questions collection. I don't know how I do this, Is it possible can we set the query for this. so that next time when I find the question from questions collection it sends filtered question. Means Return only that question from questions collection which id not same as the second collection child qustion_id.
This is my first collection where I have store questions, _id title option result feedback
_id:{type:String},
title:{type:String, required:true},
options:{type:Array, required:true},
result:{type:Array, required:true},
feedback:{type:String}
This is my Second collection where I have store attempted question_id and score
quiz:[
{
questionId:{
type:mongoose.Schema.Types.ObjectId,
ref: 'Question',
index: true
},
score:{type:Number},
time:{type:String}
}
]
This is not exactly I just create an example
var query = {}
firstcollection.find($and[{_id:},{secondcollection question_id:}]},function(err, data){
so that filter data means filter _id will store in data.
and I send this data to the frontend
res.send(data);
});
The main problem is conceptual, you are trying to work with mongodb, which is document store in RDBMS style. Under the community pressure Mondo added some minimal join functionality in latest version, but it doesn't make it relational DB.
There is no good way to perform such query. The idea behind document store is simple - you do have collection of documents and you query this collection, and only this collection. All link between collections are "virtual" and only provided by code logic, with no support from DB engine.
So all you can do with mongo is: query first collection for ids (with appropriate projection, to fetch ids only), store answer to some array and then perform second query to other collection using this array.

Embed document without knowing _id in Mongoose/Mongodb

I know there are no "joins" in MongoDB. I'm attempting to link a large number of documents to the 40,000+ locations in my locations collection.
My locations collection has custom (read: not under my control) identifiers for locations and their corresponding lat/lng coordinates.
var Locations = new Schema({
location_id: String,
loc: { //lng, lat: as per mongodb documents
type: [Number],
index: '2d'
}
});
There are several collections that have a field referencing this custom identifier to match latitude and longitude.
var MyCollection = new Schema({
location: String,
otherFields: Strings...
});
I'm a little lost on how to best go about this. A lot of posts suggest linking via Schema, but I've only seen that with an Schema.Types.ObjectId. This seems impractical for me because the data I'm importing only have the custom identifier.
Could I perhaps add another field into MyCollection and find the correct _id of the location to link to while I'm uploading data. If so, can someone point me in the right direction for accomplishing this.
Map reduce could be used somehow perhaps? I'm still a bit novice with Mongo.
Tried
I did try loading up the entirety of the location data into a JS object then checking that object against the return object from my other query, injecting the matching location data into my return object. This works but is unbearably slow.
First, just for the record: MongoDb will still generate an _id property for each object you store.
1. "[...], if the mongod receives a document to insert that does not contain an _id field, mongod will add the _id field that holds an ObjectId. [...]"
Source
You wrote that location_id is not under your control. And you want to use location_id because the other collections are using it also? So you don't want to break the standard in your project which is good.
As I see, you already have the location property in MyCollection and can store the location_id there.
As far as I know, you have to write your own linking methods now. You have to store the location_id in MyCollection and load the Location if you want to access if via MyCollection by
Locations.find({location_id: <the_location_id>})
But maybe your main problem is that you cannot find the Locations you are looking for in a reliable time?
I don't know your criteria for finding Locations for MyCollection is. If it is proximity of coordinates then you can reduce the amount of locations to check by filtering out the ones you really don't need to check. Then you don't have to check all the 40.000 Locations, but maybe just 100? In the following I assume it is the proximity.
Do you have lat, lon in both collections (Locations, MyCollection) ?
If so, you can define a query which gets locations around your MyCollection object (square). Then you will receive a smaller amount of Locations from MongoDb. Now you can apply your more complex check which checks if they really belong to your MyCollection-object.
something like this:
Locations.find({lat: {$gt: <x>-a, $lt: <x>+a}, lon: {$gt: <y>-b, $lt: <y>+b}}, function(locations){ ... });
I hope it helps.

How to make a subdocument have two parent documents in mongodb?

I'm using mongoose with Node.js for an application. I have a Document class, which has a Review subdocument. I also have a User class.
I want the user to be able to see all the reviews they've done, while I also want the Document to be able to easily get all of its reviews. Searching through all the documents and all their reviews to find ones matching a user seems horribly ineffecient. So, how do I allow the Review to be owned by both a Document and a User?
If this is impossible, how else can I efficiently have two documents know about one subdocument.
If you don't want to deal with consistency issues I don't think there's any way except for normalization to assign two parents for a document. Your issue is a common one for social networks, when developers have to deal with friends, followers, etc. Usually the best solution depends on what queries you are gonna run, what data is volatile and what is not and how many children a document might have. Usually it turns out to be a balance between embedding and referencing. Here's what I would do if I were you:
Let's assume Documents usually have 0-5 Reviews. Which is a few, so we might consider embed Reviews into Documents. Also we would often need to display reviews every time a Document is queried, this is one more reason for embedding. Now we need a way to query all reviews by a User efficiently. Assume we don't run this query as often as the first one but still it is important. Let's also assume that when we query for User's Reviews we just want to display Review titles as links to Review page or even Document page as probably it's hard to read a review without seeing the actual Document. So the best way here would be to store { document_id, review_id, reviewTitle }. ReviewTitle should not be volatile in this case. So now when you have a User object, you can easily query for reviews. Using document_id you will filter out most documents and it will be super fast. Then you can get required Reviews either on the client side or by using MapReduce to turn Reviews into separate list of documents.
This example contains many assumption so it might not be exactly what you need by my goal was to show you the most important things to consider while designing your collections and the logic you should follow. So just to sum up, consider QUERIES, HOW VOLATILE SOME DATA IS and HOW MANY CHILDREN A DOCUMENT IS GONNA HAVE, and find a balance between embeding and referencing
Hope it helps!
This is an old question, but here is a solution that, I think, works well:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var DocumentSchema = new Schema({
title: String,
...
});
mongoose.model('Document', DocumentSchema);
var UserSchema = new Schema({
name: String,
...
});
mongoose.model('User', UserSchema);
var ReviewSchema = new Schema({
created: {
type: Date,
default: Date.now
},
document: {
type: Schema.ObjectId,
ref: 'Document'
},
reviewer: {
type: Schema.ObjectId,
ref: 'User'
},
title: String,
body: String
});
Then, you could efficiently query the Reviews to get all reviews for a User, or for a Document.
Hope this helps someone.

Indexing array of references using mongodb multikey indexing method in mongoose

My schema has an array of ObjectIds which are refs to another schema. What i want is to index this array entry using multikey indexing method of mongodb.
So that given an ObjectId of some document in the ProductCat Collection, i can list all the documents in my current collection which has the given ObjectId in the _pro_cat field.
I am confused about the exact way in which i should declare the field, in mongoose schema declaration, Here is what i am trying:
_pro_cat: { type: [mongoose.Schema.Types.ObjectId], ref: 'ProductCat', index: true }
_pro_cat: [{ type: mongoose.Schema.Types.ObjectId, ref: 'ProductCat', index: true }]
The name of this collection is Seller. I am making the relation, by using the reference in the schema and the actual field value is an array, each of type ObjectId, which will be the ObjectIds of the documents in the other Collection (ProductCat in this case). If i index this field i.e.. _pro_cat, then given an ObjectId of a document in ProductCat Collection, i would be able to find all the documents in the Seller collection which has given ObjectId in its array field _pro_cat.
I think i may have to call a separate index function. But i thought that this is a field level indexing so it may not be needed.
I suspect the later is only for sub-docs and not for refs. Would greatly appreciate, if anyone could shed some light on it. Thanks.
Side Question: [RE-SOLVED]
In the mongodb documentation, it says that multikey indexing is automatic. multikey indexing. Does this mean that if that field is indexed, using the above methods, then mongo will recognize that the field is an array and use multikey indexing. OR does it mean that all the fields which are of array type will get indexed, without the need to explicitly telling it to index.
Thanks a lot.
related
Multikey Indexing means that every item in an array-field is indexed if you tell mongo to index the field.
It does not mean that all array fields are indexed automatically without the need to explicitely tell mongo to index the field.

Resources