I'm new to robmongo and I received an assignment to write some queries.
let say I have a collection that each key has some values for example value of "userId" and value of "deviceModel".
I need to write a query that shows for each device model how many users has this device.
this is what I got so far:
db.device_data.aggregate([ {"$group" : {_id:"$data.deviceModel", count:{$sum:1}}}])
The problem is that this aggregate for each device the number of keys it appears.
{
"_id" : { "$binary" : "AN6GmE7Thi+Sd/dpLRjIilgsV/4AAAg=", "$type" : "00" },
"auditVersion" : "1.0",
"currentTime" : NumberLong(1479301118381),
"data" : {
"deviceDesign" : "bullhead",
"loginType" : "GOOGLE",
"source" : "SDKLoader",
"systemUptimeMillis" : 137652880.0,
"simCountryIso" : "il",
"networkOperatorName" : "Cellcom",
"hasPhonePermission" : true,
"deviceIdentifier" : "353627074839559",
"sdkVersion" : "0.7.939.2016-11-14.masterDev",
"brand" : "google",
"osVersion" : "7.0",
"osVersionIncremental" : "3239497",
"deviceModel" : "Nexus 5X",
"deviceSDKVersion" : 24.0,
"manufacturer" : "LGE",
"sdkShortBuildDate" : "2016-11-14",
"sdkFullBuildDate" : "Mon Nov 14 22:16:40 IST 2016",
"product" : "bullhead"
},
"timezone" : "Asia/Jerusalem",
"collectionAlias" : "DEVICE_DATA",
"shortDate" : 17121,
"userId" : "00DE86984ED3862F9277F7692D18C88A#1927cc81cfcf7a467e9d4f4ac7a1534b"}
this is an example of how one key locks like.
The below query should give you distinct count of userId for a deviceModel. I meant if a same userId present for a deviceModel multiple items, it will be counted only once.
db.collection.aggregate([ {"$group" : {_id:"$data.deviceModel", userIds:{$addToSet: "$userId"}}
},
{
$unwind:"$userIds"
},
{
$group: { _id: "$_id", userIdCount: { $sum:1} }
}])
Unwind:-
Deconstructs an array field from the input documents to output a
document for each element.
In the above solution, it deconstructs the userId array formed on the first pipeline.
addToSet:-
Returns an array of all unique values that results from applying an
expression to each document in a group of documents that share the
same group by key.
This function ensures that only unique values are added to an array. In the above case, the userId is added to an array in the first pipeline.
Related
I have a long array of docs to create. When I create them I get no errors.
const docsJson =[some array of json of docs to create]
const orders = await MySchema.create(ordersJSON);
// orders.length returns the same number of docs as docsJson
But when I search for the new docs, only some were created.
const actualOrdersCreated = await MySchema.find({ _id: { $in: orders.map((p) => p._id) } });
// actualOrdersCreated.length returns less docs than in docsJson
What's causing this?
I think your data is to large.
The maximum BSON document size is 16 megabytes.
Reference: https://www.mongodb.com/docs/manual/reference/limits/
This was due to having a ttl (time to live) index on one mongo database and not the other. I was copying docs over from a database. The index on the first database was:
$ mongo "mongodb+srv://....database-1-url"
>> db.myschema.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_"
},
{
"v" : 2,
"key" : {
"paidOn" : 1
},
"name" : "paidOn_1",
"background" : true
}
]
But the database I was working with had expireAfterSeconds.
$ mongo "mongodb+srv://....database-2-url"
>> db.myschema.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_"
},
{
"v" : 2,
"key" : {
"expireAt" : 1
},
"name" : "expireAt_1",
"background" : true,
"expireAfterSeconds" : 86400
},
{
"v" : 2,
"key" : {
"paidOn" : 1
},
"name" : "paidOn_1",
"background" : true
}
]
So mongo was deleting the new docs where the expireAt field had an old date.
To fix it I ran await Order.syncIndexes(); in a script. This cleared the index to [ { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_" } ]. This is fine for my purpose. But the index isn't the same as the 1st database. The paidOn key is no longer indexes.
What I thought helped but didn't
At first I thought the issue was due to the large size of the jsonDocs.
I had objects with fields that had large base64 strings for images. These were placeholders, supposed to be replaced with http urls for the images.
After I removed the base64 strings I was able to upload the documents. I thought this helped but it was just speeding things up. It takes 1 minute for mongo to check up on expired docs.
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>.$.
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
In my database, I currently have two kinds of objects, users and jobs. I am already storing userIDs in jobs. Do I also need to store jobIDs in each user?
A typical user:
"-JqzUjcOfddBNd_HtjKb" : {
"contact" : {
"-JqzWcIyD77ZwatEKALp" : {
"email" : "someguy#yahoo.com"
},
"-JqzWrtyni3ZGOKooNF7" : {
"email" : "someguy#outlook.com"
}
},
"country" : "234",
"cv" : "https://linktourl.com",
"dateAdded" : 1433436879708,
"ethnicity" : "0",
"firstName" : "John",
"lastName" : "Smith",
"notes" : {
"-JqzhvtNcueUsPr8xwh8" : {
"date" : 1433440599702,
"user" : "iwrotethisnote#example.com",
"value" : "interested in job; need to interview"
}
},
"roles" : [ true ]
},
And a typical job:
"-Jqz5mOr-DmLcxmTVRPi" : {
"age" : [ "2" ],
"city" : "0",
"clientID" : "-Jqz7goZC76vl94VT0dq",
"dateModified" : 1433431226687,
"longDesc" : "Teacher should have experience",
"notes" : {
"-Jqz6SO74OJOESwOVfkG" : {
"date" : 1433430513294,
"notevalue" : "bill spoke with her",
"userid" : "name#gmail.com"
}
},
"schedule" : "Evening or weekend",
"status" : "needDetail",
"subjects" : {
"15" : true,
"42" : true
},
"title" : "She wants a native speaker"
},
As you can see, jobs have a field for clientID, which is a foreign key (so to speak) of a user's id. When I access a user's information, I want to know those jobs that they are associated with (i.e. for which they have supplied their client ID). How to do this in Firebase?
Should I:
Update both objects, and keep an array of jobIDs in the client object?
Query all jobs, then pass in those that have the user's ID as the client ID. Something like:
// user controller
var jobs = [];
jobsRef.$on('value', function(snapshot) {
snapshot.val().forEach(function(job) {
if (job.clientID = $scope.userID) {
jobs.push(job);
}
}
}
Option 1 makes the data redundant (which I guess is ok, because of the emphasis on denormalization), but it also makes it more likely to become out of sync, for example if one of the two updates I would be making fails.
Option 2 seems like it would run a lot slower.
You can use equalTo() of firebase query
var jobsRef = new Firebase();
jobsRef.orderByChild("clientID").equalTo($scope.userID)
.on("value", function(snapshot) {
console.log(snapshot.key());
});
For more info, please check out Query.equalTo()