mongodb find by comparing field values - mongodb

Is it possible to express the following SQL query in mongodb:
SELECT * FROM table AS t WHERE t.field1 > t.filed2;
edit:
To summarize:.
using a third field storing "field1 - field2" is almost perfect, but requires a little extra maintenance.
$where will load and eval in JavaScript and won't use any indexes. No good for large data.
map/reduce has the same problem and will go trough all records even if we need only one

You can do this using $where:
db.coll.find( { $where: "this.field1 > this.field2" } );
But:
Javascript executes more slowly than
the native operators, but it is very flexible
If performance is an issue better to go with way suggested by #yi_H.

You could store in your document field1 - field2 as field3, then search for { field3: { $gt: 0 } }
It also possible to get matching documents with mapreduce.

You can use a $where. Just be aware it will be fairly slow (has to execute Javascript code on every record) so combine with indexed queries if you can.
db.T.find( { $where: function() { return this.Grade1 > this.Grade2 } } );
or more compact:
db.T.find( { $where : "this.Grade1 > this.Grade2" } );

Related

Mongodb query to select less than and equal based on custom comparator

I am using mongodb database and I need to run less than and equal filter based on custom comparator. Following is more details.
"profile" collection is having "level" field as string
{"name":"Test1", "level":"intermediate"}
Following are value of level and its corresponding weight
novice
intermediate
experienced
advance
I want to write query like as below so that it should return all the profile collection which level less than and equal to "experienced" (i.e. includes result for "novice", "intermediate" and "experienced"
db.profile.find( { level: { $lte: "experienced" } } )
I understand, I need to provide custom comparator. But how can i do?
You can't use custom comparators in a MongoDB Query. The ones available are: $eq, $gt, $gte, $lt, $lte, $ne, $in, $nin.
You can, however, use $in to get what you want:
db.profile.find( { level: { $in: [ "experienced", "intermediate ", "novice" ] } } );

Can mongodb do this SQL?

In MySQL Query:
UPDATE $TABLE SET `col1`=`col2` WHERE 1;
UPDATE $TABLE SET `col1`=IF(`col1`>100, `col1`-100, `col1`) WHERE 1;
Can MongoDB do like this?
What you appear to want to do is a conditional update - update the document based on its current state. MongoDB only has very limited support for this. See the update operators for details.
For the first query, it would be best to retrieve the documents, change them, and then save them. Unless your intention is not to copy the field but to rename it. Then you can use $rename.
For the second, it would be possible with this query:
db.collection.update(
{ field1: { $gt: 100} },
{ $inc: { field1, -100 },
{ multi: true}
);
By the way: You can also use the same pattern in SQL. It would likely work faster than using an if-condition.
UPDATE $TABLE SET `col1`=`col1`-100 WHERE `col1 ` > 100

How to sort by the value of number1 plus number2 in the mongodb

I prepare get some objects in the mongodb.
{number1:123,number2:321}
{number1:222,number2:4532}
For example, these are what i have in the database. But now, i want to get them by the value of number1 + number2.
How to do it ?
One option that no one has actually said here is to use the aggregation framework. Better than using $where since it will be faster and you can actually sort on that return and more as such:
db.c.aggregate([
{'$project': {sumField: {$add: ['$number1', '$number2']}}},
{'$sort': {sumField: 1}}
]);
Like so. Better than using the inbuilt JS, plus with an index and covered query you could make this faster since aggregation, unlike $where can actually use an index.
Use $where operator
db.myCollection.find( {'$where':'this.number1 + this.number2 = 200'})
Adding a virtual to your schema in mongodb will help you
YourSchema.virtual('numbersum')
.get(function () {
return this.number1 + ' ' + this.number2;
})
Then, you´ll be able to do that:
db.yourcollection.find().sort({numbersum:1});
Check this answer. Sorting by virtual field in mongoDB (mongoose)
EDIT this will work only when using mongoose as connector

Select distinct more than one field using MongoDB's map reduce

I want to execute this SQL statement on MongoDB:
SELECT DISTINCT book,author from library
So far MongoDB's DISTINCT only supports one field at a time. For more than one field, we have to use GROUP command or map-reduce.
I have googled a way to use GROUP command:
db.library.group({
key: {book:1, author:1},
reduce: function(obj, prev) { if (!obj.hasOwnProperty("key")) {
prev.book = obj.book;
prev.author = obj.author;
}},
initial: { }
});
However this approach only supports up to 10,000 keys. Anyone know how to use map reduce to solve this problem?
Take a look at this article which explains how to find unique articles using map-reduce in MongoDB.
Your emit statement is going to look something like:
emit({book: this.book, author: this.author}, {exists: 1});
and your reduce can be even simpler than the example since you don't care about how many there are for each grouping.
return {exists: 1};
In case someone faces the similar problem. This is the full solution:
Map step
map= "function(){
emit(
{book: this.book, author:this.author}, {exists: 1}
);
}"
Reduce step
reduce= "function(key, value){
return {exists: 1};
}"
Run the command
result= db.runCommand({
"mapreduce": "library",
"map": map,
"reduce": reduce,
"out: "result"
})
Get the result
db.result.find()

Mongo complex sorting?

I know how to sort queries in MongoDB by multiple fields, e.g., db.coll.find().sort({a:1,b:-1}).
Can I sort with a user-defined function; e.g., supposing a and b are integers, by the difference between a and b (a-b)?
Thanks!
UPDATE: This answer appears to be out of date; it seems that custom sorting can be more or less achieved by using the $project function of the aggregation pipeline to transform the input documents prior to sorting. See also #Ari's answer.
I don't think this is possible directly; the sort documentation certainly doesn't mention any way to provide a custom compare function.
You're probably best off doing the sort in the client, but if you're really determined to do it on the server you might be able to use db.eval() to arrange to run the sort on the server (if your client supports it).
Server-side sort:
db.eval(function() {
return db.scratch.find().toArray().sort(function(doc1, doc2) {
return doc1.a - doc2.a
})
});
Versus the equivalent client-side sort:
db.scratch.find().toArray().sort(function(doc1, doc2) {
return doc1.a - doc2.b
});
Note that it's also possible to sort via an aggregation pipeline and by the $orderby operator (i.e. in addition to .sort()) however neither of these ways lets you provide a custom sort function either.
Ran into this and this is what I came up with:
db.collection.aggregate([
{
$project: {
difference: { $subtract: ["$a", "$b"] }
// Add other keys in here as necessary
}
},
{
$sort: { difference: -1 }
}
])
Why don't create the field with this operation and sort on it ?