mongodb find documents with fields with a data doesn't exist - mongodb

I have mongodb document like
{
"_id" : ObjectId("543d563bde1e58511c264340"),
...some fields ...
"pref" : [
{
"user_id" : 1,
"value" : 0.56
}
]
}
How can I find all the documents where pref does not contain an entry with user_id :1 ?

It's a little unclear what you're looking for here. If you want to find all entries where user_id has any other value than '1', then you'd want:
db.collection.find({"pref.user_id": {'$ne': 1}})
If you're looking for documents where the 'user_id' field doesn't exist at all:
db.collection.find({"pref.user_id": {'$exists': 0}})
Keep in mind, though the behavior of both of these queries on a nested array. What you're actually going to get is all the documents where any of the objects in the 'pref' array matches the specified condition.

Related

Multiple update in a document in MongoDB

I am trying to update multiple nested documents in a document in mongoDB.
Say my data is:
{
"_id" : "ObjectId(7df78ad8902c)",
"title" : "Test",
"img_url" : "[{s: 1, v:1}, {s: 2, v: 2}, {s: 3, v: 3}]",
"tags" : "['mongodb', 'database', 'NoSQL']",
"likes" : "100"
}
I want to update v to 200 for s = 1 and s= 2 in img_url list.
It is easy to update v for any single s.
Is there any way to update multiple documents satisfying some criteria.
I tried:
db.test.update({ "_id" : ObjectId("7df78ad8902c"), "img_url.s": {$in : ["1", "2"]}}, {$set: { "img_url.$.v" : 200 } });
and
db.test.update({ "_id" : ObjectId("7df78ad8902c"), "img_url.s": {$in : ["1", "2"]}}, {$set: { "img_url.$.v" : 200 } }, {mulit: true});
Some sources are suggesting it is not possible to do so.
Multiple update of embedded documents' properties
https://jira.mongodb.org/browse/SERVER-1243
Am I missing something ?
For the specific case/example you have here. You are specifying an _id which means you are to update only one with that specific _id.
to update img_url try without the _id; something like this:
db.test.update({}, {"$set":{"img_url.0":{s:1, v:400}}}, {multi:true})
db.test.update({}, {"$set":{"img_url.1":{s:2, v:400}}}, {multi:true})
0 and 1 in img_url are the array indexes for s:1 and s:2
in order to update based on specific criteria you need to set the attribute you need on the first argument. say for example, to update all documents that have likes greater than 100 increment by 1 you do (assuming likes type is int...):
db.people.update( { likes: {$gt:100} }, {$inc :{likes: 1}}, {multi: true} )
hope that helps

Mongo query existence and value of embedded object property

A 5,000 to 10,000 record Mongo collection contains:
{
"_id" : ObjectId("55e16c34c78b04f43f2f55a0"),
"appID" : NumberInt(4830800),
"topics" : {
"test1" : 1.440899998865E12,
"test2" : 1.440899998865E12,
"test3" : 1.440899998865E12,
"test4" : 1.440899998865E12
},
}
I need to query for records that contain a specified property name in the topics field and where the value of the specified name is greater than or equal to a given number.
something like
find({"topics.test1": { $gte: 1440825382535 }})
This query works as expected, returning a set of records that have a test1 property with a test1 value >= 1440825382535
If I create a simple index on the topics field explain() says that no index is used for the query (understandably).
The set of property names that may be searched for is not predefined. The query is dynamically built based on names that are found elsewhere.
Is there a way to index this table to speed up queries? The full scan query takes quite a bit of time to run (on the order of 1.5 seconds).
To make this type of data indexable, you need to change the schema to make topics an array and move the dynamic test1, test2, etc. keys into values.
So something like:
{
"_id" : ObjectId("55e16c34c78b04f43f2f55a0"),
"appID" : NumberInt(4830800),
"topics" : [
{name: "test1", value: 1.440899998865E12},
{name: "test2", value: 1.440899998865E12},
{name: "test3", value: 1.440899998865E12},
{name: "test4", value: 1.440899998865E12}
]
}
Then your query changes to:
find({topics: {$elemMatch: {name: 'test1', value: {$gte: 1440825382535}}}})
Which you can support with an index of:
{'topics.name': 1, 'topics.value': 1}
Was a little confused at what you were trying to do, but maybe something like this?
find({"topics.test1": {$exists: true}, { $gte: 1440825382535 }})

MongoDB not using compound index on '_id'

