I have two collections, user_logs and users, user_logs documents have user_id field so I need some data from user_logs but in the same query I want to check if some other field from user related to the current user_log is empty. How should I do this?
A query can only access one collection at a time. Mongodb doesn't support joins.
They that's why they recommend that you embed the referenced data inside the document.
If the logs documents for each user isn't too big, then you can change the embed that info inside the user collection.
Giving you something like this.
Embedded User Collection:
{
user_id : "uid1",
logs : [
{ message : "Error: System shutdown", date : "2014-11-11" },
{ message : "Error: System shutdown", date : "2014-11-13" }
]
}
However, if you want to keep your current structure then you're going to have to perform two queries to find related info between the users and user_logs collections.
Example
db.user_logs.insert([
{ _id : "ul1", log : "code 1", user_id : "u1" },
{ _id : "ul2", log : "code 2", user_id : "u1" }
]);
db.users.insert([
{ _id : "u1", name : "bob", user_logs_id : "ul1" },
{ _id : "u2", name : "smith", user_logs_id : "ul2" }
]);
var userId = db.user_logs.findOne({}).user_id;
db.users.findOne({ _id : userId })
//outputs
{ "_id" : "u1", "name" : "bob", "user_logs_id" : "ul1" }
Related
I have a mongo DB aggregation pipeline that performs the following steps:
Sorts a list of user stats objects by timestamp
Groups the results by user ID
Sorts by a specified stat's name
Pages the results via skip and limit stages
In plain English, this pipeline returns a page from a list of user stats sorted by a specified stat. Each user can have multiple stats object, so I group to return only the most recent stats object for each user.
In Mongo Shell, this looks like:
db.getCollection("stats").aggregate(
[
{ "$sort" : { "Timestamp" : -1.0 } },
{
"$group" : {
"_id" : "$UserId",
"UserId" : { "$last" : "$UserId" },
"StatsOverall" : { "$last" : "$StatsOverall" },
"Timestamp" : { "$last" : "$Timestamp" }
}
},
{ "$sort" : { "StatsOverall.Rank" : -1.0 } },
{ "$skip" : specifiedPageNumber },
{ "$limit" : specifiedNumResultsPerPage }
]
);
This works fine.
I now want to modify this query to be able to search the user by name, and get back the entire page that user is contained on. (This is for a leaderboard). So, if the user is on page 5 of the leaderboard, I want to return the entirety of page 5.
However, I'm having trouble seeing a solution that doesn't require me to either load all of the users in to memory and page them there (awful idea), or go back and forth to the database iterating through pages (almost as awful).
Is there some way I can modify my aggregation pipeline to do all this at the database level?
EDIT: As requested, added some sample data and the expected result.
Sample data looks something like this... I've omitted some fields that aren't relevant. The initial data is a collection of user's stats, where each user can have more than one object. My existing pipeline returns the 1 most recent stats object for each user sorted by a specified stat name.
{
"_id" : "5c611e71ab0ffc430410e0ba",
"UserId" : "5c611e71ab0ffc430410e0ba",
"StatsOverall" : {
"Rank" : NumberInt(1000),
"GamesLost" : NumberInt(30),
"GamesWon" : NumberInt(50)
}
"Timestamp" : "2019-02-10T21:35:06.599Z"
}
// ----------------------------------------------
{
"_id" : "5c6238658966ae5860795879",
"UserId" : "5c6238658966ae5860795879",
"StatsOverall" : {
"Rank" : NumberInt(413),
"GamesLost" : NumberInt(2),
"GamesWon" : NumberInt(141),
},
"Timestamp" : "2019-02-10T21:35:06.599Z"
}
// many objects like this
The expected result looks like this:
{
"_id" : "5c611e71ab0ffc430410e0ba",
"UserId" : "5c611e71ab0ffc430410e0ba",
"StatsOverall" : {
"Rank" : NumberInt(1000),
"GamesLost" : NumberInt(30),
"GamesWon" : NumberInt(50)
}
"Timestamp" : "2019-02-10T21:35:06.599Z"
}
It returns the exact same type of object, sorted the same way as the existing pipeline, however I want to return only the page the the user is on. In the example result, assume the page size is just 1 result per page. So, the result would contain the 1 page that the user with the given UserId is on. In my sample result, that ID would be 5c611e71ab0ffc430410e0ba.
I'm used to working with firebase where I can access a document directly by fetching data from the db like so.
db.collection('collectionName/documentID').get();
I can't seem to find any documentation regarding doing something similar in mongodb. Do I have to use a find query to grab data from a mongodb or have I missed something? Thanks
I'm thinking
const collection = db.collection('collectionName');
collection.findOne({_id: ObjectId('documentID'); });
Since mongo consolse is an interactive javascript shell, One way would be to create a method similar to this:
function collectionNameGet(idToFind) {
return db.collection.find({_id: idToFind });
}
In the mongo shell you can directly get it as below:
db.st4.find({"_id" : "1234"})
Result set:
{ "_id" : "1234", "raw" : { "meas" : { "meas1" : { "data" : "blabla" }, "mesa2" : { "data" : "foo" } } } }
Or by default mongo id as:
db.st1.find({"_id" : ObjectId("5c578d57ce9ba4a066ca2fa4")})
{ "_id" : ObjectId("5c578d57ce9ba4a066ca2fa4"), "name" : "Just a name", "users" : [ "user1", "user2" ] }
For display the result in pretty format
db.st1.find({"_id" : ObjectId("5c578d57ce9ba4a066ca2fa4")}).pretty()
Result set:
{
"_id" : ObjectId("5c578d57ce9ba4a066ca2fa4"),
"name" : "Just a name",
"users" : [
"user1",
"user2"
]
}
Here st4 is my collection name in the database test, so once you are on mongo shell do the below steps before above query:
use test
db.st1.insert({"name" : "Just a name", "users" : [ "user1", "user2" ] })
and then you can query by default _id generated mongo, you can simply make a query to get the recently added documents in the collection st1 as below:
db.st1.find().sort({_id:-1}).limit(1)
Hope this will help you out to do some basic query on mongo shell
I am trying to find a way to filter the records in Mongo db using Spring query.
Here is the scenario, let's see I have an Activity entity/document. One of the fields is a list of names. I want to see if I can get all the records that the names field includes get given value, let's say "Joker".
For example, my json in Mongo is
Activity 1 -
{
"_id" : ObjectId("52c14eb92f7ceb854e445354"),
...
"names" : [{
"username" : "username1",
"realname" : "Super Man"
}, {
"username" : "username2",
"realname" : "Iron Man"
}]
}
Activity 2 -
{
"_id" : ObjectId("52c14eb92f7ceb854e445355"),
...
"names" : [{
"username" : "username3",
"realname" : "Bat Man"
}, {
"username" : "username4",
"realname" : "Joker"
}]
}
And I expect the query will let me get Activity 2 only.
Also, if possible, I prefer to use spring Mongo query in my code. Thanks in advance.
Try
db.collection.find({"names.realname": "Joker"});
I never used Spring query but should be something like
Query query = new Query();
query.addCriteria(Criteria.where("names.realname").is("Joker"));
List<MyClass> users = mongoTemplate.find(query, MyClass.class);
I have the following items in my collection:
> db.test.find().pretty()
{ "_id" : ObjectId("532c471a90bc7707609a3d4f"), "name" : "Alice" }
{
"_id" : ObjectId("532c472490bc7707609a3d50"),
"name" : "Bob",
"partner_type1" : {
"status" : "rejected"
}
}
{
"_id" : ObjectId("532c473e90bc7707609a3d51"),
"name" : "Carol",
"partner_type2" : {
"status" : "accepted"
}
}
{
"_id" : ObjectId("532c475790bc7707609a3d52"),
"name" : "Dave",
"partner_type1" : {
"status" : "pending"
}
}
There are two partner types: partner_type1 and partner_type2. A user cannot be accepted partner in the both of types. But he can be a rejected partner in partner_type1 but accepted in the another, for example.
How can I build Mongo query that fetches the users that can become partners?
When your user can only be accepted in one partner-type, you should turn it around: Have a field accepted_as:"partner_type1" or accepted_as:"partner_type2". For people who aren't accepted yet, either have no such field or set it to null.
In both cases, your query to get any non-accepted will then be:
{
data.accepted_as: null
}
(null matches both non-existing fields as well as fields explicitly set to null)
For me the logical schema would be this:
"partner : {
"type": 1,
"status" : "rejected"
}
At least that keeps the paths consistent between documents.
So if you want to stay away from using mapReduce type methods to find out "which field" it is on, and otherwise use plain queries and the aggregation pipeline, then don't vary field paths on documents. If you alter the "data" then that is the most consistent form.
I have some data that looks like this:
{
"_id" : "5227aa5d9881d31cd3aa0e78",
"Message" : "This is a message 5:47 PM",
"IssuedAt" : ISODate("2013-09-04T21:47:09.932Z"),
"Users" : [
{
"_id" : "dhBHuZL9M+hqtKIx14iu",
"IsRead" : true
},
{
"_id" : "SOMSOMOMODJFJDFKJKDJF",
"IsRead" : false
}
]
}
and I was hoping retrieve the following about one user:
{
"_id" : "5227aa5d9881d31cd3aa0e78",
"Message" : "This is a message 5:47 PM",
"IssuedAt" : ISODate("2013-09-04T21:47:09.932Z"),
"IsRead" : false
}
I tried this but it will only return the record with the whole subdocument:
db.collection.find({"Users": {$elemMatch: {"_id": 'dhBHuZL9M+hqtKIx14iu'}}}, {"Message": 1, "Users.$.IsRead": 1}).pretty()
Is there a way to get what I am looking for without using aggregate?
In the current requirement , i think you have to redesign your schema . Schema has to be more efficient for your query. May be Schema for the current requirement will be to have User Collection
{
"_id": "dhBHuZL9M+hqtKIx14iu" ,
"Name" : "Test",
"ReadMessages" [{}],
"UnReadMessage" [{}]
}
Now for any users you can get the read messages and unread message very easy . One update statement you have to fire to move the message from UnRead to read. But it will happen once and rest read will be very fast. I know it will not solve your problem but may help you wish to change the schema.