How do I include 'undefined' in the array returned by mongodb's distinct() operator when the field does not exist? - mongodb

I would like to get an array of distinct values of a certain field in a mongodb collection. However, the field is optional and if any document does not have the field, I want the array to include the value 'undefined' (or null or any indication). The distinct operator seems to ignore any documents that do not have the field, rather than include 'undefined' in the array of distinct values. Does anyone know how I can override this behavior?
Documentation for the distinct operator: http://docs.mongodb.org/manual/reference/method/db.collection.distinct/
I am getting around this now by making a second db call that counts the items with this field equal to undefined, but I would like to do it in one call.

You can do this using aggregation framework:
db.collection.aggregate(
{$group:{_id:"$myField"}}
}
It will include null value if any documents don't have the field, or have it as null value.

I really don't see a way to do what you're describing in one call.
However, to optimize what you're doing now:
I think you're doing something like:
db.myCollection.count({$not: {$exists: myfield}})
If your collection is large, this can get slow. You might consider
db.myCollection.findOne({$not: {$exists: myfield}})
If no documents exist without "myfield", it will return null.

Related

Why does db.collection.find({}).maxTimeMS(100) return everything, but db.collection.find({}, {$maxTimeMS: 100}) only returns object IDs?

I'm using MongoDB 2.6.8. According to the $maxTimeMS reference, these two queries should behave identically:
> db.collection.find({}).maxTimeMS(100)
> db.collection.find({}, {$maxTimeMS: 100})
The first query does exactly what I want, but the second query restricts only returns the object IDs of the documents. I tried increasing $maxTimeMS to 100000000 and there was no change in behavior.
Why am I getting different results for these two commands?
You found a bug in the documentation.
The reason that db.collection.find({}, {$maxTimeMS: 100}) returns only the _id of each object is because mongoDB is interpreting the {$maxTimeMS: 100} portion of the query as a projection.
So it thinks you want to see all the documents and you want to see the fields _id and the field $maxTimeMS. Of course, none of your documents have a $maxTimeMS field, so they only show the _id.
The proper way to perform the query you want without the shortcut is:
db.collection.find({ $query: {}, $maxTimeMS: 100 })

Distinguish array from single value in a document

I have two type of documents in a mongodb collection:
one where key sessions has a simple value:
{"sessions": NumberLong("10000000000001")}
one where key sessions has an array of values.
{"sessions": [NumberLong("10000000000001")]}
Is there any way to retrieve all documents from the second category, ie. only documents whose value is an arary and not a simple value?
You can use this kind of query for that:
db.collectionName.find( { $where : "Array.isArray(this.sessions)" } );
but you'd better convert all the records to one type to keep the things consistent.
This code can be simple like this:
db.c.find({sessions:{$gte:[]}});
Explanation:
Because you only want to retrieve documents whose sessions data type is array, and by the feature of $gte (if data types are different between tow operands, it returns false; Double, Integer32, Integer64 are considered as same data type.), giving an empty array as the opposite operand will help to retrieve all results by required.
Also , $gt, $lt, $lte for standard query (attention: different behaviors to operaors with same name in expression of aggregation pipeline) have the same feature. I proved this by practice on MongoDB V2.4.8, V2.6.4.

meteor, find whether user exists in mongodb document array or not?

This is my meteor code to search whether user exist in the array or not
var u_exist=Polls_Coll.findOne( {option1:{$elemMatch:{ids:"xxx"}}} );
My question is, How to know whether the statement returning something or not(user exist or not)
$elemMatch will return only where one of the conditions supplied actually finds a match in the array. So if you don't get a document back then there was no match.
Also findOne is a single document. Modifiers such as .count() will not work on that. If you have more documents to be expected use find intstead. Also findOne not not make much sense without applying a unique identifier such as _id in the query. Without that you are almost certainly not getting what you want.
While useful for your purpose, findOne is not a good match with the $elemMatch operator. The reasoning is you can possibly get multiple results of the same document having the same set of array elements that matched the condition that you gave.
Buyer beware.

In Mongodb, how to retrieve the subset of an object that matches a condition?

