NodeJS Mongoose mapping of data - node.js

I'm making a hire platform for cars. Now i used mongoose, setup my models and found out i can't populate a reference that's too deep.
I got 2 models: Cars and Types and Customers.
I'm trying to receive everything from my Customers like placed orders.
The response i'm getting currently is the one below.
"slug": "myslug",
"note": "",
"isReturned": false,
"includedKM": "0",
"price": "0",
"car": {
"type": "_id"
}
I got the car to populate but i can't seem to get the type to populate. I tried things like .populate('car.type') but with no result. I looked this up and apparently it's too deep to populate so i came up with a solution to bypass this.
const customer = req.customer;
Order.find({'customer': customer.slug}).populate('car').then(function(orders){
Car.find(orders).populate('type').then(function(types){
return res.json(types);
});
});
The types i'm getting now are the populated types from the cars.
Now i want to swap the content of the car.type to this result and generate my own populated response.
Anyone any clue how to do this? It's probably pretty easy since i have the type_id in both. Just need to map the data from one to the other.
Thanks in advance!

Since mongoose 4 you can populate documents across multiple levels.
Maybe this post will help you.
mongoose - populate sub-schema sub-array

Found a solution to this problem. Apparently there is a plugin for mongoose (deep-populate) that allows you to populate 2nd grade child references

Send value of car type as parameter or get it inside request in a variable value.
After that you can do it like this:
Order.findOneAndUpdate({customer: customer.slug}, {$set: {car.type: value}}, function (err, customer) {
if (err) {
return res.status(500).json({
title: 'An error occured',
error: err
});
}
res.status(201).json({
message: 'Success',
obj: customer
});
});

Related

Mongoose, Sort Results by Reference Attributes

I will attempt to address the problem as clearly as possible.
Let's say I have two collections, Col_A and Col_B.
Col_A has an attribute, attribute C, that references our other collection, Col_B, via ObjectID.
image for visual reference
I want to query for documents from Col_A and sort by attribute E in Col_B.
A practical example, if the above is too broad, would be Users and Listings on a site like eBay. For this example, let's say Col_A is "Listings", Col_B is "Users", attribute C is "seller", and attribute E is "rating".
Query: grab all listings matching some criteria, sorted by sellers with the highest rating
That would mean "grab all documents from Listings matching some given criteria, and sort the results by the rating attribute for each Users document the listings reference.
How would one go about doing this? I understand how to apply basic sorting logic via mongoose, such as:
Listings.find({"attribute A" : "something"}).sort({"some_field": "uhh"}).exec(function(err, docs) { ... });
, but can I nest in the attributes for referenced documents and sort by those? Do I need to somehow apply queries within the sort parameters or something? Is Mongoose capable of something like this? I looked at Mongoose's populate functionality, but I can't get anything working, and apparently there's a bug that discourages folks from actually using populated documents for sorting.
Github Issue
I apologize if this question has been answered already. I've been digging through the documentation, SO, and various other sites, but I can't get anything going. Thanks in advance!
You could use populate method to populate seller and the query would return an array like this:
[
{
title: 'Listing #1',
seller: {
name: 'John',
rating: 100
}
}
{
title: 'Listing #2',
seller: {
name: 'John',
rating: 20
}
}
]
then you can sort the data like this:
Listings.find({"attribute A" : "something"}).populate('seller').exec(function(err, docs) {
docs.sort(function compare(a, b){
let c = 0;
if (a.seller.rating > b.seller.rating) {
c = 1;
} else if (b.seller.rating > a.seller.rating) {
c = -1;
}
return c;
});
// return or response with docs
});
It might not be what you are looking but I tried :)
Mongo or Mongoose will not do this kind of sort since populate method makes 2 queries and Mongo doesn't know there's any relation between them.

Mongoose.js - I can't get the ID of a Mongoose.Model instance (MongoDB Document)

