In MongoDB, is it possible to use $set or $mul but specify another field in the same document rather than a constant? - mongodb

Say that I have a document:
{ _id: 1, item: "ABC", supplier: "XYZ", price: 10, available: 23 }
and then I run something like
db.products.update(
{ _id: 1, supplier: "XYZ" },
{ stock_value: {$mul: ["price", "available", 0.8] }}
)
to get a document
{ _id: 1, item: "ABC", supplier: "XYZ", price: 10, available: 23, stock_value: 184 }
I'd like to do this without loading everything into the client. And I need to be able to specify a different constant (e.g. the 0.8) for each supplier.
I'm thinking I should just use an aggregation with an $out to the same collection, to overwrite the whole then when the update is done, but I can't do a different aggregate() call for each supplier since I'm overwriting the collection - all other suppliers will be skipped. Is there some sort of "in place" aggregation? or a way to append $out ?

Related

How does unordered insertion behave in db.collections.insert() in mongodb

db.products.insert(
[
{ _id: 20, item: "lamp", qty: 50, type: "desk" },
{ _id: 21, item: "lamp", qty: twenty, type: "floor" },
{ _id: 22, item: "bulk", qty: 100 }
],
{ ordered: false }
)
I am trying to insert three documents in the collection products, but the second document has an error, the qty field has erroneous value. I am using ordered: false so I am expecting that all the other documents should get inserted except the second one, but this is not the case, none of the documents get inserted.
Then what is the difference between ordered and unordered insertion? How can I achieve the desired results in this case.
Ordered/unordered inserts are distinguished on the server side.
If you don't have the twenty variable defined, the error happens on the client side and no insert is attempted at all.

Is there a way to sort the order of columns in mongodb?

I am learning MongoDB and I've encountered a thing that mildly annoys me.
Let's say I got this collection:
[
{
_id: ObjectId("XXXXXXXXXXXXXX"),
name: "Tom",
followers: 10,
active: true
},
{
_id: ObjectId("XXXXXXXXXXXXXX"),
name: "Rob",
followers: 109,
active: true
},
{
_id: ObjectId("XXXXXXXXXXXXXX"),
name: "Jacob",
followers: 2,
active: false
}
]
and I rename the name column to username with the command:
db.getCollection('users').update({}, { $rename: { "name" : "username" }}, false, true)
now the username property is at the end of the record, example:
[
// ... rest of collection has the same structure
{
_id: ObjectId("XXXXXXXXXXXXXX"),
followers: 109,
active: true,
username: "Rob"
}
// ... rest of collection has the same structure
]
How do I prevent this from happening or how do I place them in a specific order? This is infuriating to work with in Robo/Studio 3T. I've got a collection with about 15 columns which are now out of order which in the GUI because of this
The $rename operator logically performs an $unset of both the old name and the new name, and then performs a $set operation with the new name. As such, the operation may not preserve the order of the fields in the document; i.e. the renamed field may move within the document.
Documentation
It is the behaviour from version 2.6
Since it is JSON based, you can get any field easily. And you have very less columns.
Keys in JSON objects are in their very nature unordered. See RFC 4627 which defines JSON, section 1 "Introduction":
An object is an unordered collection of zero or more name/value
pairs, where a name is a string and a value is a string, number,
boolean, null, object, or array.
(Emphasis mine)
Therefore, it would even be correct, if you wrote
{
"name": "Joe",
"city": "New York"
}
and got back
{
"city": "New York",
"name": "Joe"
}

Mongodb: How to update calculated field in atomic operation

In an atomic update operation, I would like to (re)calculate a field in a document based on the updated values of other fields in the same document.
Example:
Documents have this structure:
{ _id: xxx, nrValues: 4, sumOfValues: 20, meanValue: 5 }
{ _id: yyy, nrValues: 3, sumOfValues: 12, meanValue: 4 }
The (indexed) meanValue field is needed for fast sort/find operations.
Now I would like to "add" new values to documents like in this pseudocode:
Documents.update( { _id: xxx },
{ $inc: { nrValues: 1, sumOfValues: newValue },
$set: { meanValue: sumOfValues / nrValues } } );
Is there a way to find real code for this pseudocode?
BTW: I know that I could use map/reduce/aggregation to sort/find the documents this way without having an actual meanValue field at all, but I do want this actual field in the document and in my (Meteor based) situation I cannot use map/reduce/aggregation.