What I'm trying to do:
Filter a field of a collection that matches a given condition. Instead of returning every item in the field (which is an array of items), I only want to see matched items.
Similar to
select items from test where items.histPrices=[10,12]
It is also similar to what's found on the mongodb website here: http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields
Here's what I have been trying:
db.test.save({"name":"record", "items":[{"histPrices":[10,12],"name":"stuff"}]})
db.test.save({"name":"record", "items":[{"histPrices":[10,12],"name":"stuff"},
{"histPrices":[12,13],"name":"stuff"},{"histPrices":[11,14],"name":"stuff"}]})
db.test.find({},{"name":1,"items.histPrices":[10, 12]})
It will return all the objects that have a match for items.histPrices:[10,12], including ALL of the items in items[]. But I don't want the ones that don't match the condition.
From the comments left on Mongodb two years ago, the solution to get only the items with that histPrices[10,12] is to do it with javascript code, namely, loop through the result set and filter out the other items.
I wonder if there's a way to do that with just the query.
Your find query is wrong
db.test.find({},{"name":1,"items.histPrices":[10, 12]})
Your condition statement should be in the first part of the find statement.In your query {} means fetch all documents similar to this sql
select items from test (no where clause)
you have to change your mongodb find to
db.test.find({"items.histPrices":[10, 12]},{"name":1})
make it work
since your items is an array and if you wanted to return only the matching sub item, you have to use positional operator
db.test.find({"items.histPrices":[10, 12]},{"name":1,'items.$':1})
When working with arrays Embedded to the Document, the best approach is the one suggested by Chien-Wei Huang.
I would just add another aggregation, with the $group (in cases the document is very long, you may not want to retrieve all its content, only the array elements) Operator.
Now the command would look like:
db.test.aggregate({$match:{name:"record"}},
{$unwind:"$items"},
{$match {"items.histPrices":[10, 12]}},
{$group: {_id: "$_id",items: {$push: "$items"}}});)
If you are interested to return only one element from the array in each collection, then you should use projection instead
The same kind of issue solved here:
MongoDB Retrieve a subset of an array in a collection by specifying two fields which should match
db.test.aggregate({$unwind:"$items"}, {$match:{"items.histPrices":[10, 12]}})
But I don't know whether the performance would be OK. You have to verify it with your data.
The usage of $unwind
If you want add some filter condition like name="record", just add another $march at first, ex:
db.test.aggregate({$match:{name:"record"}}, {$unwind:"$items"}, {$match:{"items.histPrices":[10, 12]}})
https://jira.mongodb.org/browse/SERVER-828
Get particular element from mongoDB array
MongoDB query to retrieve one array value by a value in the array

Mongo - dot notation works like $or? Can't provide multiple parameters?

I tried to update an existing document with two dot notation parameters, my query:
{ _id: "4eda5...", comments._id: "4eda6...", comments.author: "john" }
my update was:
{ "comments.$.deleted": true }
However, weirdly enough, when I passed a non-existent combination of comment id+author, it just updated the first matching comment by that author.
Any ideas why that's happening?
EDIT: C# Code sample
var query = Query.And(Query.EQ("_id", itemId), Query.EQ("cmts._id", commentId));
if (!string.IsNullOrEmpty(author))
query = Query.And(query, Query.EQ("cmts.Author", author));
var update = Update.Set("cmts.$.deleted", true);
var result = myCol.Update(query, update, UpdateFlags.None, SafeMode.True);
You want $elemMatch if you want the _id and author to be in the same comment. Really, your query doesn't make much sense including the author as the id should be as unique as you can get, no?
It is based on the first matching array element which replaces the "$" in for the update.
This is working by design. It is similar to an or since it can find a document which both has the _id and an author that match in any of the array elements.
The query is not working the way you are expecting it to. Basically, when using the $ positional notation you need to make sure that your query only has one clause that queries an array, otherwise it is ambiguous which of the two array comparisons the $ should refer to.
In your case, you are asking for a document where:
The _id is equal to some value
The comments array contains some document where the _id is equal to some value
The comments array contains some document where the author is equal to some value
Nothing in your query says that 2. and 3. need to be satisfied by the same document.
So even though you are using a non-existent combination of comment._id and comment.author, your comment array does have at least one entry where the _id is equal to your search value and some other entry (just not the same one) where the author is equal to your search value.
Since the author was the last one checked, that's what set the value of the $, and that's why that array element got updated.