I'm doing a simple Mongoose.Model instance find() which returns a a random document from an employee collection. The thing is, I can't seem to be able to get it's ID. When I see the document contents with MongoLab, I get the following:
{
"_id": {
"$oid": "554cef2fb6bb56cc421dd47c"
},
"name": "Bill Gates",
"role": "Software Engineer Associate",
"age": 21,
"hardware": [],
"__v": 0
}
But when I try to get the ID with commands on the employee instance that came back like employee._doc._id I get the following:
Object {_bsontype: "ObjectID", id: "ULï/¶»VÌBÔ|"}
What's going on? I've debugged my code and tried to find that _id property but it's just nowhere to be found.
You should probably include more code snippets in your questions so that it's a bit easier to identify where your flaw lies, but here is an example of how your code should look.
Your model should be exported from the schema:
var Employee = Storage.model('Employee', EmployeeSchema);
And your query should look something like this:
Employee.findOne(<--your query here-->).exec(function(err, employee) {
if (err) {
//always handle errors here :)
}
if (employee) {
//You can access the objectId of the employee like
console.log(employee._id);
}
});

Saving subdocuments with mongoose

I have this:
exports.deleteSlide = function(data,callback){
customers.findOne(data.query,{'files.$':1},function(err,data2){
if(data2){
console.log(data2.files[0]);
data2.files[0].slides.splice((data.slide-1),1);
data2.files[0].markModified('slides');
data2.save(function(err,product,numberAffected){
if(numberAffected==1){
console.log("manifest saved");
var back={success:true};
console.log(product.files[0]);
callback(back);
return;
}
});
}
});
}
I get the "manifest saved" message and a callback with success being true.
When I do the console.log when I first find the data, and compare it with the console.log after I save the data, it looks like what I expect. I don't get any errors.
However, when I look at the database after running this code, it looks like nothing was ever changed. The element that I should have deleted, still appears?
What's wrong here?
EDIT:
For my query, I do {'name':'some string','files.name':'some string'}, and if the object is found, I get an array of files with one object in it.
I guess this is a subdoc.
I've looked around and it says the rules for saving subdocs are different than saving the entire collection, or rather, the subdocs are only applied when the root object is saved.
I've been going around this by grabbing the entire root object, then I do loops to find the actual subdoc I that I want, and after I manipulate that, I save the whole object.
Can I avoid doing this?
I'd probably just switch to using native drivers for this query as it is much simpler. (For that matter, I recently dropped mongoose on my primary project and am happy with the speed improvements.)
You can find documentation on getting access to the native collection elsewhere.
Following advice here:
https://stackoverflow.com/a/4588909/68567
customersNative.update(data.query, {$unset : {"slides.1" : 1 }}, function(err){
if(err) { return callback(err); }
customersNative.findAndModify(data.query, [],
{$pull: {'slides' : null } }, {safe: true, 'new' : true}, function(err, updated) {
//'updated' has new object
} );
});

REST API (Mongo DB) Arguments Error 500

I have a Problem in with my REST API. It will not correctly communicate with mongodb. Node.js throw following Exception:
Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters
My Manager looks like follows:
app.get('/users/:id', function(req, res) {
userProvider.fetchUserById(req.params.id, function(error, user) {
if (user == null) {
res.send(error, 404);
} else {
res.send(user);
}
});
});
My Provider looks like follows:
this.fetchUserById = function(id, cb) {
this.db.collection(usersTable, function(error, users) {
if (error) {
cb(error, null);
} else {
users.findOne({
_id:users.db.bson_serializer.ObjectID.createFromHexString(id)
}, function(error, result) {
cb(error, result);
});
}
});
};
My Mongoobject looks like follows:
{
"title": "title",
"givenname": "Vorname",
"lastname": "Nachname",
"name": "Vorname Nachname",
"username": "Benutzername",
"password": "Kennwort",
"email": "kuerzel#emailadresse.de",
"phone": "0000 - 000000",
"fax": "000000 - 000000",
"lastlogin": "15.11.2013",
"createdate": "15.11.2013",
"changedate": "15.11.2013",
"state": "1",
"_id": ObjectId("5283fbf56e3adb01f1000001")
}
When I now send a GET: /users/1 the error will thrown.
I´am new in Node.js. :-)
If your human readable IDs are unique, you can use them.
Just insert the documents with your nice id:
db.users.insert({ _id: myVeryNiceUniqueId, title: '...' }, ...);
As alternative, you could add an additional 'external id' to the document and create an unique index. But you will get many, many problems when you want to scale (sharding). Who is going to create the unique IDs? You will have something not-so-human-readable, I promise.
Edit
In Mysql I have start the Ids from 5XXXX and used them. How can I realize this with mongodb
You should ask another question or search answers. It is not a trivial task and you have to take concurrency into account. Maybe the concept of findAndModify or "Upsert" could help you. One of the most important things is that you need a way of atomic updates for the ID-generator. In one project in past I used Redis for this, but I'd always use MongoDB IDs since then. You should know that MongoDB ObjectIDs usually are generated by the Client-Driver. For REST services this is ideal: save document to DB and return status 201 Created, url=.../user/98357348957.
btw. should I generate the OrderIDs in Javascript by checking the highest value of an external id in the collection?
As said, you can get into trouble with usage of duplicate IDs when your server is in high contention. Try to find a solution with findAndModify or cache a block of IDs and use them in one process. I probably wouldn't search max and increment but use a special collection for ID-management.
What is here best practice?
Don't know. Using an ID-Generator. Depends on your enviroment (single process, single machine). If it is a simple setup, you'll find simple solutions. The setup for Twitter is not simple and they have developed a solution called twitter / snowflake. If you really want very nice, short IDs, but you have more than one process/machine, Redis might be want you want. And if the ID doesn't have to be contigous (without small gaps), you could draw a bunch of new IDs (e.g 100) and cache them for future consumption per process.
Can I hash the objectID to an 5 digit number?
No, there is no guarantee that this number is unique.

