How can I get the _id from a single document using mongoose? - mongodb

I want to query for a document with a given condition and have it return the _id for that document. Here is what I tried, but it doesn't work:
User.find(
{phone: phone},
null,
{},
function (err, data) {
user_id = data._id;
}
);
Basically, I'm trying to query the Users collection for a user/document with a certain phone number, and then have it return the _id for that user. What am I doing wrong?

If you're trying to find only one document you need to use findOne:
User.findOne({phone : phone}, function(err, data) {
if (err) return console.error(err);
user_id = data._id;
});
If multiple documents satisfy the query, this method returns the first document according to the natural order which reflects the order of documents on the disk.
If you want to get multiple documents you need to use find and your data parameter will then contain all users that matches your criteria.

Related

MongoDb: Insert Document in collection only if collection has no newer document since point in time

I want to depict the following use case using MongoDb:
I want to read from a collection and memorize that particular point in time.
When writing the next time to that collection, I want to not be able to write a new document, if another document has been added to that collection in between.
Using a timestamp property on the documents would be ok.
Is this possible?
One trick is use findAndModify
Assume at the time of reading, your most recent timestamp on a document is oldTimestamp:
db.collection.findAndModify({
query: {timestamp: {$gt: oldTimestamp}},
new: true, // Return modified / inserted document
upsert: true, // Update if match found, insert otherwise
update: {
$setOnInsert: {..your document...}
}
})
This will not insert your document if another document is inserted between your read and write operation.
However, this won't let you know that the document is inserted or not directly.
You should compare returned document with your proposed document to find that out.
In case using nodejs driver, the correct pattern should be:
collection.findAndModify(criteria[, sort[, update[, options]]], callback)
According to the example, our query should be:
db.collection('test').findAndModify(
{timestamp: {$gt: oldTimestamp}}, // query, timestamp is a property of your document, often set as the created time
[['timestamp','desc']], // sort order
{$setOnInsert: {..your document..}}, // replacement, replaces only the field "hi"
{
new: true,
upsert: true
}, // options
function(err, object) {
if (err){
console.warn(err.message); // returns error if no matching object found
}else{
console.dir(object);
}
});
});
This can be achieved, using a timestamp property in every document. You can take a look at the Mongoose Pre Save path validation hook . Using this hook, you can write something like this.
YourSchema.path('timestamp').validate(function(value, done) {
this.model(YourSchemaModelName).count({ timestamp: {$gt : value} }, function(err, count) {
if (err) {
return done(err);
}
// if count exists and not zero hence document is found with greater timestamp value
done(!count);
});
}, 'Greater timestamp already exists');
Sounds like you'll need to do some sort of optimistic locking at the collection level. I understand you are writing new documents but never updating existing ones in this collection?
You could add an index on the timestamp field, and your application would need to track the latest version of this value. Then, before attempting a new write you could lookup the latest value from the collection with a query like
db.collection.find({}, {timestamp: 1, _id:0}).sort({timestamp:-1}).limit(1)
which would project just the maximum timestamp value using a covered query which is pretty efficient.
From that point on, it's up to your application logic to handle the 'conflict'.

Mongoose document update- selected objects in array - with $in operator

My schema is as below.
Customer: {
orders: [{
...
status: 'Pending'
...
}]
}
I have a list of order ids whose which are delivered to the customer. So I need to change the Status to closed. This is my simple requirement. I have my below code, which updates the status 'closed' only for the first order id in the OrderIdList.
orderIdList = ["54899c0cbdde6b281e9aaa22","54899c28bdde6b281e9aaa23","54899c2abdde6b281e9aaa24","54899c2cbdde6b281e9aaa25"]
Customer.update({
'orders._id': {
$in: orderIdList
}
}, {
'$set': {
'orders.$.status': 'Closed'
}
}, {
multi: true,
upsert: true
}, function(err, rows) {
console.log("error");
console.log(err);
console.log("rows updated");
console.log(rows);
});
Only one row is updated. for the orderId "54899c0cbdde6b281e9aaa22"
Why is it not updating for all the order Ids. Please suggest me the solution.
Thanks in advance.
which updates the status 'closed' only for the first order id in the
OrderIdList.
Yes, that is how the positional operator($) works. It updates only the first item in an array that has matched the condition in the query.
From the docs,
the positional $ operator acts as a placeholder for the first element
that matches the query document.
multi: true is applicable for parent documents and not for embedded documents. If this parameter is set to true, all the documents that have an order matching the required Id, will be updated. But the positional operator will update only the first sub document that matched the query in all these documents.
To update all the matching orders for all the matching document, currently it is not possible in a single mongo update query. You need to perform the logic in the application code.
One way of doing it in the application code:
Match all the customer documents which contain an order that we are
looking for.
For each customer document identify the orders that we need to
update.
Modify the order.
Perform an update based on the Customer document _id.
Sample Code:
var orderIdList = ['54899c0cbdde6b281e9aaa22'];
Customer.find({"orders._id":{$in:orderIdList }},function(err,resp){
resp.forEach(function(doc){
var orders = doc.orders;
orders.forEach(function(order){
if(orderIdList.indexOf(order._id.toString()) != -1){
order.status = 'closed';
Customer.update({"_id":doc._id},{$set:{"orders":orders}},function(err,aff,raw){
console.log("Updated: "+aff);
});}})})})

