MongoDB - Aggregate $match on ObjectId - mongodb

I have a schema that looks like this:
var mongoose = require('mongoose');
module.exports = mongoose.model('Owner',{
username: String,
blocks: {type:mongoose.Schema.Types.ObjectId, ref: 'Block'},
});
I'm trying to run a query to see if Owner has a reference to Block's id. Owner has an array of ObjectIds. When I run db.owners.aggregate({$match: {username: 'example'}},{$unwind: "$blocks"},{$project: { _id : 1,blocks: 1}}) it returns:
{ "_id" : ObjectId("550d9dc64d9dc3d026fadfc7"), "blocks" : ObjectId("550dc117dc9605ab27070af7") }
{ "_id" : ObjectId("550d9dc64d9dc3d026fadfc7"), "blocks" : ObjectId("550dc123dc9605ab27070af8") }
{ "_id" : ObjectId("550d9dc64d9dc3d026fadfc7"), "blocks" : ObjectId("550dc12edc9605ab27070af9") }
{ "_id" : ObjectId("550d9dc64d9dc3d026fadfc7"), "blocks" : ObjectId("550dc157dc9605ab27070afa") }
How can I match the block id? I've tried db.publishers.aggregate({$match: {username: 'example'}},{$unwind: "$blocks"},{$project: { _id : 1,blocks: 1}},{$match : {"blocks._id" : '550dc157dc9605ab27070afa'}}) but that doesn't work.

I think you don't need aggreation for that, you can use a simple find() or findOne() query:
var Mongoose = require('mongoose');
var ObjectId = Mongoose.Types.ObjectId;
Owner.findOne({ username: 'example', blocks: new ObjectId('550dc157dc9605ab27070afa') }, function (err, owner) {
...
});

that does not work right now with versions > 3.8.31
i've wasted countless hours on that one, should have tested the earlier mayor sooner...

Related

mongoose find collection by _id in a list of array

Here's my Schema
var PositiveSchema = new mongoose.Schema({
schoolID: {
type: mongoose.Schema.Types.ObjectId, ref: 'School'
},
name: String,
restrictAwardTo: Object
})
Now restrictAwardTo saves the data in this format
"restrictAwardTo" : [
{
"_id" : "5c31907d908eb8404657cbf0",
"firstName" : "Admin 2a"
},
{
"_id" : "5c1a7677c98da061141475a8",
"firstName" : "Justice"
},
{
"_id" : "5c1a7677c98da061141475a9",
"firstName" : "Itik"
}
],
How can I search inside my document using one of the _id listed under restrictAwardTo? I tried the solutions given below
mongooseJS find docs with IDs in an array
mongoose query: find an object by id in an array but it returns empty.
in Robo3t db.getCollection('positives').find({ 'restrictAwardTo._id' : ObjectId('5c31907d908eb8404657cbf0') })
Update: In Robo3t, this query db.getCollection('positives').find({ 'restrictAwardTo._id' : {$in: ['5c1a7677c98da061141475a7']} }) works. Now I'm making it work for mongoose too.
Here's the mongoose that works for me:
Positive.find({ schoolID: mongoose.mongo.ObjectId(schoolID), "restrictAwardTo._id": { $in: [userID]} })
But I'm not entirely sure of the performance for large records.
You could go through this way.
Positive.findOne({'restrictAwardTo': {$elemMatch: {_id: userID}}},
(err,schoolInfo) => { });

How to find and update a mongoose model?

I have a model userDatas and it contain a list of user data.
[{
"_id" : ObjectId("5bb6730721f28a295436b36f"),
"userId" : "5bb6730721f28a295436b36e",
"reputationNumber" : 0,
"questions" : [],
"answers" : []
},
{
"_id" : ObjectId("5bb6738c21f28a295436b371"),
"userId" : "5bb6738c21f28a295436b370",
"reputationNumber" : 0,
"questions" : [],
"answers" : []
}]
I want to filter by userId and add "5bb7d72af050ca0910282ff4" string to questions array. How to accomplish that?
//Userdatas.find
Since you are using mongoose you can use the findOneAndUpdate and addToSet to achieve this:
Userdatas.findOneAndUpdate(
{userId: "5bb7d72af050ca0910282ff4"},
{$addToSet: {questions: '5bb7d72af050ca0910282ff4'}},
function (err, result) {
...
}
)
The query would look something like
Userdatas.findOne({where: {userId: "5bb7d72af050ca0910282ff4"}}, function (err, data) {
if (!err && data) {
var questionsArr = [];
if (data.questions) {
questionsArr = data.questions;
}
questionsArr.push('5bb7d72af050ca0910282ff4');
data.updateAttributes({questions: questionsArr}, function(err, updateData) {
cb (err, updateData);
});
} else {
cb (err, {});
}
});
Unfortunately you would need 2 queries to do this. First, to get the document where the userId matches your required userId and to push the string to the questions array and update the document.
EDIT:
The first findOne query fetches the document where userId matches our user. The updateAttributes query is executed on the document that was fetched, so it updates the correct document.

Updating mongo document after it's been found

I'm trying to update a number of documents from an array of object ids passed. I want to be able to update each document by pushing the userid into the visitor array.
i'm getting this error CastError: Cast to ObjectId failed for value. see my code below
var estates = req.params.estates.split(',');
Estate.findByIdAndUpdate({ _id: { $in: estates } }).then(function(Estate)
{
Estate.visitors.push(mongoose.Types.ObjectId( req.params.userid));
Estate.save();
});
according to mongoose documentations findByIdAndUpdate first param is an id which you are passing an object
try replacing it by findOne({ _id: { $in: estates } })
Your API usage is wrong, first argument for findByIdAndUpdate should be an id, it fails to cast array to id.
for matching and updating multiple documents,
you need to use updateMany
db.foo.updateMany({"_id" : { $in : [1,2,3] }},{$push : {test: 2}},{$upsert : false})
or update with multi : true option
db.store.update({"_id" : { $in : [1,2,3] }},{$push : {test: 2}},{$upsert : false, multi : true})
output
{ "acknowledged" : true, "matchedCount" : 3, "modifiedCount" : 0 }
Mongoose
Estate.update({ _id: { $in: estates } }, {$push : {visitors : req.params.userid}}, {multi : true, upsert : false})
Doc findByIdAndUpdate

