Can you match sub-fields with $all in Mongo? - mongodb

I have a collection of document, where each document looks like this:
{'name' : 'John', 'locations' :
[
{'place' : 'Paris', 'been' : true}
{'place' : 'Moscow', 'been' : false}
{'place' : 'Berlin', 'been' : true}
]
}
Where the locations array could have any length.
I want to match documents where the been field is true for all elements in the locations array. Looking at the documentation it looks like I should use $and somehow but I'm not sure if it works with sub-fields.

There are several options:
use $ne: db.destinations.find({"locations.been":{$ne:false}})
change your business logic to precompute that value before saving the document. Otherwise, this search must look through all records and then all places. This value could be indexed.
use the $where operator, but, understand the performance implications. It may require a full table scan. In this case, it would.
write a map-reduce function with the filter logic and only emit those that are valid. You'd need to incrementally update it per the docs.
write a query using the aggregation framework. There are a lot of good examples here. Although, like other solutions, this could end up looping through the entire collection.

I think it's impossible to do with standart MongoDB operators like $elemMatch or $all. The only possible way is to write custom JS query:
db.test.find("return this.locations.every(function(loc){return loc.been});")

Related

Order of Fields in Mongo Query vs Ordered Checked In

Say you're querying documents based on 2 data points. One is a simple bool parameter, and the other is a complicated $geoWithin calculation.
db.collection.find( {"geoField": { "$geoWithin" : ...}, "boolField" : true} )
Will mongo reorder these parameters, so that it checks the boolField 1st, before running the complicated check?
MongoDB uses indexes like any other DBs. So the important thing for mongoDB is if any query fields has an index or not, not the order of query fields. At least there is no information in their documentation that mongoDB try to checks primitive query fields first. So for your example if boolField has an index mongoDB first check this field and eliminate documents whose boolField is false. But If geoField has an index then mongoDB first execute query on this field.
So what happens if none of them have index or both of them have? It should be the given order of fields in query because there is no suggestion or info beside of indexes in query optimization page of mongoDB. Additionally you can always test your queries performances with just adding .explain("executionStats").
So check the performance of db.collection.find( {"geoField": { "$geoWithin" : ...}, "boolField" : true} ) and db.collection.find( { "boolField" : true, "geoField": { "$geoWithin" : ...} } ). And let us know :)
To add to above response, if you want mongo to use specific index you can use cursor.hint . This https://docs.mongodb.com/manual/core/query-plans/ explains how default index selection is done.

Mongo DB: What are the Performance issues in using $all

I am trying to search a list of values on a matching field in documents which is array of documents. Using $in makes it OR between the values I supply. Using $all seems to be more logical.
For eg:
Collection: Phrases
sample doc:
{
"locales": [
{
"name": "BPT",
"internal_desc": "Entre 2 e 3 horas"
},
{
"name": "JPN",
"internal_desc": "2 ~ 3 時間"
}
]
}
Query:
db.phrases.find({"locales.name":{"$all":["BPT", "JPN"]}})
But some posts suggesting $all is bad in terms of performance. Is there any other way to achieve this?
Using $and instead of $all will result in equivalent performance. The bottom line is that given what you are trying to accomplish using $all is your best bet (as far as my understanding goes). However, $all can be optimized by making the first element in the expression more selective. For example if you know that "BPT" shows up in 2% of documents and "JPN" shows up in 20% of documents then it makes sense to list "BPT" as the first element in the $all expression. This way mongo only needs to filter through fewer documents on each consecutive element in your $all expression. Im sure you've seen the documentation but here is a link nonetheless: $all - mongodb
You can use the $and syntax, as shown in the query below;
db.phrases.find({$and : [
{"locales.name" : "BPT"},
{"locales.name" : "JPN"}
]
});
You can get information about your query, to see what the db is doing when executing the query by using the explain command, as displayed below;
db.phrases.explain().find({$and : [
{"locales.name" : "BPT"},
{"locales.name" : "JPN"}
]
});
Although, the explain command is more relevant to dbs where indexes are used, since it sort of gives you information about, which index was utilised by the db on the search.
Have a quick look into MongoDB indexes and explain() for further information.
I hope this helps.
Regards,
Nick.

