MongoDB How to compare fields in a single document? [duplicate] - mongodb

This question already has answers here:
MongoDb query condition on comparing 2 fields
(4 answers)
Closed 4 years ago.
Given the collection "example":
{_id: 1, prediction: "H", result: "A"}
{_id: 2, prediction: "H", result: "H"}
{_id: 3, prediction: "A", result: "A"}
What do I need to do to find records where the prediction and result values match? ie. documents 2 and 3?
Finding all the "H" predictions is:
db.example.find( { prediction: "H" } )
But I need to replace the literal "H" with the value in the result field of the same document.
Edit: Sorry, I should have said that I was using Mongo 3.6.

You should be able to do this with an aggregation query, try out the $redact stage:
db.test.aggregate(
[
{ $redact: {
$cond: {
if: { $eq: [ "$prediction", "$result" ] },
then: "$$DESCEND",
else: "$$PRUNE"
}
}
}
]
);
This will yield the result:
{ "_id" : 2, "prediction" : "H", "result" : "H" }
{ "_id" : 3, "prediction" : "A", "result" : "A" }
More info on redact can be found here - https://docs.mongodb.com/manual/reference/operator/aggregation/redact/#redact-aggregation

You can use agregation
db.example.aggregate(
[
{
$project:
{
_id: 1,
prediction: 1,
result: 1,
compare: { $eq: [ "$prediction", "$result" ] }
}
},
{
$match:
{
compare: true
}
}
]
)

If you are using 3.6, this will work. refer this
db.getCollection('TEST').find( { $where: function() {
return this.prediction == this.result
} })
Result :
{
"_id" : 2,
"prediction" : "H",
"result" : "H"
}
{
"_id" : 3,
"prediction" : "A",
"result" : "A"
}

Related

Mongodb - find query based on an element of an array

I have the following type of document where I have to find instock elements based on a given value. i.e Return all instocks where ele = 5.
{"item":"journal",
"instock":[
{ "warehouse":"A", "ele":[2,4,5] },
{ "warehouse":"C", "ele":[8,5,2] },
{ "warehouse":"F", "ele":[3] },
{ "warehouse":"K", "ele":[2,8,4] }
]
}
I tried to use $elemMatch but it just produces the first element.
db.inventory.find({"item": "journal"}, {"_id": 0, "item": 0, "instock":{$elemMatch: {"ele": {$in: [5]}}} })
But it just gives:
{ "instock" : [
{ "warehouse" : "A", "ele" : [ 2, 4, 5 ] }
]}
And the expectation is
{ "instock" : [
{ "warehouse" : "A", "ele" : [ 2, 4, 5 ] },
{ "warehouse" : "C", "ele" : [ 8, 5, 2 ] }
]}
How should I get the expected result?
The $elemMatch or instock.$ will return first match document in find()'s projection,
You can use aggregation expression in projection from MongoDB 4.4, for your example use array $filter operator in projection,
db.collection.find({
"item": "journal"
},
{
instock: {
$filter: {
input: "$instock",
cond: { $in: [5, "$$this.ele"] }
}
}
})
Playground
For the old version you can use aggregate() using above same operator.
I suggest you to unwind the instock array, then filter the data, and finally if you want you can group the result by item to obtain the desired result.
Here is an exemple: solution

Create A function Which can do specific Sum on entire table [duplicate]