I have a collection in MongoDB which has following documents.
/* 0 */
{
"T" : [
374135056604448742
],
"_id" : {
"#" : 7778532275691,
"ts" : ISODate("2013-07-26T02:25:00Z")
}
}
/* 1 */
{
"T" : [
1056188940167152853
],
"_id" : {
"#" : 34103385525388,
"ts" : ISODate("2013-07-30T03:00:00Z")
}
}
/* 2 */
{
"T" : [
1056188940167152853
],
"_id" : {
"#" : 34103385525388,
"ts" : ISODate("2013-07-30T03:18:00Z")
}
}
Now, I'm trying to query some documents with following query.
db.entries.find({
'_id.ts': {'$gte': beginTS, '$lte': endTS},
'_id.#' : 884327843395156951
}).hint([('_id', 1)]).explain()
According to my understanding, since _id is a compound field, and Mongo always maintains a index on _id, hence to answer above query, Mongo should have used the index on '_id'. However, the answer to the above query is as following:
{u'allPlans': [{u'cursor': u'BtreeCursor _id_',
u'indexBounds': {u'_id': [[{u'$minElement': 1}, {u'$maxElement': 1}]]},
u'n': 2803,
u'nscanned': 4869528,
u'nscannedObjects': 4869528}],
u'cursor': u'BtreeCursor _id_',
u'indexBounds': {u'_id': [[{u'$minElement': 1}, {u'$maxElement': 1}]]},
u'indexOnly': False,
u'isMultiKey': False,
u'millis': 128415,
u'n': 2803,
u'nChunkSkips': 0,
u'nYields': 132,
u'nscanned': 4869528,
u'nscannedAllPlans': 4869528,
u'nscannedObjects': 4869528,
u'nscannedObjectsAllPlans': 4869528,
u'scanAndOrder': False,
As it can be observed, MongoDB is doing an entire scan of DB to find just handful of documents. I don't know what the hell is wrong here.
I tried changing the order of query, but same result. I have no idea what is happening here. Any help if deeply appreciated.
UPDATE
I understood the nuance here. The _id is not a compound index, it's a mere exact index. This means that if _id is a document then irrespective of the structure of document and how many nested attrs or sub-documents it may have, the _id index will only contain one entry for the _id field. This entry is suppose to be hash of _id document and will be maintained unique.
You are using an object as a key, but you're not using a compund index here.
The _id index is a bit special, because it is created automatically and is always unique. Normally, the _id index is an ObjectId, a UUID or maybe an integer or a string that contains some kind of hash. MongoDB supports complex objects as keys. However, to MongoDB, this is still just a document. It can be compared to other documents, and documents that have the same fields and values will be equal. But since you didn't create the index keys (and you can't create that index manually), MongoDB has no idea that it contains a field # and a field ts.
A compound index, on the other hand, refers to the fields of a document explicitly, e.g. {"product.quantity" : 1, "product.created" : -1}. This must be specified when the index is created.
It seems you're trying to basically store a timestamp in your primary key. MongoDB's ObjectId already contains a timestamp, so you can do date-based range queries on ObjectIds directly.

matching fields internally in mongodb

I am having following document in mongodb
{
"_id" : ObjectId("517b88decd483543a8bdd95b"),
"studentId" : 23,
"students" : [
{
"id" : 23,
"class" : "a"
},
{
"id" : 55,
"class" : "b"
}
]
}
{
"_id" : ObjectId("517b9d05254e385a07fc4e71"),
"studentId" : 55,
"students" : [
{
"id" : 33,
"class" : "c"
}
]
}
Note: Not an actual data but schema is exactly same.
Requirement: Finding the document which matches the studentId and students.id(id inside the students array using single query.
I have tried the code like below
db.data.aggregate({$match:{"students.id":"$studentId"}},{$group:{_id:"$student"}});
Result: Empty Array, If i replace {"students.id":"$studentId"} to {"students.id":33} it is returning the second document in the above shown json.
Is it possible to get the documents for this scenario using single query?
If possible, I'd suggest that you set the condition while storing the data so that you can do a quick truth check (isInStudentsList). It would be super fast to do that type of query.
Otherwise, there is a relatively complex way of using the Aggregation framework pipeline to do what you want in a single query:
db.students.aggregate(
{$project:
{studentId: 1, studentIdComp: "$students.id"}},
{$unwind: "$studentIdComp"},
{$project : { studentId : 1,
isStudentEqual: { $eq : [ "$studentId", "$studentIdComp" ] }}},
{$match: {isStudentEqual: true}})
Given your input example the output would be:
{
"result" : [
{
"_id" : ObjectId("517b88decd483543a8bdd95b"),
"studentId" : 23,
"isStudentEqual" : true
}
],
"ok" : 1
}
A brief explanation of the steps:
Build a projection of the document with just studentId and a new field with an array containing just the id (so the first document it would contain [23, 55].
Using that structure, $unwind. That creates a new temporary document for each array element in the studentIdComp array.
Now, take those documents, and create a new document projection, which continues to have the studentId and adds a new field called isStudentEqual that compares the equality of two fields, the studentId and studentIdComp. Remember that at this point there is a single temporary document that contains those two fields.
Finally, check that the comparison value isStudentEqual is true and return those documents (which will contain the original document _id and the studentId.
If the student was in the list multiple times, you might need to group the results on studentId or _id to prevent duplicates (but I don't know that you'd need that).
Unfortunately it's impossible ;(
to solve this problem it is necessary to use a $where statement
(example: Finding embeded document in mongodb?),
but $where is restricted from being used with aggregation framework
db.data.find({students: {$elemMatch: {id: 23}} , studentId: 23});

how to query child objects in mongodb

I'm new to mongodb and am trying to query child objects. I have a collection of States, and each State has child Cities. One of the Cities has a Name property that is null, which is causing errors in my app. How would I query the State collections to find child Cities that have a name == null?
If it is exactly null (as opposed to not set):
db.states.find({"cities.name": null})
(but as javierfp points out, it also matches documents that have no cities array at all, I'm assuming that they do).
If it's the case that the property is not set:
db.states.find({"cities.name": {"$exists": false}})
I've tested the above with a collection created with these two inserts:
db.states.insert({"cities": [{name: "New York"}, {name: null}]})
db.states.insert({"cities": [{name: "Austin"}, {color: "blue"}]})
The first query finds the first state, the second query finds the second. If you want to find them both with one query you can make an $or query:
db.states.find({"$or": [
{"cities.name": null},
{"cities.name": {"$exists": false}}
]})
Assuming your "states" collection is like:
{"name" : "Spain", "cities" : [ { "name" : "Madrid" }, { "name" : null } ] }
{"name" : "France" }
The query to find states with null cities would be:
db.states.find({"cities.name" : {"$eq" : null, "$exists" : true}});
It is a common mistake to query for nulls as:
db.states.find({"cities.name" : null});
because this query will return all documents lacking the key (in our example it will return Spain and France). So, unless you are sure the key is always present you must check that the key exists as in the first query.