What is the mongodb equivalent of sorting over GROUPed BY field

I have two collections in MongoDB. Let's call one "objects":
{
_id: 11,
name: "Foo"
},
{
_id: 12,
name: "Bar"
},
{
_id: 13,
name: "Quux"
}
Another one is "values", that references "objects":
{
_id: 21,
value: "One",
object_id: 11
},
{
_id: 22,
value: "Two",
object_id: 11
},
{
_id: 21,
value: "Three",
object_id: 13
}
I want to query "objects" collection, showing the number of incoming links from "values" for each document in "objects" and sort by that number, in an descending order. I want to receive something like this:
{
_id: 11,
name: "Foo",
links: 2
},
{
_id: 13,
name: "Quux"
links: 1
},
{
_id: 12,
name: "Bar",
links: 0
}
In SQL, this is achievable using a simple outer join, GROUP BY and ORDER BY.
I can't use MapReduce (this must be an online query, not a scheduled job). I can't denormalize anything as "objects" and "values" in reality are collections of pretty large documents with lots of legacy code depending on their exact structure.
Any advice?
MongoDB has no JOINs. There is no way to get data from more than one collection with a single query. This means you need to program the JOIN on the application layer and then do your GROUP BY, also in the application. How to do this depends on the technology which your application is using.
Sorry, but this is what you get when you structure your data for MongoDB as if it were an SQL database.

Query multiple date ranges, return only specific key in MongoDB

In Mongo, I have a documents that look like the following:
dateRange: [{
"price": "200",
"dateStart": "2014-01-01",
"dateEnd": "2014-01-30"
},
{
"price": "220",
"dateStart": "2014-02-01",
"dateEnd": "2014-02-15"
}]
Nice and simple right? Just dates and prices. Now, the tricky party I'm is how would I go about creating a query to find the dateRange that fits with 2014-01-12, and then JUST return the price after it's found instead of the entire array of dateRanges?
These dateRanges can get quite large, and I'm trying to minimize the amount of data returned (if this is possible at all with Mongo). Note, the date format I can change up if required, I was just using the above for example purposes.
Any help is appreciated, thanks!
You want to use the $elemMatch operator, which is only valid in versions 2.2 upward. You will also need to make sure you use multikey indexes.
edit: To be clear you will also have to use the $elemMatch find operator as pointed out in comment below.
This being said, I agree with the gist of comment by mnemosyn. It would be better to have each element of the array represented as a single document.
quick example of $elemMatch to demonstrate the projection. Simply add $elemMatch to the find as well.
> db.test.save ( {
_id: 1,
zipcode: 63109,
students: [
{ name: "john", school: 102, age: 10 },
{ name: "jess", school: 102, age: 11 },
{ name: "jeff", school: 108, age: 15 }
]
} );
> db.test.find( { zipcode: 63109 }, { students: { $elemMatch: { school: 102 } } } ).pretty() );
{
"_id" : 1,
"students" : [
{
"name" : "john",
"school" : 102,
"age" : 10
}
]
}
Well, the problem with that schema is that it uses large embedded arrays - this can be quite inefficient, because a mongodb query will always find a document, not a subset of an embedded object. Even if you're using a projection, mongodb will have to read the entire object internally, so if the array becomes huge, say 100k entries, that will slow things down to a halt.
Why not simply separate these array elements into documents, e.g.
{
price : 200,
productId : ObjectId("foo"), // or whatever the price refers to
dateStart : "2014-01-01",
dateEnd : "2013-01-30"
}
This way, mongodb doesn't need to pull the entire object with all prices, but only the prices that match your date range. This will minimize the amount of data transferred. You can then also use the query projection to only return the price, i.e. db.collection.find({ criteria }, {"price" : 1, "_id" : 0}).
Of course, the number of objects will increase dramatically, but efficient indexing will solve that problem. The only inefficiency induced is the duplication of the productId, which is cheaper than dealing with huge embedded arrays.
P.S: I'd suggest using actual dates (ISODate) instead of strings, even if their format is sortable.