Sorting priority - mongodb

I have a collection Answers in a MongoDB database and I sort the documents with the number of upvotes each answer has.
Answers.find({}, {sort: {voteCount: -1}})
Some of these answers, however, are posted by instructors and they should be sorted before of "regular" answers. Answer posted by instructors have a field isInstructor: true.
How do I retrieve my list of answers sorted in a way that instructors' answers come first (sorted by voteCounts as well) and then, after those, the normal answers (still sorted by voteCounts)?

Well As mention in my comment you must sort your answers by isInstructor in descending order and by voteCount in descending order. Also note that the second argument to .find is a projection document. Thus to sort your document you need to use the cursor.sort method instead.
Answers.find().sort({ "isInstructor": -1, "voteCount": -1 })

Use the cursor's sort() method, listing the fields you would like to sort by, in order. EG
db.answers.find({}).sort(isInstructor: -1, voteCount: -1})
Reference: MongoDB Manual

Related

ORDER BY FIELD in mongoDB without using aggregate [duplicate]

This question already has answers here:
How to ORDER BY FIELD VALUE in MongoDB
(2 answers)
Closed 6 years ago.
I there a way to do ORDER BY FIELD in MongoDB?
In Mysql there is something like:
SELECT id, name, priority
FROM mytable
ORDER BY FIELD(priority, "core", "board", "other")
Can this be achieved in MongoDB?
Similar query is answered in : How to ORDER BY FIELD VALUE in MongoDB
This uses aggregate function. What I want is without aggregate and something with in find.
Not currently. MongoDB only allows to sort a field by normal old ascending and descending order. You cannot give a custom sort order of priority or anything.
The closest would be to use the aggregation framework to assign values to the field to sort the way you want. Though this will not work well with large queries so I would not recommend it.
Yes MongoDB has "Order By" functionality it is achieved using sort() see below for two primitive examples of how to use:
db.collection.find({}).sort({"fieldName" : 1})
For descending sort use:
db.collection.find({}).sort({"fieldName" : -1})

Mongo find and rank result [duplicate]

This question already has answers here:
In MongoDB search in an array and sort by number of matches
(2 answers)
Closed 7 years ago.
I have a mongo collection that has docs similar to the schema below. And I have a ui from which a user could filter on the attributes (using logical or). Since its using OR I want the results to be ordered such that I first get the ones that match the most filters. For example: if i filter on author "auth1" and "tag1" I get back both records but the second record is on top. In this example I just have 2 attr to filter on, but there are about 20.
Do you have any suggestions of the best way of tackling this? I was thinking of writing a map-reduce query to compute the "match rank".
{name: "bookName", tags:["tag1"], authors: [] }
{name: "bookName2", tags:["tag1", "tag2"], authors: ["auth1", "auth2"] }
If I understand your proble, You wants sort the result By authors.length and tags.length.
The problem, in MongoDB (I test on 2.6) with the sort(), it is impossible to sort by two parallels arrays. You can sort by album.length or tags.length but not both.
// sort By max to min authors
db.getCollection('rank').find({}).sort({ authors : -1 });
// sort by max to min tags
db.getCollection('rank').find({}).sort({ tags : -1 });
If you watn sort your result by both of them, you should use the Aggregation Framework. You have a great explanation here : https://stackoverflow.com/a/9040269/1506914
It is possible using Aggregation Framework. Have a look at this question, it's similar to yours.

MongoDB Query Nested Array Search

I need to query documents with mongoDb that contain nested arrays. I see a lot of examples using the simple $in operator. The only problem is that I strictly need to check for proper subsets.
Consider the following document.
{data: [[1,2,3], [4,5,6]]}
The query needs to be able to get documents with all of [1,2,3] where 1,2,3 can be in any order, which rules out the following query, because it will only match in the correct order.
{data:{$elemMatch:{$all:[[1,2,3]]}}}
I've also tried nested $elemMatch operators with no success, because the $in operator will return the document even if only one element matches such as the following.
{data:{$elemMatch:{$elemMatch:{$in:[1,4]}}}}
Not sure what your actual query looks like, but this should do what you need:
db.documentDto.find({"some_field":{"$elemMatch":{"$in":[1,2,3]}} })
I haven't got a complete answer (and not much time as its late here) but I would consider
Using aggregation pipeline instead of a query if your not already
Use $unwind operator to deconstruct your nested arrays
Use $sort to sort the contents of the arrays - so you can now compare
Use $match to filter out the arrays which don't fit the array subset values as you can now check based on order.
Use $group to group the result back together based on the _id value
Ref:
http://docs.mongodb.org/manual/reference/operator/aggregation-pipeline/ will give you info on each of the above.
From a quick search I came up with a similar question/example that might be helpful: Mongodb sort inner array

In Mongodb, how to retrieve the subset of an object that matches a condition?