Mongoose: Using addToSet with ObjectIds Results in Orphan Id

I am having a rather interesting problem using mongoDB's $addToSet to an array full of ObjectIds.
In my mongoose schema ("Happening"), I declare an array of ObjecIds called "expected", to be used by .populate().
expected: [{type: Schema.Types.ObjectId, ref: "User" }]
... which works nicely everywhere I use it. So far so good.
I then attempt to update the Happening.expected array using $addToSet as outlined here:
http://docs.mongodb.org/manual/reference/operator/addToSet/
like so:
app.get("/happening/yamobethere/:id", ensureLoggedIn("/login"),
function (req, res) {
// userId is the mongo ObjectId of the user record
var userId = req.session.user.id,
eventId = req.params.id;
models.Happening.update(
{_id: eventId}, {
$addToSet: {expected: userId}
},
function(err, updated){
if (err) {
res.json({"error": err});
}
res.json({"updated": updated});
});
});
... which always yields:
{updated: 1}
Now the docs lead me to expect the actual userId that I passed in, so the "1" is a bit odd. I expected it to be a fail, and in light of the weirdness that happens next, it appears to be a mongodb error of some sort percolating it's way back to me as results.
The weirdness is, when I check my database, I see that indeed a new ObjectId has been added: just not the one I passed in.
"expected" : [
ObjectId("51cb18623ade2b9f1e000004"),
ObjectId("51cdb7c12f0e58bdb3000001")
],
becomes
"expected" : [
ObjectId("51cb18623ade2b9f1e000004"),
ObjectId("51cdb7c12f0e58bdb3000001"),
ObjectId("51cdb80e09612bfab3000002")
],
The new ObjectId does not appear in any of my collections. It appears to be an orphan, but I'm a mongo noob, so I may be full of compost on this.
I did attempt to cast the userId as an ObjectId:
$addToSet: {expected: mongoose.Types.ObjectId.fromString(userId)}
but that changed nothing, and really should not be necessary, since the schema should handle it.
I'd really rather not resort to downloading the entire object, appending the value to the "expected" array, then sending the whole schmear back for an update.
Any help appreciated, folks. Thanks!
Update:
A colleague suggested the following technique:
var addMe = {$addToSet: {expected: userId}};
models.Happening.findByIdAndUpdate(eventId, addMe, function(err, me) {
if (err) {
return json(err);
}
res.json(200, me);
});
... which is a bit of an improvement, since it actually returns an object for me to inspect. Unfortunately, it also results in orphaned ObjecIds appearing in the array, rather than the existing userId value I specified.
Thanks again!
It appears that my passport strategy is returning the ObjectID of the rejected attempted creation of a new user in the db via data from oauth. So, the code is fine, my data is garbage.
Never trust anything, and be prepared to look like a boob. :-)
Thanks for the clarification on my return values JohnnyHK.

Resources