execMatch only returning first result - javascript

I am trying to find a particular record (with an id of 1) and then to return only its history field. This history field is an array of objects with a timestamp property. When I return this history field, I want it to contain only objects with timestamp properties greater than 0.
Sample collection
[{
"_id" : 1,
"history" : [
{
"content" : "hello",
"timestamp" : 1394639953878,
"_id" : ObjectId("53208451767743b748ddbd7d")
},
{
"content" : "world",
"timestamp" : 1394639953879,
"_id" : ObjectId("33208451767743b748ddbd7e")
}
]
}]
Thus, according to Mongo and Mongoose documentation, I do something like:
model.find({_id: 1})
.select({ history: { $elemMatch: { timestamp: { $gt: 0 } } } });
The issue: I understand that there will only be one result (since I select the id 1) but I am using $elemMatch to populate its history field with all objects that have a timestamp greater than 0. The issue is that only one object is ever in history.

The $elemMatch projection returns only the first matching element satisfying the condition. Checkout the documentation.
Using aggregate would be the right approach to achieve what you want I think. Please see below:
db.bar.aggregate([
{$match:{_id:1}},
{$unwind:"$history"},
{$match:{"history.timestamp":{$gt:0}}},
{$group:{_id:"$_id", history:{$push:"$history"}}}
])

Related

copying values inside a record from one key to other in mongodb