Get documents which expired before today in MongoDB

I am using MongoDB 2.6 and I'm making a query that filter the documents by expired date. I want to know which documents expired before today. My persisted data represent publications.
I've made some queries but it doesn't return documents. I know there are many documents who satisfy this condition.
I tried two query but no one works:
1.
{
"domain.ApplicationCase.fields.ExpireDate": { $gte : {
$currentDate: {
lastModified: true,
"cancellation.date": { $type: "timestamp" }
}}}
}
2.
{
"domain.ApplicationCase.fields.ExpireDate": { $gte : new Date()}
}
Part of document records:
{
"_id" : "1234546",
"fields" : {
"Orchestration" : "default",
"Segmentation" : "PFI",
"MatchKey" : "1",
"UserID" : "001"
},
"domain" : {
"ApplicationCase" : {
"_id" : null,
"fields" : {
"ExpireDate" : "2015-11-13T13:47:26Z",
....
What's wrong?
If you want to get records which are expired before today then you should use $lte instead of $gte:
db.myCollection.find({
"domain.ApplicationCase.fields.ExpireDate": { $lte : new Date()}
})
Update:
So the core problem with your documents is that, the value of domain.ApplicationCase.fields.ExpireDate are not in the date format instead they are in simple String format.
So you first need to convert them to the date in order for the query to work since you are comparing an String with the Date.
Probably, you can use a code like this to convert the string to the date:
db.myCollection.find({
"domain.ApplicationCase.fields.ExpireDate": {$exists: true}
}).snapshot().forEach(function(record) {
var stringValue = record.domain.ApplicationCase.fields.ExpireDate;
db.myCollection.update({_id: record._id}, {$set: {
"domain.ApplicationCase.fields.ExpireDate": ISODate(stringValue)
}});
})

search in combination two field in Mongodb

I use from Mongodb and my database schema like this:
firstName: 'vahid',
lastName: 'kh',
age: 12
I want find all records that firstname + lastname likes 'vahid kh'. In SQL, this would be:
Select * from table where firstName + lastName like '%vahid kh%'
Seems this is available only in 2.4, which was released today.
> db.temp.save({ "firstName" : "Bob", "lastName" : "Smith", Age : 12 });
> db.temp.find();
{ "_id" : ObjectId("5148a2a00477cddcdece1b34"), "firstName" : "Bob", "lastName" : "Smith", "Age" : 12 }
> db.temp.aggregate({ $project : { "name" : { $concat : [ "$firstName", " ", "$lastName" ] } } });
{
"result" : [
{
"_id" : ObjectId("5148a2a00477cddcdece1b34"),
"name" : "Bob Smith"
}
],
"ok" : 1
}
You can use $regex, this way you can use partial matches.
Something like that:
db.collection.find( { field: /acme.*corp/i } )
Here is somewhat similar question with answer in php: MongoRegex and search multiple rows in collection
Docs about mongoregex are here: http://docs.mongodb.org/manual/reference/operator/regex/
Edit:
I just read your comment with query in sql. Simple solution could be to make field fullname and search it with $regex, it is kind of db denormalization, where you store somewhat redundant data.
Or even easier, this should do the job too:
db.collection.find( { firstName: /*vahid/i, lastName: /kh*/i } )
To search against a combination of two or more fields, you need to use the aggregation framework. It lets you sort by the combinations of fields, and you don't need to store (denormalize) any extra fields:
db.collection.aggregate([
{
$match: {
// Optional criteria to select only some documents to process, such as...
deleted: null
}
},
{
$addFields: {
// Need to prefix fields with '$'
fullName: { $concat: [ "$firstName", "$lastName" ] },
}
},
{
$search: { fullName: /.*vakid kh.*/ },
}
]);
Explanation:
the $addFields aggregation pipeline stage creates dynamic, on-the-fly fields
$concat creates the fullName field by concatenating the first and last name
$search does a regular expression search, which is the MongoDB equivalent to the SQL LIKE operator.
I have code in expressjs code aggregate is bit slow then find so I have use conditional based and use regular express to find true results, I am sharing with you nodejs code I hope it useful for you or my other code lover friends.
router.get('/publisher/:q',function(req,res){
var q = ucfirst( req.params['q'] );
var qSplit = q.split(" ");
var db = mongojs(CONNECTION_STRING, ['tags_helper','users']);
var query = { "status" : 1, isAdmin : { $exists : false } };
console.log(qSplit);
if( qSplit.length > 1 )
{
query["firstName"] = new RegExp(qSplit[0],"i");
query["lastName"] = new RegExp(qSplit[1],"i");
}
else
{
qSplit[0] = new RegExp(qSplit[0],"i");
qSplit[1] = new RegExp(qSplit[0],"i");
//query.push( { $or : [ {"firstName": new RegExp(q,"i")},{"lastName": new RegExp(q,"i")} ] } );
query["$or"] = [ {"firstName": new RegExp(q,"i")},{"lastName": new RegExp(q,"i")} ];
}
db.users.find( query,{_id : 1,firstName:1,lastName:1,image:1,profileName:1}).limit(10,function (err, users) {
//TODO your further code ..
});
});
Have a happy coding day.