How to use "more" and less in query? - mongodb

I have a simple mongodb collection:
{
"_id" : { "id" : "3CE33FCC-AFB1-F59A-2839-3D151DB95A6B" },
"value" : { "count" : 2 }
}
Why this query
db.testb.find({ "value" : { "count" : { $gt: 1 } } })
doesn't work ?

You can use dot notation to access sub documents in mongoDB
This should work:
db.testb.find({ "value.count" : { $gt: 1 } });

In mongodb there is so known dot notation, that can be used to reach into objects and arrays.
Workable query according to dot notation will looks like this:
db.testb.find({ "value.count" : { $gt: 1 } })

Related

MongoDB $divide on aggregate output

Is there a possibility to calculate mathematical operation on already aggregated computed fields?
I have something like this:
([
{
"$unwind" : {
"path" : "$users"
}
},
{
"$match" : {
"users.r" : {
"$exists" : true
}
}
},
{
"$group" : {
"_id" : "$users.r",
"count" : {
"$sum" : 1
}
}
},
])
Which gives an output as:
{ "_id" : "A", "count" : 7 }
{ "_id" : "B", "count" : 49 }
Now I want to divide 7 by 49 or vice versa.
Is there a possibility to do that? I tried $project and $divide but had no luck.
Any help would be really appreciated.
Thank you,
From your question, it looks like you are assuming result count to be 2 only. In that case I can assume users.r can have only 2 values(apart from null).
The simplest thing I suggest is to do this arithmetic via javascript(if you're using it in mongo console) or in case of using it in progam, use the language you're using to access mongo) e.g.
var results = db.collection.aggregate([theAggregatePipelineQuery]).toArray();
print(results[0].count/results[1].count);
EDIT: I am sharing an alternative to above approach because OP commented about the constraint of not using javascript code and the need to be done only via query. Here it is
([
{ /**your existing aggregation stages that results in two rows as described in the question with a count field **/ },
{ $group: {"_id": 1, firstCount: {$first: "$count"}, lastCount: {$last: "$count"}
},
{ $project: { finalResult: { $divide: ['$firstCount','$lastCount']} } }
])
//The returned document has your answer under `finalResult` field

How to write SELECT FROM testing WHERE int_col + int_col2 > 123 in mongodb query?

I am trying to change a part of database from MySQL to MongoDB,
but I had a problem with SELECT FROM testing WHERE int_col + int_col2 > 123. I am trying to change it to MongoDB query but I can't.
Please help me, thanks!.
Easiest way is to split it into 2 actions in an aggregation pipeline:
db.getCollection(collection).aggregate(
[
{
"$addFields" : {
"sum" : {
"$sum" : [
"$int_col",
"$int_col2"
]
}
}
},
{
"$match" : {
"sum" : {
"$gt" : 123
}
}
}
])

How to search document with condition of not having exact object in array of objects?

I have a collection of persons whose schema looks like the collection of following documents.
Document: {
name:
age:
educations:[{
title:xyz,
passed_year:2005,
univercity:abc},
{
title:asd
passed_year:2007,
univercity:mno
}],
current_city:ghi
}
Now I wanna show all the persons who has not done xyz education from abc university in year 2005.
I think two possible queries for this need but not sure which one to use as both of them are giving me the output
Query 1:
db.persons.find({"education":{$ne:{$elemMatch:{"title":"xyz","passed_year":2005,"univercity":"abc"}}}})
Query 2:
db.persons.find({"education":{$not:{$elemMatch:{"title":"xyz","passed_year":2005,"univercity":"abc"}}}})
I'm quite confused about operator $ne and $not, which one should I use with $elemMatch as both of them are giving me the output.
Given this $elemMatch: {"title":"xyz","passed_year":2005,"univercity":"abc"} I think you want to exclude any documents which contain an sub document in the educations array which contains all of these pairs:
"title" : "xyz"
"passed_year" : 2005
"univercity" : "abc"
This query will achieve that:
db.persons.find({
"educations": {
$not: {
$elemMatch:{"title": "xyz", "passed_year": 2005, "univercity": "abc"}
}
}
})
In your question you wrote:
both of them are giving me the output
I suspect this is because your query is specifying education whereas the correct attribute name is educations. By specifying education you are adding a predicate which cannot be evaluated since it references a non existent document attribute so regardless of whether that predicate uses $ne or $not it will simply not be applied.
In answer to the question of which operator to use: $not or $ne: if you run the above query with .explain(true) you'll notice that the parsed query produced by Mongo is very different for each of these operators.
Using $ne
"parsedQuery" : {
"$not" : {
"educations" : {
"$eq" : {
"$elemMatch" : {
"title" : "xyz",
"passed_year" : 2005,
"univercity" : "abc"
}
}
}
}
}
Using $not:
"parsedQuery" : {
"$not" : {
"educations" : {
"$elemMatch" : {
"$and" : [
{
"passed_year" : {
"$eq" : 2005
}
},
{
"title" : {
"$eq" : "xyz"
}
},
{
"univercity" : {
"$eq" : "abc"
}
}
]
}
}
}
}
So, it looks like use of $ne causes Mongo to do something like this psuedo code ...
not educations equalTo "$elemMatch" : {"title" : "xyz", "passed_year" : 2005, "univercity" : "abc"}
... i.e. it treats the elemMatch clause as if it is the RHS of an equality operation whereas use of $not causes Mongo to actually evaluate the elemMatch clause.

Exclude 0 values from mongodb $avg but keeping other fields

I run some aggregation queries on MongoDB 3.2.
I would like to group documents by a field with an average on another numeric field.
I need the average to ignore the 0 values.
The problem is I can't entirely filter the document, cause there is another field I need for a count.
Let's illustrate :
This is the structure of my documents:
{"stringToGroupByOn":"foo", "valueToAvg":42, "valueToSum":21}
{"stringToGroupByOn":"foo", "valueToAvg":0, "valueToSum":13}
I can't just filter like this:
db.foobar.aggregate([
{
$match : { valueToAvg : { $gt : 0 } }
},
{
$group : {
_id : '$stringToGroupByOn',
avg : { $avg : '$valueToAvg' },
count : { $sum : '$valueToSum' }
}
}
])
Because I lose the value 13 for the count.
Do you think there is a way to do it in only one query ?
You can use $cond in projection to set null instead of 0, as null is not considered when using average.
db.avg.aggregate([
{$project:{
_id:1,
valueToSum:1,
stringToGroupByOn:1,
valueToAvg:{$cond:
{ if: { $eq: [ "$valueToAvg", 0 ] },
then: null,
else: "$valueToAvg" }}
}},
{
$group : {
_id : '$stringToGroupByOn',
avg : { $avg : '$valueToAvg' },
count : { $sum : '$valueToSum' }
}
}
output:
{
"_id" : "foo",
"avg" : 42.0,
"count" : 34.0
}

array projection with findandmodify

I'm using findAndModify command to maintain a list of undo/redo commands. I'm using "fields" tag to specify what the "before" values were. This is then used to build undo command. ie:
cmd:
{
findAndModify : "aaa",
query : { _id: ObjectId('5215f7d1fe789bb17427bde9') },
update : { "$set" : { "v1" : 200 } },
fields : { v1 : 1, _id : 0 }
}
built result:
"Do" : { "$set" : { "v1" : 200 } },
"Undo" : { "$set" : { v1" : 100 } }
However, I cannot make this work for arrays. I do a set command like this:
{ $set : "myArrayVar.3" : 100 }
I've tried using projections like:
{ "myArrayVar.3" : 1 }
{ "myArrayVar.$" : 1 }
but both return just empty array braces:
{ myArrayVar : [] }
What am I missing?
You can't use numeric array indexes in projections, but you can use $slice instead:
fields: { myArrayVar: { $slice: [2, 1] } }
That would include just the third element (skip 2, take 1).