I have an array field called tags in almost every document in a collection. There are a few docs that don't have that field.
If I run db.posts.find({ "some_tag": { $in: tags } }); I'd expect MongoDB to return only the posts that do have "some_tag" in their tag field, and exclude the ones that don't even have the field, but I'm actually getting this error: JavaScript execution failed: ReferenceError: miembros is not defined
Why is that? Can I query my collection this way if some documents don't have that field?
EDIT: Problem fixed. See first comment.
I'd expect MongoDB to return only the posts that do have "some_tag" in their tag
That's what it does, if you query it correctly:
> db.tags.insert({"name" : "john", "tags" : ["tag", "boo"]});
> db.tags.insert({"name" : "mike" });
> db.tags.find({"tags" : "boo" });
{ "_id" : ObjectId("525e53a5e90cc5362ea98842"),
"name" : "john", "tags" : [ "tag", "boo" ] }
or
> db.tags.find({"tags" : {$in : [ "boo"] } });
{ "_id" : ObjectId("525e53a5e90cc5362ea98842"),
"name" : "john", "tags" : [ "tag", "boo" ] }
The problem is that you inverted the $in: You're looking for "posts that do have 'some_tag' in their tag field", but the query { "some_tag": { $in: tags } } looks for documents that have a field some_tag with one of the value given in tags.
Related
I have two collections. The first collection contains students:
{ "_id" : ObjectId("51780f796ec4051a536015cf"), "name" : "John" }
{ "_id" : ObjectId("51780f796ec4051a536015d0"), "name" : "Sam" }
{ "_id" : ObjectId("51780f796ec4051a536015d1"), "name" : "Chris" }
{ "_id" : ObjectId("51780f796ec4051a536015d2"), "name" : "Joe" }
The second collection contains courses:
{
"_id" : ObjectId("51780fb5c9c41825e3e21fc4"),
"name" : "CS 101",
"students" : [
ObjectId("51780f796ec4051a536015cf"),
ObjectId("51780f796ec4051a536015d0"),
ObjectId("51780f796ec4051a536015d2")
]
}
{
"_id" : ObjectId("51780fb5c9c41825e3e21fc5"),
"name" : "Literature",
"students" : [
ObjectId("51780f796ec4051a536015d0"),
ObjectId("51780f796ec4051a536015d0"),
ObjectId("51780f796ec4051a536015d2")
]
}
{
"_id" : ObjectId("51780fb5c9c41825e3e21fc6"),
"name" : "Physics",
"students" : [
ObjectId("51780f796ec4051a536015cf"),
ObjectId("51780f796ec4051a536015d0")
]
}
Each course document contains students array which has a list of students registered for the course. When a student views a course on a web page he needs to see if he has already registered for the course or not. In order to do that, when the courses collection gets queried on the student's behalf, we need to find out if students array already contains the student's ObjectId. Is there a way to specify in the projection of a find query to retrieve student ObjectId from students array only if it is there?
I tried to see if I could $elemMatch operator but it is geared towards an array of sub-documents. I understand that I could use aggregation framework but it seems that it would be on overkill in this case. Aggregation framework would probably not be as fast as a single find query. Is there a way to query course collection to so that the returned document could be in a form similar to this?
{
"_id" : ObjectId("51780fb5c9c41825e3e21fc4"),
"name" : "CS 101",
"students" : [
ObjectId("51780f796ec4051a536015d0"),
]
}
[edit based on this now being possible in recent versions]
[Updated Answer] You can query the following way to get back the name of class and the student id only if they are already enrolled.
db.student.find({},
{_id:0, name:1, students:{$elemMatch:{$eq:ObjectId("51780f796ec4051a536015cf")}}})
and you will get back what you expected:
{ "name" : "CS 101", "students" : [ ObjectId("51780f796ec4051a536015cf") ] }
{ "name" : "Literature" }
{ "name" : "Physics", "students" : [ ObjectId("51780f796ec4051a536015cf") ] }
[Original Answer] It's not possible to do what you want to do currently. This is unfortunate because you would be able to do this if the student was stored in the array as an object. In fact, I'm a little surprised you are using just ObjectId() as that will always require you to look up the students if you want to display a list of students enrolled in a particular course (look up list of Id's first then look up names in the students collection - two queries instead of one!)
If you were storing (as an example) an Id and name in the course array like this:
{
"_id" : ObjectId("51780fb5c9c41825e3e21fc6"),
"name" : "Physics",
"students" : [
{id: ObjectId("51780f796ec4051a536015cf"), name: "John"},
{id: ObjectId("51780f796ec4051a536015d0"), name: "Sam"}
]
}
Your query then would simply be:
db.course.find( { },
{ students :
{ $elemMatch :
{ id : ObjectId("51780f796ec4051a536015d0"),
name : "Sam"
}
}
}
);
If that student was only enrolled in CS 101 you'd get back:
{ "name" : "Literature" }
{ "name" : "Physics" }
{
"name" : "CS 101",
"students" : [
{
"id" : ObjectId("51780f796ec4051a536015cf"),
"name" : "John"
}
]
}
It seems like the $in operator would serve your purposes just fine.
You could do something like this (pseudo-query):
if (db.courses.find({"students" : {"$in" : [studentId]}, "course" : courseId }).count() > 0) {
// student is enrolled in class
}
Alternatively, you could remove the "course" : courseId clause and get back a set of all classes the student is enrolled in.
I am trying to explain by putting problem statement and solution to it. I hope it will help
Problem Statement:
Find all the published products, whose name like ABC Product or PQR Product, and price should be less than 15/-
Solution:
Below are the conditions that need to be taken care of
Product price should be less than 15
Product name should be either ABC Product or PQR Product
Product should be in published state.
Below is the statement that applies above criterion to create query and fetch data.
$elements = $collection->find(
Array(
[price] => Array( [$lt] => 15 ),
[$or] => Array(
[0]=>Array(
[product_name]=>Array(
[$in]=>Array(
[0] => ABC Product,
[1]=> PQR Product
)
)
)
),
[state]=>Published
)
);
I'm having some trouble determining how to find a document within a collection, and a value within an object in a subarray of that document — and then update a value within an object in that array.
I need to do the following:
find by _id
find object in ratings array that matches the user + post keys
update the report value within that object
For example, the documents in my collection are set up like below.
{
"_id" : "mz32AcxhgBLoviRWs",
"ratings" : [
{
"user" : "mz32AcxhgBLoviRWs",
"post" : "SMbR6s6SaSfsFn5Bv",
"postTitle" : "fdsfasdf",
"date" : "2017-09-27",
"rating" : "4",
"review" : "sdfa",
"report" : "a report"
},
{
"user" : "mz32AcxhgBLoviRWs",
"post" : "iZbjMCFR3cDNMo57W",
"postTitle" : "today",
"date" : "2017-09-27",
"rating" : "4",
"review" : "sdfa",
"report" : "some report"
}
]
}
It seems that you want just one update, not three separated queries.
Collection.update({
_id: <id>,
ratings: {
$elemMatch: {
user: <user>,
post: <post>
}
}
}, {
$set: {
'ratings.$.report': <report>
}
});
Documentation: $elemMatch, <array>.$.
I have a simple application with registration/login and it is basically a coursera/udemy type, where the app lists specific courses and users can like them or enroll in them. I have been trying to make a mongodb function that updates a user in the database and since users can like the courses it has to update all courses too (courses have a field "usersLiked", which is an array and keep all user documents which have liked it).
The course structure is the following:
{
"_id" : ObjectId("5977662564aac9f6c8d48884"),
"title" : "Title",
"lecturer" : "Lecturer",
"length" : 30,
"coverPhoto" : "Photo",
"usersLiked": [
{
"_id" : ObjectId("597763e346a7a463cbb8f529"),
"fullname" : "Name",
"username" : "Username",
"password" : "Hash",
"city" : "City",
"street" : "Street",
"website" : "Website"
}
],
"lectures" : [
{
"title" : "Introduction",
"number" : 1,
"url" : "someURL"
}
]
}
And the user structure:
{
"_id" : ObjectId("597763e346a7a463cbb8f529"),
"fullname" : "Name",
"username" : "Username",
"password" : "Hash",
"enrolledCourses" : [
...
]
}
],
"city" : "City",
"street" : "Street",
"website" : "Website"
}
So now I am calling this function when I want to change. It changes the userCollection but in the courseCollection it does nothing, while it should get all courses and if some of them have an object with username(the user's username) in the "usersLiked" array it should modify the user there too.
const updateUser = (username, details) => {
usersCollection
.update({
username: username,
}, {
$set: {
fullname: details.fullname,
city: details.city,
street: details.street,
website: details.website,
},
});
coursesCollection
.updateMany(
{
usersLiked: {
$elemMatch: {
username: username,
},
},
},
{
$set: {
'usersLiked.username': details.username,
'usersLiked.city': details.city,
'usersLiked.street': details.street,
'usersLiked.website': details.website,
},
}
);
};
Your match on the course update looks valid but you are trying to set values into an array and according to the Mongo docs you need to provide an array indexer or a positional operator.
The following will allow the set command to operate on the first element within the usersLiked array which matches the given username.
coursesCollection.updateMany(
{
usersLiked: {
$elemMatch: {
username: username,
},
},
},
{
$set: {
'usersLiked.$.username': details.username,
'usersLiked.$.city': details.city,
'usersLiked.$.street': details.street,
'usersLiked.$.website': details.website
},
}
)
You could also choose which element in the usersLiked array to update e.g. usersLiked.1.username but I suspect each course only has one element in usersLiked for a given username in which case using the $ operator (which means: the first matching array element) should suffice.
When I try to find specific object in array using find({query}) I always get all elements from array.
Activities array stores activities (it would be a thousands of them) as you can see in the following snippet:
This is my collection:
{
"_id" : ObjectId("58407140755324d04db2ce95"),
"owner" : 103429326776572,
"activities" : [
{
"name" : "test1",
"startTime" : ISODate("2016-08-11T17:41:54Z"),
"type" : "te1",
"lat" : 1,
"lon" : 1,
"creator" : 126212904493088,
"coverPhoto" : {
"name" : "test1",
"path" : "c:\\Users\\Francis\\Desktop\\dusk\\public\\coverPhotos\\SJ9tpP6Mx.jpg"
},
"identifier" : "H1g9F6vpGl",
"users" : [
1,
2,
3
],
"hashTags" : [
"some",
"hashtags"
]
},
{
"name" : "test2",
"startTime" : ISODate("2016-08-11T17:41:53Z"),
"type" : "te2",
"lat" : 1,
"lon" : 1,
"creator" : 103312904493090,
"coverPhoto" : {
"name" : "test2",
"path" : "c:\\Users\\Francis\\Desktop\\dusk\\public\\coverPhotos\\Hy8qpvafe.jpg"
},
"identifier" : "rJlU5TvpMx",
"users" : [
1,
2,
3
],
"hashTags" : [
"some",
"hashtags"
]
}
]
}
I need to get for example an activity that has specific identifier.
I tried to use queries like:
1) db.myCollection.find({'activities.identifier' : "rJlU5TvpMx"})
2) db.myCollection.find({'activities' : { $elemMatch : { "identifier" : "rJlU5TvpMx", "creator" : 103312904493090 } })
And all combinations with '' or "" signs
I found above queries at mongodb docs in equal documents schema as mine is.
Can you tell me what am I doing wrong ?
You can try either use single match or multiple match based on your need. This makes use of $elemMatch(projection)
db.myCollection.find({"_id" : ObjectId("58407140755324d04db2ce95")},
{activities: {$elemMatch: { identifier: "rJlU5TvpMx"}}})
db.myCollection.find( {"_id" : ObjectId("58407140755324d04db2ce95")},
{activities: {$elemMatch: {creator : 103312904493090, identifier: "rJlU5TvpMx" }}})
You are looking for the projection object which gets passed as an argument in your query. It allows the return of specific fields from your search rather than the entire document. http://mongoosejs.com/docs/api.html#model_Model.find
I would also suggest looking at the response to this question here: Mongoose Query: Find an element inside an array which makes use of the unwind operator to enter the array as it seems to be relevant to your needs.
In the collection you are searching in, you have just one Document(Object). If you apply method find() to your collection and the query inside matches the value in activities.identifier it will return the only Document(object).
To have a better understanding of what I am talking about check example on mongoose API doc
And query result here.
Try check this out https://docs.mongodb.com/v3.0/reference/operator/projection/elemMatch/#proj._S_elemMatch instead
I'm trying to find the mongo document by matching the "Tel" field value,
{
"_id" : ObjectId("54f047aa5b9e5c7c13000000"),
"data" : [
{
"Id" : "1",
"Country" : "India",
"Timezone" : "Europe/Paris",
**"Tel" : "03 20 14 97 70",**
"Prenom" : "ddd",
"Email" : "ddd#gmail.com",
"City" : "Chennai",
"date" : "",
"active" : "true"
}
]
}
how to fetch the above document from mongo collection using the below find method without space in "Tel" field,
>db.test.find({"data.Tel":"0320149770"})
Please can anyone help me !!!
If this is what you really want to do on a regular basis then you are best off adding another field to the document that has the string present without any spaces.
The reason why is though there are functions you can perform to do the search, none of the methods are able to use an index to match the document, so this means scanning everything in the collection in order to find a match.
You can do this with JavaScript evaluation in a $where clause:
db.test.find(function() {
return this.data.some(function(el) {
el.Tel.replace(/ /g,"") == "0320149770"
});
});
But don't do that because it's really bad. you are better off just updating all the data instead:
db.test.find().forEach(function(doc) {
doc.data = doc.data.map(function(el) {
el.TelNum = el.Tel.replace(/ /g,"");
})
db.test.update({ "_id": doc._id },{ "$set": { "data": doc.data } });
})
Or something along those lines to have a field without spaces all ready to search on directly.