I have some keys stored on database that need to be reset periodically.
It goes like this
{
name: "Bob",
limits:{
one: 1000
two: 14
three: 19
}
}
i looked for a query that assigns values to all keys inside an object, at first I thought $each would help me but it has a totally different purpose.
What i want is to assign, let's say, 0 to all the keys inside limits
Note: key names from limits aren't constant across users and aren't always known.
It would be exactly the same as Object.keys(limit).forEach(itm=> limit[itm]=0) does for regular JS, but in Mongo's context
You're halfway through:
You can iterate the collection and apply the forEach method for every cursor document
db.collection.find().forEach(function(d) {
Object.keys(d.limits).forEach(item=> d.limits[item]=0);
db.collection.save(d);
})
Caution: this can be very slow.
Related
I'm trying to get the min and max value from some fields inside a collection. I'm not sure if this:
result = collection.find(date_filter, expected_projection).sort({'attribute': -1}).limit(1)
is equivalent to this:
result_a = collection.find(date_filter, expected_projection)
result_b = result_a.sort({'attribute': -1}).limit(1)
I don't want the server to query all the data in result_a from the database. Is the first line of code actually fetching every document in my collection and THEN sorting it, or just fetching the max element in the attribute field?
No, they aren't equivalent; and MongoDB will not return the entire collection to the client - whether or not the attribute field is indexed.
When you chain operators together in a MongoDB command (e.g. find().sort().limit()), it is not treated by the MongoDB server as a set of separate functions to be called sequentially; it is treated as a single query which should be optimised as a whole and executed as a whole on the MongoDB server.
See the documentation on Combining Cursor Methods for another example of how the chaining is not taken as a sequence of independent operations:
The following statements chain cursor methods limit() and sort():
db.bios.find().sort( { name: 1 } ).limit( 5 )
db.bios.find().limit( 5 ).sort( { name: 1 } )
The two statements are equivalent; i.e. the order in which you chain the limit() and the sort() methods is not significant. Both statements return the first five documents, as determined by the ascending sort order on ‘name’.
The first line of code tells MongoDB to return only the document with the lowest value for "attribute". If "attribute" is indexed, then MongoDB can directly access only that one document, and not even consider the rest of the collection.
Do this once:
collection.create_index([('attribute', 1)])
Having that index in place means you can find the highest-sorting or lowest-sorting document practically instantly.
I have a collection which im aggregating and grouping by the field "type". The final result should be just maximum of five documents in each type. But if i limit before group only five first docs will be grouped. if i limit after the group the first five types will return.
is there a way to do this without doing find() for each type , limiting to 5 and merging all the results ?
If you can use C# (which according to my quick google-search about mongodb you do), you can do this with one of the GroupBy's which have an "ResultSelector-Function", like this:
var groups = Enumerable.Range(0, 1000).
GroupBy(
x => x/10,
(key, elements) => new { Key = key, Elements = elements.Take(5) }
);
About the speed of this code - I believe the group is completely build before the result-selector is instantiated - so a custom foreach over the input sequence and building the groups by hand might be faster (if you can somehow determine when you are done)
P.S.: On second thought - I doubt my answer is the one you want. I had a look at the mongo-DB documentation, and "map" in combination with a suitable "reduce" function might be exactly what you want.
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
I have unique documents which are indexed by non-unique keys. What makes this document unique, is the combination of multiple keys within the document. For example:
{
first: 'John',
last: 'Foo'
}
{
first: 'Henry',
last: 'Bar'
}
{
first: 'Frank',
last: 'Foo'
}
{
first: 'John',
last: 'Bar'
}
So, based on the example above: If we wanted to query for the first name of Frank, we would only get one result. Ideally, since we only have one result, we wouldn't even need to compare the last name to our query. However, if we query for the name John, we would get two results, so we would need to compare the secondary argument.
How would this style of query be achieved in Mongo? The goal is simply to save needless compares if there is only a single match to begin with.
Note that i am aware that this style of query doesn't guarantee the correct document. It assumes that the primary, and each subsequent field match, is "good enough" to verify the identity of the document, if only one document is matched. Though if there are other less obvious reasons why this method should not be used, by all means discuss it :)
I wouldn't worry about this at all, especially if you have an index on this. A compound index on first, last will only scan the index elements that start with "first". If that's one document, then it stops. If you it also needs to match "last", then it will scan those parts of the index.
My question may be not very good formulated because I haven't worked with MongoDB yet, so I'd want to know one thing.
I have an object (record/document/anything else) in my database - in global scope.
And have a really huge array of other objects in this object.
So, what about speed of search in global scope vs search "inside" object? Is it possible to index all "inner" records?
Thanks beforehand.
So, like this
users: {
..
user_maria:
{
age: "18",
best_comments :
{
goodnight:"23rr",
sleeptired:"dsf3"
..
}
}
user_ben:
{
age: "18",
best_comments :
{
one:"23rr",
two:"dsf3"
..
}
}
So, how can I make it fast to find user_maria->best_comments->goodnight (index context of collections "best_comment") ?
First of all, your example schema is very questionable. If you want to embed comments (which is a big if), you'd want to store them in an array for appropriate indexing. Also, post your schema in JSON format so we don't have to parse the whole name/value thing :
db.users {
name:"maria",
age: 18,
best_comments: [
{
title: "goodnight",
comment: "23rr"
},
{
title: "sleeptired",
comment: "dsf3"
}
]
}
With that schema in mind you can put an index on name and best_comments.title for example like so :
db.users.ensureIndex({name:1, 'best_comments.title:1})
Then, when you want the query you mentioned, simply do
db.users.find({name:"maria", 'best_comments.title':"first"})
And the database will hit the index and will return this document very fast.
Now, all that said. Your schema is very questionable. You mention you want to query specific comments but that requires either comments being in a seperate collection or you filtering the comments array app-side. Additionally having huge, ever growing embedded arrays in documents can become a problem. Documents have a 16mb limit and if document increase in size all the time mongo will have to continuously move them on disk.
My advice :
Put comments in a seperate collection
Either do document per comment or make comment bucket documents (say,
100 comments per document)
Read up on Mongo/NoSQL schema design. You always query for root documents so if you end up needing a small part of a large embedded structure you need to reexamine your schema or you'll be pumping huge documents over the connection and require app-side filtering.
I'm not sure I understand your question but it sounds like you have one record with many attributes.
record = {'attr1':1, 'attr2':2, etc.}
You can create an index on any single attribute or any combination of attributes. Also, you can create any number of indices on a single collection (MongoDB collection == MySQL table), whether or not each record in the collection has the attributes being indexed on.
edit: I don't know what you mean by 'global scope' within MongoDB. To insert any data, you must define a database and collection to insert that data into.
Database 'Example':
Collection 'table1':
records: {a:1,b:1,c:1}
{a:1,b:2,d:1}
{a:1,c:1,d:1}
indices:
ensureIndex({a:ascending, d:ascending}) <- this will index on a, then by d; the fact that record 1 doesn't have an attribute 'd' doesn't matter, and this will increase query performance
edit 2:
Well first of all, in your table here, you are assigning multiple values to the attribute "name" and "value". MongoDB will ignore/overwrite the original instantiations of them, so only the final ones will be included in the collection.
I think you need to reconsider your schema here. You're trying to use it as a series of key value pairs, and it is not specifically suited for this (if you really want key value pairs, check out Redis).
Check out: http://www.jonathanhui.com/mongodb-query