In MongoDB how do you query for records that contain ONLY certain fields and no others

In MongoDB,
To query for records that contain certain fields you can do:
collection.find({'field_name1': {'$exists': true}})
And that will return any record that has the 'field_name1' field set...
But how do you query mongo to find records that contains ONLY 'field_name1' (and no other fields)? I'd like to be able to do this for, say, a list of fields.
The sad answer, as you'll often find with MongoDB and other NoSQL databases is probably that it would be best to structure your data in a way that allows you to query it as simply as possible.
That said, there are ways of doing this, but as far as I know, it requires you execute JavaScript server side. This will be slow, and cannot possibly take advantage of indexes and other logical features of MongoDB, so use it only if it's absolutely necessary, if performance is at all important.
So, the easiest way to do this, is probably to create a function that returns the number of fields in an object, which we can use with the $where query syntax. This allows you to run arbitrary JavaScript queries against your data, and can be combined with normal syntax queries.
Sadly, my JavaScript-fu is a little weak, so I don't know how (or if) you can get at the count of members of an object in JS in a one-liner, so to do this, I would store a function server side.
From the mongo shell, execute the following:
db.system.js.save(
{
"_id" : "countFields",
"value" : function(x) { i=0; for(p in x) { i++; } return i}
}
)
With that, you have a saved JavaScript function, server side, called countFields that returns the number of elements in an object. Now, you need to execute your find-operation with the $where query:
db.collection.find({
'field_name1': {'$exists': True},
'$where' : 'countFields(this)==2'
})
This would give you only the documents that meet both the $exists condition, and the $where clause. Note that I'm comparing with 2 in the example, since the countFields function counts _id as a field.

Adding an index to a MongoDB collection hash field

I have a MongoDB collection that I would like to add an index on. For the purpose of this post, let's say the collection name is Cats. I have a hash key on the Cats collection so if you do db.cats.findOne(); it'll look like the following:
> db.cats.findOne();
{
"_id" : ObjectId("4f248f8ae4b0b775c9eb002d"),
"metaData" : {
"type" : "cute",
"id" : "4ed3b6c599114b488be52bc3"
},
....
}
I query very often (using Mongoid), with something like this:
Cat.first(:conditions => { "metaData.id" => an_id }
I'd really like to be able to take advantages of indexes here, but I'm not entirely sure if I should index all of metaData or just metaData.id (I query against id specifically, and very often).
Would love any solution to this problem because I think I can dramatically speed up queries if I do the right thing here. Also, this is a unique index.
also metaData is not an embedded document. it does not have its own collection. it is simply a hash with a 1:1 mapping in each cats object.
You can just define an index on the embedded document. This is covered here:
http://www.mongodb.org/display/DOCS/Indexes#Indexes-UsingDocumentsasKeys
For your specific example, this would be:
db.Cats.ensureIndex({ "metaData.id" : 1}, {unique : true})
To compare your results do some of your standard queries in the shell with a .explain() to compare the speed with and without the index. If you are not doing a lot of queries you might need to hint the index to use so that it doesn't cache the "best" index (don't forget there is one on _id by default). More explain info here:
http://www.mongodb.org/display/DOCS/Explain

Mongodb: return matched filters when using $or in find()

Suppose I am doing a query in Mongodb like this
db.user.find({$or : [{"field1" : "abc"}, {"field2" : "def"}, {"field3" : "ghi"}]})
And a number of documents are returned. What is the easiest way to know which one (or multiple) of the three filters is matched for each document returned? By "easiest", I do not wish to add more executions of find()'s.
Thanks.
There is no such option to solve this on the MongoDB query layer. Likely you want to perform individual queries instead one big $or query in order to solve your problem.