This question already has answers here:
Is it possible to sum 2 fields in MongoDB using the Aggregation framework?
(3 answers)
Closed 5 years ago.
I have a Mongo DB dataset Like this:
{
"_id" : ObjectId("5a8a75cdec129c86c6a2aaca"),
"asset" : "121",
"A" : 6,
"C" : 12,
"B" : 4,
"D" : 8,
"finalAB" : "",
"finalCD" : "",
},
{
"_id" : ObjectId("5a8a75cdec129c86c6a2aaca"),
"asset" : "122",
"A" : 8,
"C" : 14,
"B" : 6,
"D" : 10,
"finalAB" : "",
"finalCD" : "",
}......upto 4k Entry Like this
Now I want to Create MongoDB function Which can do the Entire Table Calculation of "finalAB" & "finalCD"
NOTE: finalAB = A + B and finalCD = C + D.
Any help is appreciated
You can't use update here because you're referring to other fields in your update however you can try to use Aggregation Framework:
db.col.aggregate([
{
$addFields: {
finalAB: { $add: [ "$A", "$B" ] },
finalCD: { $add: [ "$C", "$D" ] },
}
},
{
$out: "col"
}
])
$addFields simply replaces your finalAB and finalCD with new values. $out is a way to redirect aggregation ouput to a new collection. That's how you can replace (update) existing collection.
Use Aggregate Query :
app.get("/getTotal",function(req,res){
YourModel.aggregate([
{
$addFields: {
finalAB: { $add: [ "$A", "$B" ] },
finalCD: { $add: [ "$C", "$D" ] },
}
},
{
$group:{ _id:null,
finalAB:{ $sum: "$finalAB"},
finalCD:{ $sum: "$finalCD"}
}
}
],function(err,result){
res.json(result);
});
});
Use full Link : https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/

MongoDB: Project to array item with minimum value of field

Suppose my collection consists of items that looks like this:
{
"items" : [
{
"item_id": 1,
"item_field": 10
},
{
"item_id": 2,
"item_field": 15
},
{
"item_id": 3,
"item_field": 3
},
]
}
Can I somehow select the entry of items with the lowest value of item_field, in this case the one with item_id 3?
I'm ok with using the aggregation framework. Bonus point if you can give me the code for the C# driver.
You can use $reduce expression in the following way.
The below query will set the initialValue to the first element of $items.item_field and followed by $lt comparison on the item_field and if true set $$this to $$value, if false keep the previous value and $reduce all the values to find the minimum element and $project to output min item.
db.collection.aggregate([
{
$project: {
items: {
$reduce: {
input: "$items",
initialValue:{
item_field:{
$let: {
vars: { obj: { $arrayElemAt: ["$items", 0] } },
in: "$$obj.item_field"
}
}
},
in: {
$cond: [{ $lt: ["$$this.item_field", "$$value.item_field"] }, "$$this", "$$value" ]
}
}
}
}
}
])
You can use $unwind to seperate items entries.
Then $sort by item_field asc and then $group.
db.coll.find().pretty()
{
"_id" : ObjectId("58edec875748bae2cc391722"),
"items" : [
{
"item_id" : 1,
"item_field" : 10
},
{
"item_id" : 2,
"item_field" : 15
},
{
"item_id" : 3,
"item_field" : 3
}
]
}
db.coll.aggregate([
{$unwind: {path: '$items', includeArrayIndex: 'index'}},
{$sort: { 'items.item_field': 1}},
{$group: {_id: '$_id', item: {$first: '$items'}}}
])
{ "_id" : ObjectId("58edec875748bae2cc391722"), "item" : { "item_id" : 3, "item_field" : 3 } }
We can get expected result using following query
db.testing.aggregate([{$unwind:"$items"}, {$sort: { 'items.item_field': 1}},{$group: {_id: "$_id", minItem: {$first: '$items'}}}])
Result is
{ "_id" : ObjectId("58edf28c73fed29f4b741731"), "minItem" : { "item_id" : 3, "item_field" : 3 } }
{ "_id" : ObjectId("58edec3373fed29f4b741730"), "minItem" : { "item_id" : 3, "item_field" : 3 } }

Add some kind of row number to a mongodb aggregate command / pipeline