I am working on MongoDB. I am trying to append some new key-value pairs to my existing collection. I got more than 50 documents in my collection, in all of which I want to update a new key value pair. In the new key value pair I am adding, one of the values of the key should be from one of the existing key value pair of the same collection.
For example:
{ name : "algebra", quantity : 25 }
Now I want to update each of the document with
{ category : "maths" , available_quantity : 25 }
Without entering distinctly for every document its available quantity. I want it to be picked from it's respective quantity's value, and be inserted in the new key available_quantity.
I am using the code to update the document :
db.books.update( {} , { $set : { category : "maths" , available_quantity : `__ ? __` }, false, true }
What best can I put in __ ? __ block?
You can do it with Aggregation framework:
db.books.update({},
[
{
"$set": {
"available_quantity": "$quantity"
}
}
],
{
multi: true
})
Working example

Mongoose update inner field

I have this kind of documents in my mongoDB :
{
id: my_id
inner_foo : [
{
inner_id : id_A
inner_field : field_A
},
{
inner_id : id_B
inner_field : field_B
}
]
}
When receiving a request, i got two ids, the id one (here my_id), and the inner_id one (here for example id_A).
How can I, using JS and mongoose, update an inner field ? such as the inner_field field of one of the array contained objet using its inner_id
I think I cannot use findByIdAndUpdate since I can't make a direct reference to the correct inner_foo entry using that method, so how should I do it ?
Many thanks !
Kev.
Demo - https://mongoplayground.net/p/2pW4UrcVFYr
Read - https://docs.mongodb.com/manual/reference/operator/update/positional/
db.collection.update(
{ id: "my_id", "inner_foo.inner_id": "id_A" }, // find the matching document
{ $set: { "inner_foo.$.inner_field": "id_A_Update" }} // set the value on the matched array index
)

MongoDB: adding sort() to query ruins result set for $and query

I've stumbled upon some very strange behavior with MongoDB. For my test case, I have an MongoDB collection with 9 documents. All documents have the exact same structure, including the fields expired_at: Date and location: [lng, lat].
I now need to find all documents that are not expired yet and are within a bounding box; I show match documents on map. for this I set up the following queries:
var qExpiry = {"expired_at": { $gt : new Date() } };
var qLocation = { "location" : { $geoWithin : { $box : [ [ 123.8766, 8.3269 ] , [ 122.8122, 8.24974 ] ] } } };
var qFull = { $and: [ qExpiry, qLocation ] };
Since the expiry date is long in the past, and when I set the bounding box large enough, the following queries give me all 9 documents as expected:
db.docs.find(qExpiry);
db.docs.find(qLocation);
db.docs.find(qFull);
db.docs.find(qExpiry).sort({"created_at" : -1});
db.docs.find(qLocation).sort({"created_at" : -1});
Now here's the deal: The following query returns 0 documents:
db.docs.find(qFull).sort({"created_at" : -1});
Just adding sort to the AND query ruins the result (please note that I want to sort since I also have a limit in order to avoid cluttering the map on larger scales). Sorting by other fields yield the same empty result. What's going on here?
(Actually even stranger: When I zoom into my map, I sometimes get results for qFull, even with sorting. One could argue that qLocation is faulty. But when I only use qLocation, the results are always correct. And qExpiry is always true for all documents anyway)
You may want to try running the same query using the aggregation framework's $match and $sort pipelines:
db.docs.aggregate([
{ "$match": qFull },
{ "$sort": { "created_at": -1 } }
]);
or implicitly using $and by specifiying a comma-separated list of expressions as in
db.docs.aggregate([
{
"$match": {
"expired_at": { "$gt" : new Date() },
"location" : {
"$geoWithin" : {
"$box" : [
[ 123.8766, 8.3269 ],
[ 122.8122, 8.24974 ]
]
}
}
}
},
{ "$sort": { "created_at": -1 } }
]);
Not really sure why that fails with find()
chridam suggestion using the aggregation framework of MongoDB proved to be the way to go. My working query now looks like this:
db.docs.aggregate(
[
{ $match : { $and : [qExpiry, qLocation]} },
{ $sort: {"created_at": -1} }.
{ $limit: 50 }.
]
);
Nevertheless, if any can point out way my first approach did not work, that would be very useful. Simply adding sort() to a non-empty query shouldn't suddenly return 0 documents. Just to add, since I still tried for a bit, .sort({}) return all documents but was not very useful. Everything else failed including .sort({'_id': 1}).

Node js, Mongoose query to match id with values stored as comma separated string

I am new to the Mongoose and NodeJS world.
I have product document where I am storing category ids as shown below.
"reviewscount" : "2",
"overallrating" : "4.5",
"urlkey" : "strive-shoulder-pack",
"productid" : "2",
"categoryid" : "3,7,4",
Now wanted to fetch all the products belongs to one category based on category Id.
I tried below code, but getting empty result
Product.find({ "categoryid" : { "$in" : [ categoryId ] } }, function (err, products) {
console.log(products);
}
Am I doing it wrong way?
This would check whether the whole categoryid is equal to something in the in-array.
You basically got two options, the first (preferred) is to store your categoryid(s) as an array instead of csv and use a simple query as seens in this answer.
The second option is to manually (mongoose docs) parse the categoryid against your Id, but that wont scale very well (besides other problems).
Product.find({$where : 'this.categoryid.indexOf("' + categoryId + '") != -1'});
Product.find({ "categoryid" : { "$in" : [ categoryId ] } }, function (err, products) {
console.log(products);
}
for this type query you need to convert in your schema.
"reviewscount" : "2",
"overallrating" : "4.5",
"urlkey" : "strive-shoulder-pack",
"productid" : "2",
"categoryid" : [3,7,4] // convert in your schema make categoryid:[Number]
to insert in categoryid use $addToSet with $each if you want unique value in array.

Finding nested array data with elemMatch in MongoDB/Meteor

collection structure as such:
{"_id" : "abc",
"potentialUsers" : [{"userID" : "def"},
{"userID" : "ghi"}]
},
{"_id" : "123",
"potentialUsers" : [{"userID" : "456"},
{"userID" : "789"}]
},
I want to query if user abc has the user def in their potential users array. My current query (client) is
getPendingLiftRequests: function() {
return collection.find({}, {potentialUsers: {$elemMatch: {userID: Meteor.user().services.facebook.id}}});
}
On the server I publish all of that collection to the user, and then just selectively show it based on the view of the client. However, when I try to use an elemMatch on the array, it just shows all of the records, when it should only show 1.
You don't need $elemMatch here. This should work:
var fId = Meteor.user().services.facebook.id;
return collection.find({'potentialUsers.userID': fid});
Have look at the documentation for minimongo in Meteor:
http://docs.meteor.com/#/full/find
You have to use the fields option to get the desired result.
getPendingLiftRequests: function() {
return collection.find({}, { fields: { potentialUsers: {$elemMatch: {userID: Meteor.user().services.facebook.id}}}});
}

Categories

Resources