Query embedded collection in a sails project with mongodb - node.js

I'm using sails-mongo in my project and i need to execute one query in an embedded collection.
My data are something like the following:
{
"_id" : ObjectId("53906c6254f36df504e99b8f"),
"title" : "my post"
"comments" : [
{
"author" : "foo",
"comment" : "foo comment"
},
{
"author" : "bar",
"comment" : "bar comment"
}
],
"createdAt" : ISODate("2014-06-05T13:10:58.365Z"),
"updatedAt" : ISODate("2014-06-05T13:10:58.365Z")
}
for example, i need to extract the comments of author foo.
Apparently sails does not support this feature yet, so i was considering using the object db of mongodb-native to make this kind of query.
As sails-mongo uses mongodb-native, can i have access to db object in my sails project? Or i will need build a new connection using mongodb-native?
If anyone has a better idea I'd be grateful. Thanks

If all you need to do is access the embedded comments, Waterline should work fine. Just do a normal find or findOne, and the comments should be accessible on the returned objects.
If you need to query the comments, e.g. to find Posts with comments by a certain author, you can access the underlying mongodb-native collection using the .native() method of your Sails model class:
Post.native(function(err, collection) {
if (err) {
// handle error getting mongo collection
}
collection.find({'comments.author':'foo'}).toArray(function(err, results) {
// Do something with results
});
});

Related

Mongoose unique validation error even though unique constraint is not there

var schema = new Schema({
_id: Schema.ObjectId,
email: {type: String, required: true}
});
Previously email field was unique (with unique constraint)
Now I removed unique constraint, then also it's giving unique validation error. I have restarted mongo then also it's throwing error.
Any Idea?
When you remove the unique constraint in the schema definition, you were supposed to manually remove it from the database as well. You can do this either in mongo shell by using the dropIndex() method or in your application using the native node.js driver's dropIndex() method of the collection.
You may need to confirm this first by checking the indexes currently in the database; you will most probably find the email unique index created when you populated the collection after defining the schema in Mongoose.
You can issue the command in mongo shell, supposing you have a collection named users in your database named test:
> db.users.getIndexes()
which will show the current indexes, you may see in your console an array like the following:
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test.users"
},
{
"v" : 1,
"unique" : true,
"key" : {
"email" : 1
},
"name" : "email_1",
"ns" : "test.users",
"background" : true,
"safe" : null
}
]
For a solution in your application code, supposing you have a Mongoose model called User that has the defined schema above, you can call the getIndexes() native node.js driver's collection method:
User.collection.getIndexes(function (err, results) {
// Handle errors
});
In mongo shell, you can then go ahead and remove the email index with the dropIndex() method :
> db.users.dropIndex("email_1")
In your application code, you can also issue the command via the Mongoose model for the collection, and call the native node.js driver's dropIndex() method of the collection:
User.collection.dropIndex("email_1", function (err, results) {
// Handle errors
});
You can also follow the below steps.
Delete users collection from database.
Save user model(Schema) file and test.

MongoDB not able to $unset particular field; automatic _id field at the end

