MongoDB error on find() - mongodb

I'm just started to create my MongoDB schema (im newbie on mongo), my schema looks like this example:
users : [
{
_id : 11,
name : "jhon",
mail : "jhon#mail.com"
},
{
_id : 12,
name : "smith",
mail: "smith#mail.com"
}
]
I want to retrieve certain user information, bsaed on a _id...
For example:
db.users.find({"users._id" : 11})
And then my result should be:
_id : 11,
name : "jhon",
mail : "jhon#mail.com"
but the result i get is all the users of the users document, i just one one, the one who have certaint _id.
How can i write a query for that?.
And just in case, my schema is designed properly?
Thanks for the help! :)

Try with this
db.users.find({"_id" : 11})

You ask whether your schema is designed properly. That of course depends on your use case, but the answer is probably... no. Instead of a single document with an array, probably what you want is multiple documents. For example, I inserted some documents using the mongo shell:
> db.users.insert({_id: 11, name: "jhon", mail: "jhon#mail.com"})
> db.users.insert({_id: 12, name: "smith", mail: "smith#mail.com"})
Then here's a query typed at the mongo shell that will return all documents:
> db.users.find()
{ "_id" : 11, "name" : "jhon", "mail" : "jhon#mail.com" }
{ "_id" : 12, "name" : "smith", "mail" : "smith#mail.com" }
And if you have the _id, you can query just that document:
> db.users.findOne({_id: 11})
{ "_id" : 11, "name" : "jhon", "mail" : "jhon#mail.com" }
Arrays have their uses, but the kind of thing you are doing here is usually done with multiple documents in a collection, rather than sub-documents within an array.
Does this help?

Related

Using $last on Mongo Aggregation Pipeline

I searched for similar questions but couldn't find any. Feel free to point me in their direction.
Say I have this data:
{ "_id" : ObjectId("5694c9eed4c65e923780f28e"), "name" : "foo1", "attr" : "foo" }
{ "_id" : ObjectId("5694ca3ad4c65e923780f290"), "name" : "foo2", "attr" : "foo" }
{ "_id" : ObjectId("5694ca47d4c65e923780f294"), "name" : "bar1", "attr" : "bar" }
{ "_id" : ObjectId("5694ca53d4c65e923780f296"), "name" : "bar2", "attr" : "bar" }
If I want to get the latest record for each attribute group, I can do this:
> db.content.aggregate({$group: {_id: '$attr', name: {$last: '$name'}}})
{ "_id" : "bar", "name" : "bar2" }
{ "_id" : "foo", "name" : "foo2" }
I would like to have my data grouped by attr and then sorted by _id so that only the latest record remains in each group, and that's how I can achieve this. BUT I need a way to avoid naming all the fields that I want in the result (in this example "name") because in my real use case they are not known ahead.
So, is there a way to achieve this, but without having to explicitly name each field using $last and just taking all fields instead? Of course, I would sort my data prior to grouping and I just need to somehow tell Mongo "take all values from the latest one".
See some possible options here:
Do multiple find().sort() queries for each of the attr values you
want to search.
Grab the original _id of the $last doc, then do a findOne() for each of those values (this is the more extensible option).
Use the $$ROOT system variable as shown here.
This wouldn't be the quickest operation, but I assume you're using this more for analytics, not in response to a user behavior.
Edited to add slouc's example posted in comments:
db.content.aggregate({$group: {_id: '$attr', lastItem: { $last: "$$ROOT" }}}).

mongodb select using association

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" }

MongoDB Why this error : can't append to array using string field name: comments

I have a DB structure like below:
{
"_id" : 1,
"comments" : [
{
"_id" : 2,
"content" : "xxx"
}
]
}
I update a new subdocument in the comments feild. It is OK.
db.test.update(
{"_id" : 1, "comments._id" : 2},
{$push : {"comments.$.comments" : {_id : 3, content:"xxx"}}}
)
after that the DB structure:
{
"_id" : 1,
"comments" : [
{
"_id" : 2,
"comments" : [
{
"id" : 3,
"content" : "xxx"
}
],
"content" : "xxx"
}
]
}
But when I update a new subdocument in the comment field that _id is 3, There is a error:
db.test.update(
{"_id" : 1, "comments.comments.id" : 3},
{$push : {"comments.comments.$.comments" : {id : 4, content:"xxx"}}}
)
error message:
can't append to array using string field name: comments
Well, it makes total sense if you think about it. MongoDb has the advantage and the disadvantage of solving magically certain things.
When you query the database for a specific regular field like this:
{ field : "value" }
The query {field:"value"} makes total sense, it wouldn't in case value is part of an array but Mongo solves it for you, so in case the structure is:
{ field : ["value", "anothervalue"] }
Mongo iterates through all of them and matches "value" into the field and you don't have to think about it. It works perfectly.. at only one level, because it's impossible to guess what you want to do if you have multiple levels
In your case the first query works because it's the case in this example:
db.test.update(
{"_id" : 1, "comments._id" : 2},
{$push : {"comments.$.comments" : {_id : 3, content:"xxx"}}}
)
Matches _id in the first level, and comments._id at the second level, it gets an array as a result but Mongo is able to solve it.
But in the second case, think what you need, let's isolate the where clause:
{"_id" : 1, "comments.comments.id" : 3},
"Give me from the main collection records with _id:1" (one doc)
"And comments which comments inside have and id=3" (array * array)
The first level is solved easily, comments.id, the second is not possible due comments returns an array, but one more level is an array of arrays and Mongo gets an array of arrays as a result and it's not possible to push a document into all the records of the array.
The solution is to narrow your where clause to obtain an unique document in comments (could be the first one) but it's not a good solution because you never know what is the position of the document you're looking for, using the shell I think the only option to be accurate is to do it in two steps. Check this query that works (not the solution anyway) but "solves" the multiple array part fixing it to the first record:
db.test.update(
{"_id" : 1, "comments.0.comments._id" : 3},
{$push : {"comments.0.comments.$.comments" : {id : 4, content:"xxx"}}}
)

Retrive only part of a subdocument in mongodb

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.

Array query and return

My first adventure into Mongo. Please save me some time by answering the following. This is the schema.
"_id" : 1,
"FullName" : "Full Name",
"Email" : "email#email.com",
"FacebookId" : NumberLong(0),
"LastModified" : ISODate("2012-04-11T09:26:10.955Z"),
"Connections" : [{
"_id" : 7,
"FullName" : "Fuller name",
"Email" : "connections#email.com",
"FacebookId" : NumberLong(0),
"LastModified" : ISODate("0001-01-01T00:00:00Z")
},
....
Given an id of a single top user, i'd like to return all of the Emails in the Connections array, and preferably, just the emails. What's the querystring? Much obliged!
You can't get only values from the sub-objects in MongoDB.
If you do a query like this:
db.test.find({"_id": 1}, {"Connections.Email":1});
you will get this kind of response:
{
"_id": 1,
"Connections" : [ {"Email":"connections#email.com"},
{"Email":"foo#example.com"} ]
}
This is the closest you can get with a simple query and field selection from MongoDB.
You can then filter out the e-mails values in your code with a simple foreach.