How can I find all documents where a field contains a particular string?

I'm new to mongodb and mongoose and I'm trying to figure out how to do a query that let's me find all records/documents where a field contains a particular string. This is for use with a search feature. For example, if I have a "users" collection, then I want to find all users whose firstname begins with, say, the string "Jo". Like this:
var Users = require('./models/users');
Users.find({ firstname : contains the string 'Jo' }, function(err, user) {
if (err) {
return done(err);
} else {
return res.json({
firstname: user.firstname,
});
}
});
I'm not sure what to put in place of "contains the string 'Jo'" so that this query will work. Basically, it should return records/documents such as "Joe", "John", "Joyce", etc.
UPDATE===========================================
Here is the updated code, but it returns all records, instead of only returning the ones that match "str":
app.get('/search/:str', function(req, res) {
var Users = require('./models/users');
Users.find({name: /[req.params.str]/}, function(err, data) {
if (err) return console.error(err);
return res.json(data);
})
});
The problem is with /[req.params.str]/. If str is set to "Jo", then it should only return users whose name contains the string "Jo", but it is returning every record. How can I fix this?
2 ways to do this:
With regular expression. MongoDB supports regular expression match. here's an example:
users.find({firstname: /Jo/}, function(err, doc) {...})
However, if you got a big collection, it's going to be slow. Because regular expression usually doesn't use index at all unless you have a fixed prefix like /^Jo/.
Or in some situations you may want to try the new feature, full text search:
First of all, you need an text index:
db.users.ensureIndex( { firstname: "text" } );
Then use the $text operator:
db.users.find({ $text: { $search: "Jo"}});
The text search does not just look for exactly what you are searching. And in some situations it may not work the way you wanted.
The $text operator can search for words and phrases. The query matches on the complete stemmed words. For example, if a document field contains the word blueberry, a search on the term blue will not match the document.
More information, please refer to the document.

Mongodb Or operator in Find Query

I have a query as below.
Post.native(function(err, collection) {
collection.find({
$or: [ { id :id }, { parentid : id} ]
}, function(err, result) {
if (err)
console.log({error: err});
console.log(result);
});
});
But this returns zero results , even though i have one result satisfying {id : id} and two results satisfying {parentid : id}. I need three results to be printed.
Please correct me if my query is wrong. Any help would be appreciated.
Please help. Thanks a lot
Here are some of the most common things I would do to debug this problem.
If you use the native driver and the fields you are querying are MongoIds, you need to ensure that ids are instances of ObjectId, i.e.
id = new mongo.ObjectID(id)
Also, mongodb natively stores its id as _id. Did you specifically create an id field?

How to retrieve array of specific field of sub document- mongodb

This is my first mongodb project,I have this document structure in mongodb, I am trying to retrieve a particular user account (each user account has an array of contacts), from this user account, I will then obtain an array of the ID fields of the users contacts and then pass this array as a parameter to another query, I am doing this to avoid having to loop through the users contacts array in order to obtain the ID fields, here is the document structure, the query I tried is below it
{
name,
id,
contacts:[{
contactId, //I need an array of this field
dateAdded
},
contactId,
dateAdded
},
{}..]
}
//
var findByIdAll = function(accountId, callback) {
var self=this;
//Get the user account
Account.findOne({_id:accountId}, function(err,doc) {
/ After the user account has been obtained, the function below will
// use an array of the users contactsId's to fetch the contact's accounts
//please how do I obtain the array of contact Id's before reaching here
self.Account.find({_id:{$in:[/array of contact Ids]}},function(err,results){
callback(results);
});
});
};
EDIT
//I have now been able to obtain an array of contactID fields using the following query
var r=db.accounts.aggregate({$match:{email:'m#live.com'}},{$unwind:"$contacts"},
{$project:{_id:0,contacts:1}},{$group:{_id:'$_id',
list:{$push:'$contacts.accountId'}}});
The result I get from the query is
r
{
"result" : [
{
"_id" : null,
"list" : [
ObjectId("51c59a31c398c40c22000004"),
ObjectId("51c59a31c398c40c22000004")
]
}
],
"ok" : 1
}
A normal MongoDB query will always give you the entire document with the same structure.
If you want to get just part of the document or make a transformation to it you need to use the Aggregation Framework (is not as hard to understand as it looks, give it a try).
In your case you might have to use $unwind in contacts to explode the array, $match to get only the account you want, and $project to present the data as you want.