What I'm trying to do:
Filter a field of a collection that matches a given condition. Instead of returning every item in the field (which is an array of items), I only want to see matched items.
Similar to
select items from test where items.histPrices=[10,12]
It is also similar to what's found on the mongodb website here: http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields
Here's what I have been trying:
db.test.save({"name":"record", "items":[{"histPrices":[10,12],"name":"stuff"}]})
db.test.save({"name":"record", "items":[{"histPrices":[10,12],"name":"stuff"},
{"histPrices":[12,13],"name":"stuff"},{"histPrices":[11,14],"name":"stuff"}]})
db.test.find({},{"name":1,"items.histPrices":[10, 12]})
It will return all the objects that have a match for items.histPrices:[10,12], including ALL of the items in items[]. But I don't want the ones that don't match the condition.
From the comments left on Mongodb two years ago, the solution to get only the items with that histPrices[10,12] is to do it with javascript code, namely, loop through the result set and filter out the other items.
I wonder if there's a way to do that with just the query.
Your find query is wrong
db.test.find({},{"name":1,"items.histPrices":[10, 12]})
Your condition statement should be in the first part of the find statement.In your query {} means fetch all documents similar to this sql
select items from test (no where clause)
you have to change your mongodb find to
db.test.find({"items.histPrices":[10, 12]},{"name":1})
make it work
since your items is an array and if you wanted to return only the matching sub item, you have to use positional operator
db.test.find({"items.histPrices":[10, 12]},{"name":1,'items.$':1})
When working with arrays Embedded to the Document, the best approach is the one suggested by Chien-Wei Huang.
I would just add another aggregation, with the $group (in cases the document is very long, you may not want to retrieve all its content, only the array elements) Operator.
Now the command would look like:
db.test.aggregate({$match:{name:"record"}},
{$unwind:"$items"},
{$match {"items.histPrices":[10, 12]}},
{$group: {_id: "$_id",items: {$push: "$items"}}});)
If you are interested to return only one element from the array in each collection, then you should use projection instead
The same kind of issue solved here:
MongoDB Retrieve a subset of an array in a collection by specifying two fields which should match
db.test.aggregate({$unwind:"$items"}, {$match:{"items.histPrices":[10, 12]}})
But I don't know whether the performance would be OK. You have to verify it with your data.
The usage of $unwind
If you want add some filter condition like name="record", just add another $march at first, ex:
db.test.aggregate({$match:{name:"record"}}, {$unwind:"$items"}, {$match:{"items.histPrices":[10, 12]}})
https://jira.mongodb.org/browse/SERVER-828
Get particular element from mongoDB array
MongoDB query to retrieve one array value by a value in the array

Time Complexity of $addToset vs $push when element does not exist in the Array

Given: Connection is Safe=True so Update's return will contain update information.
Say I have a documents that look like:
[{'a': [1]}, {'a': [2]}, {'a': [1,2]}]
And I issue:
coll.update({}, {'$addToSet': {'a':1}}, multi=True)
The result would be:
{u'connectionId': 28,
u'err': None,
u'n': 3,
u'ok': 1.0,
u'updatedExisting': True
}
Even when come documents already have that value. To avoid this I could issue a command.
coll.update({'a': {'$ne': 1}}, {'$push': {'a':1}}, multi=True)
What's the Time Complexity Comparison for $addToSet vs. $push with a $ne check ?
Looks like $addToSet is doing the same thing as your command: $push with a $ne check. Both would be O(N)
https://github.com/mongodb/mongo/blob/master/src/mongo/db/ops/update_internal.cpp
if speed is really important then why not use a hash:
instead of:
{'$addToSet': {'a':1}}
{'$addToSet': {'a':10}}
use:
{$set: {'a.1': 1}
{$set: {'a.10': 1}
Edit
Ok since I read your question wrong all along it turns out that actually you are looking at two different queries and judging the time complexity between them.
The first query being:
coll.update({}, {'$addToSet': {'a':1}}, multi=True)
And the second being:
coll.update({'a': {'$ne': 1}}, {'$push': {'a':1}}, multi=True)
First problem springs to mind here, no indexes. $addToSet, being an update modifier, I do not believe it uses an index as such you are doing a full table scan to accomplish what you need.
In reality you are looking for all documents that do not have 1 in a already and looking to $push the value 1 to that a array.
So 2 points to the second query even before we get into time complexity here because the first query:
Does not use indexes
Would be a full table scan
Would then do a full array scan (with no index) to $addToSet
So I have pretty much made my mind up here that the second query is what your looking for before any of the Big O notation stuff.
There is a problem to using big O notation to explain the time complexity of each query here:
I am unsure of what perspective you want, whether it is per document or for the whole collection.
I am unsure of indexes as such. Using indexes will actually create a Log algorithm on a however not using indexes does not.
However the first query would look something like: O(n) per document since:
The $addToSet would need to iterate over each element
The $addToSet would then need to do an O(1) op to insert the set if it does not exist. I should note I am unsure whether the O(1) is cancelled out or not (light reading suggests my version), I have cancelled it out here.
Per collection, without the index it would be: O(2n2) since the complexity of iterating a will expodentially increase with every new document.
The second query, without indexes, would look something like: O(2n2) (O(n) per document) I believe since $ne would have the same problems as $addToSet without indexes. However with indexes I believe this would actually be O(log n log n) (O(log n) per document) since it would first find all documents with a in then all documents without 1 in their set based upon the b-tree.
So based upon time complexity and the notes at the beginning I would say query 2 is better.
If I am honest I am not used to explaining in "Big O" Notation so this is experimental.
Hope it helps,
Adding my observation in difference between addToSet and push from bulk update of 100k documents.
when you are doing bulk update. addToSet will be executed separately.
for example,
bulkInsert.find({x:y}).upsert().update({"$set":{..},"$push":{ "a":"b" } , "$setOnInsert": {} })
will first insert and set the document. And then it executes addToSet query.
I saw clear difference of 10k between
db.collection_name.count() #gives around 40k
db.collection_name.count({"a":{$in:["b"]}}) # it gives only around 30k
But when replaced $addToSet with $push. both count query returned same value.
note: when you're not concerned about duplicate entry in array. you can go with $push.