I am writing code to manage a collection of merchants through a website using the plain MongoDB driver for NodeJS. My trouble occurs when the merchant is trying to confirm their account. It is expected for the code to use $unset to erase the value from database therefore activating the merchant's account. Instead, the query returns true, but the confirm field remains untouched. Here is an example of one of the problematic merchants in the database as printed by db.merchants.find({}) (with added line breaks for better readability):
{ "email" : "test#test.com", "password" : "<hashed password is here>",
"salt" : "<salt is here>", "access" : "merchant",
"created" : 1444000000000, "confirm" : "bKEQD0aiV8aXIQPY4CUxCm7KGSZ2pFJM",
"name" : "Test user",
"contact" : { "name" : "Test User",
"address" : "123 Cedar Ln N",
"city" : "Some City", "state" : "AA",
"zip" : "00000", "country" : "USA",
"phone" : "5555555555" },
"_id" : ObjectId("56133111d30ce5d4736323cb") }
AssumEffectively, this is the JS used in order to insert the item to the database:
db.collection("merchants").insert({
name: "Test user",
email: "test#test.com",
.... more fields....,
confirm: "bKEQD0aiV8aXIQPY4CUxCm7KGSZ2pFJM"
}, someCallback);
...and here is how I attempt to unset the "confirm" field:
db.collection("merchants").findOne({email: "test#test.com"}, function(err, user) {
if(err) return handleError();
if(!user) return sendFailureMessageToClient();
if(client.confirm == user.confirm) {
db.collection("merchants").update({_id: user.id},
{$unset: {confirm: true}}, someCallback);
}
}
I am certain that the update command above is actually being executed.
One of the things I think is interesting about the data above is that the _id field is at the end... is that supposed to normally happen? Would that mess up the $unset for the update command? If that is not the issue, then what is?
Thanks in advance for help.
EDIT: I showed why I am using a separate findOne request and then update.
_id is a special BSON object of ObjectId. Using the id string is not enough. You probably want to use _id as well:
db.collection("merchants").update({_id: ObjectId(user._id)},
You may also want to use findAndModify instead.
You missed the underscore in user.id, should be user._id. Right now, it's trying to fetch an user with undefined _id.
If it's not that, you should do your update on the email field, it'll work just fine (assuming it's unique).
The _id at the end of the document doesn't matter, there is no strict order in JSON/BSON fields. The only moment an order is maintened is inside arrays.

MongoDB _id is convert to ObjectID automatically, but sometime it is not.

I am using a wrapper called mongoskin to access mongoDB. mongoskin is a simple wrapper around mongoDB javascript api.
But when I write to mongoDB, sometimes _id is converted to ObjectID, sometime is it not. The different behavior causes many problem when I have to compare _id. For example:
The following documents in company collection, "creator" is not converted to ObjectID, but item in "clients" is converted to ObjectID automatically.
> db.company.find()
{ "_id" : ObjectId("53d4b452f5b25900005cb998"), "name" : "Default Company Co.", "clients" : [ ObjectId("53d4b452f5b25900005cb999"), ObjectId("53d4b452f5b25900005cb99a") ] }
{ "_id" : ObjectId("53d4b452f5b25900005cb999"), "name" : "client company for 777 - updated", "creator" : "53d4b452f5b25900005cb998", "ssn" : "12-123-1234" }
This is the nodejs code I used to assign _id for "creator"
clientCompany.creator = req.session.user.company_id;
This is the nodejs code I used to assign _id for "clients"
var updateObj = {$addToSet: {clients:resultClient._id} };
// update creator company.clients
creatorCompany.update(updateObj, function(err, result) { ...}
When I console.log "req.session.user.company_id" and "resultClient._id", they both looks like a string type. How come one end up as ObjectID in MongoDB? If there is an auto conversion, how do I make this behavior consistent?
Thanks!
I'm guessing resultClient is the result of a query and req.session.user.company_id a string from your web application? In that case you need to create an ObjectId from the string:
clientCompany.creator = mongoskin.ObjectID(req.session.user.company_id);

Show message owner

Help me with understanding mongodb, please.
have three collections: threads, messages and users.
thread
{ "title" : "1212", "message" : "12121", "user_id" : "50ffdfa42437e00223000001", "date" : ISODate("2013-04-11T19:48:36.878Z"), "_id" : ObjectId("51671394e5b854b042000003") }
message
{ "message" : "text", "image" : null, "thread_id" : "51671394e5b854b042000003", "user_id" : "516d08a7772d141766000001", "date" : ISODate("2013-04-17T15:58:07.021Z"), "_id" : ObjectId("516ec68fb91b762476000001") }
user
{ "user" : "admin", "date" : ISODate("2013-04-16T08:15:35.497Z"), "status" : 1, "_id" : ObjectId("516d08a7772d141766000001") }
How can I display all messages for current thread and get user name (for comment) from users collection?
this code get only messages without user name
exports.getMessages = function(id, skip, callback) {
var skip = parseInt(skip);
messages.find({thread_id: id}).sort({date: 1}).skip(skip).limit(20).toArray(
function(e, res) {
if (e) {
callback(e)}
else callback(null, res)
});
};
Node.js and mongo native
Generally, Mongo uses embedded documents or references to maintain relationships. Here is a link from the mongo docs worth reading.
What you are currently doing is storing a manual reference to the user collection within your message collection. Mongo manual references require additional queries in order to get that referenced data. In this case, using a reference based relationship will work, but it would force the N+1 query problem. Meaning you will have to make an addition query for every message you wish to display plus the original query for messages. References are explained in further detail here. One solution would be to incorporate DBRefs, which would require language specific driver support.
Another alternative would be use embedded documents. In this case you would store the related user object embedded within the messages object. Here is another link to the mongo docs with a great example. In this case, you would make a single query, which will return all of the messages, with each related user object embedded inside. Although embedded documents encourage duplicate data, in many cases they provide performance benefits. All of this information is explained in the mongo docs and can be read in detail to further understand the data modeling of mongo.
Additionally, the mongoose library is pretty awesome and has a populate function which is helpful for references.

Adding objects to a deep array in MongoDB

I've just started building a little application using MongoDB and can't seem to find any examples where I can add objects to a deep array that I can then find on an individual basis.
Let me illustrate by the following set of steps I take as well as the code I've written.
I create a simple object in MongoDB like so:
testing = { name: "s1", children: [] };
db.data.save(testing);
When I query it everything looks nice and simple still:
db.data.find();
Which outputs:
{
"_id" : ObjectId("4f36121082b4c129cfce3901"),
"name" : "s1",
"children" : [ ]
}
However, after I update the "children" array by "pushing" an object into it, I get into all sorts of problems.
First the update command that I run:
db.data.update({ name:"s1" },{
$push: {
children: { name:"r1" }
}
});
Then when I query the DB:
db.data.find({
children: { name: "r1" }
});
Results in:
{
"_id" : ObjectId("4f36121082b4c129cfce3901"),
"children" : [ { "name" : "r1" } ],
"name" : "s1"
}
Which doesn't make any sense to me, since I would have expected the following:
{
"name": "r1"
}
Is there a better way of inserting data into MongoDB so that when I run queries I extract individual objects rather than the entire tree? Or perhaps a better way of writing the "find" query?
By default mongodb find retrieve all the fields(like * from in sql). You can extract the particular field by specifying the field name
.
db.data.find({ "children.name": "r1" },'children.name');
Why would you expect ot to return only part of a document? It returns the whole document unless you tell it which fields you want to explicitly include or exclude. See http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields

Resources