Distinguish array from single value in a document - mongodb

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.

Related

Is it possible to search within a MongoDB field with type of Date or Int?

I have a search field within my application that I want users to enter a search term into and it searches across various fields in each Mongo document. Naturally, I can search data within the fields that are of type String (currently using a regular expression), but how do I do this for those with type Date or type Int that?
Note: when I say search within the field, I mean if a user types '16' into the search field, it will return dates that contain '16', e.g. 01/01/2016 or 16/03/2014. Same principle for integers.
One quick way I think is you can use $where
value to search val = "16"
db.foo.find({$where : "function(){ if(this.dateField.toString().indexOf(val)>= 0 || (""+this.intField).indexOf(val)>=0){return true;}}"})
What basically you can try is convert the field value into string and then search it in there. Downside is $where doesn't uses index, it basically scans the collection, You cannot use other operators when you are using $where.
Yes it is possible.
You can tell your find() to look into only fields which are of specific data types.
$type comes handy.
check out the following link for examples and usage.
https://docs.mongodb.com/manual/reference/operator/query/type/
an example would be
db.addressBook.find({"field": "search-value"}, { "field" : { $type : "double" } } )
will return documents where search-value has a match as well as if the field is of type double.
since i have an image hence posting it as another answer rather than a comment.
now if you notice, documents with ID 2 and ID 6 are exactly the same. Only difference is the data type for the zipcode field.
i ran my query with $and as you can see and it bring backs only the matching record. if i get rid of the $and and the $type condition it will bring back both the records.
i hope this will help you solve your issue.

ODM Query Builder: Is there an "inverse in" operator, or equivalent operation?

In my synfony 2 project, I'm filtering search results using a query builder. In my MongoDB i have some values in an array.
Query Bulider has the "in" operator that allows to query for values that equal one of many in an array. I wanted to perform the opposite operation, i.e. given a single value, query for entries in the data base that contain an array, that contains my value.
For instance, say I have this entry in my MongoDB:
{
"_id": 123,
"name": "John",
"countries_visited":
[
"Australia"
"Bulgaria",
"Canada"
]
}
And I want to query my database for persons who have visited "Canada". Right now, I'm using the where attribute as follows, but I'm looking for a better way to do this.
$qb->field('countries_visited')->where("function(){
return this.indexOf(".$countryName.") > -1
}");
edit:
The in and notIn operator receives an array as parameter and compares it against a single value in MongoDB. I need to provide a single parameter and apply it to an array field in MongoDB, hence "inverse in". I guess I need a contains operator if there's such a thing.
Interesting, MongoDB takes care of this automatically. If querying for a single value against an array field, Mongo will assume you want the check the array if it contains the value.
Taken from the docs:
Match an Array Element
Equality matches can specify a single element in the array to match. These specifications match if the array contains at least one element with the specified value.
So you should be able to do
$users = $dm->getRepository('User')->findOneBy([
'countries_visited' => 'Canada'
]);
or
$qb->field('countries_visited')->equals('Canada');

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

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.

Mongo find unique results

What's the easiest way to get all the documents from a collection that are unique based on a single field.
I know I can use db.collections.distrinct to get an array of all the distinct values of a field, but I want to get the first (or really any one) document for every distinct value of one field.
e.g. if the database contained:
{number:1, data:'Test 1'}
{number:1, data:'This is something else'}
{number:2, data:'I'm bad at examples'}
{number:3, data:'I guess there\'s room for one more'}
it would return (based on number being unique:
{number:1, data:'Test 1'}
{number:2, data:'I'm bad at examples'}
{number:3, data:'I guess there\'s room for one more'}
Edit: I should add that the server is running Mongo 2.0.8 so no aggregation and there's more results than group will support.
Update to 2.4 and use aggregation :)
When you really need to stick to the old version of MongoDB due to too much red tape involved, you could use MapReduce.
In MapReduce, the map function transforms each document of the collection into a new document and a distinctive key. The reduce function is used to merge documents with the same distincitve key into one.
Your map function would emit your documents as-is and with the number-field as unique key. It would look like this:
var mapFunction = function(document) {
emit(document.number, document);
}
Your reduce-function receives arrays of documents with the same key, and is supposed to somehow turn them into one document. In this case it would just discard all but the first document with the same key:
var reduceFunction = function(key, documents) {
return documents[0];
}
Unfortunately, MapReduce has some problems. It can't use indexes, so at least two javascript functions are executed for every single document in the collections (it can be limited by pre-excluding some documents with the query-argument to the mapReduce command). When you have a large collection, this can take a while. You also can't fully control how the docments created by MapReduce are formed. They always have two fields, _id with the key and value with the document you returned for the key.
MapReduce is also hard to debug an troubleshoot.
tl;dr: Update to 2.4

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