The idea is to return a kind of row number to a mongodb aggregate command/ pipeline. Similar to what we've in an RDBM.
It should be a unique number, not important if it matches exactly to a row/number.
For a query like:
[ { $match: { "author" : { $ne: 1 } } }, { $limit: 1000000 } ]
I'd like to return:
{ "rownum" : 0, "title" : "The Banquet", "author" : "Dante", "copies" : 2 }
{ "rownum" : 1, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 }
{ "rownum" : 2, "title" : "Eclogues", "author" : "Dante", "copies" : 2 }
{ "rownum" : 3, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 }
{ "rownum" : 4, "title" : "Iliad", "author" : "Homer", "copies" : 10 }
Is it possible to generate this rownum in mongodb?
Not sure about the performance in big queries, but this is at least an option.
You can add your results to an array by grouping/pushing and then unwind with includeArrayIndex like this:
[
{$match: {author: {$ne: 1}}},
{$limit: 10000},
{$group: {
_id: 1,
book: {$push: {title: '$title', author: '$author', copies: '$copies'}}
}},
{$unwind: {path: '$book', includeArrayIndex: 'rownum'}},
{$project: {
author: '$book.author',
title: '$book.title',
copies: '$book.copies',
rownum: 1
}}
]
Now, if your database contains a big amount of records, and you intend to paginate, you can use the $skip stage and then $limit 10 or 20 or whatever you want to display per page, and just add the number from the $skip stage to your rownum and you'll get the real position without having to push all your results to enumerate them.
Starting in Mongo 5, it's a perfect use case for the new $setWindowFields aggregation operator and its $documentNumber operation:
// { x: "a" }
// { x: "b" }
// { x: "c" }
// { x: "d" }
db.collection.aggregate([
{ $setWindowFields: {
sortBy: { _id: 1 },
output: { rowNumber: { $documentNumber: {} } }
}}
])
// { x: "a", rowNumber: 1 }
// { x: "b", rowNumber: 2 }
// { x: "c", rowNumber: 3 }
// { x: "d", rowNumber: 4 }
$setWindowFields allows us to work for each document with the knowledge of previous or following documents. Here we just need the information of the place of the document in the whole collection (or aggregation intermediate result), as provided by $documentNumber.
Note that we sort by _id because the sortBy parameter is required, but really, since you don't care about the ordering of your rows, it could be anything you'd like.
Another way would be to keep track of row_number using "$function"
[{ $match: { "author" : { $ne: 1 } }} , { $limit: 1000000 },
{
$set: {
"rownum": {
"$function": {
"body": "function() {try {row_number+= 1;} catch (e) {row_number= 0;}return row_number;}",
"args": [],
"lang": "js"
}
}
}
}]
I am not sure if this can mess up something though!

MongoDB: count the repetitive time of array element with MapReduce

Say for every document of a collection, it has an string array. how could I count the repetitive time of every element of the array in all this collection? Right now I can find all the distinct element, but then Map Reduce function is a little tricky that I haven't fully understood.
Doc A
{
_id:
name:
actors: ["a", "b", "c"]
}
Doc B
{
_id:
name:
actors: ["a", "d"]
}
Doc C
{
_id:
name:
actors: ["a", "c", "f"]
}
I wanne get a statistic result with a:3 b:1 c:2 d:1 f:1.
An alternative route that you could take is the aggregation framework. Considering the above collection as an example
Populate test collection:
db.collection.insert([
{ "_id" : 1, "name" : "ABC1", "actors": ["a", "b", "c"] },
{ "_id" : 2, "name" : "ABC2", "actors" : ["a", "d"] },
{ "_id" : 3, "name" : "XYZ1", "actors" : ["a", "c", "f"] }
])
Using MongoDB 3.4.4 or newer:
db.collection.aggregate([
{ "$unwind" : "$actors" },
{ "$group": { "_id": "$actors", "count": { "$sum": 1} } },
{ "$group": {
"_id": null,
"counts": {
"$push": {
"k": "$_id",
"v": "$count"
}
}
} },
{ "$replaceRoot": {
"newRoot": { "$arrayToObject": "$counts" }
} }
])
Output
{
a: 3,
b: 1,
c: 2,
d: 1,
f: 1
}
Using MongoDB 3.2 and below:
The following aggregation pipeline operation uses the $unwind stage to output a document for each element in the actors array and the $group stage to group the documents by the value in the actors array then
counts the number of documents per each group (which gives the occurrence of the array elements as a group) by way of the $sum operator:
db.collection.aggregate([
{ "$unwind" : "$actors" },
{ "$group": { "_id": "$actors", "count": { "$sum": 1} } }
])
The operation returns the following results which would be a close match to your expectations but won't give you the documents as key/value pair:
/* 0 */
{
"result" : [
{
"_id" : "f",
"count" : 1
},
{
"_id" : "d",
"count" : 1
},
{
"_id" : "c",
"count" : 2
},
{
"_id" : "b",
"count" : 1
},
{
"_id" : "a",
"count" : 3
}
],
"ok" : 1
}