Reputation: 1446
So I am using mongoose and node.js to access a mongodb database. I want to bump up each result based on a number (they are ordered by date created if none are bumped up). For example:
{ name: 'A',
bump: 0 },
{ name: 'B',
bump: 0 },
{ name: 'C',
bump: 2 },
{ name: 'D',
bump: 1 }
would be retreived in the order: C, A, D, B. How can this be accomplished (without iterating through every entry in the database)?
Upvotes: 2
Views: 439
Reputation: 17903
I don't think a purely query-based solution is possible with your document schema (I assume you have createdDate
and bump
fields). Instead, I suggest a single field called sortorder
to keep track of your desired retrieval order:
sortorder
is initially the creation timestamp. If there are no "bumps", sorting by this field gives the correct order.sortorder
is invalidated. So simply correct the sortorder
values: each time a "bump" occurs swap the sortorder
fields of the bumped document and the document directly ahead of it. This literally "bumps" the document up in the sort order.sortorder
.You can remove fields bump
and createdDate
if they are not used elsewhere.
As an aside, most social sites don't directly manipulate a post's display position based on its number of votes (or "bumps"). Instead, the number of votes is used to calculate a score. Then the posts are sorted and displayed by this score. In your case, you should combine createdDate
and bumps
into a single score that can be sorted in a query.
This site (StackOverflow.com) had a related meta discussion about how to determine "hot" questions. I think there was even a competition to come up with a new formula. The meta question also shared the formulas used by two other popular social news sites: Y Combinator Hacker News and Reddit.
Upvotes: 1
Reputation: 4962
Try something like this. Store a counter tracking the total # of threads, let's call it thread_count
, initially set to 0, so have a document somewhere that looks like {thread_count:0}
.
Every time a new thread is created, first call findAndModify()
using {$inc : {thread_count:1}}
as the modifier - i.e., increment the counter by 1 and return its new value.
Then when you insert the new thread, use the new value for the counter as the value for a field in its document, let's call it post_order
.
So each document you insert has a value 1 greater each time. For example, the first 3 documents you insert would look like this:
{name:'foo', post_order:1, created_at:... } // value of thread_count is at 1
{name:'bar', post_order:2, created_at:... } // value of thread_count is at 2
{name:'baz', post_order:3, created_at:... } // value of thread_count is at 3
etc.
So effectively, you can query and order by post_order
as ASCENDING, and it will return them in the order of oldest to newest (or DESCENDING for newest to oldest).
Then to "bump" a thread in its sorting order when it gets upvoted, you can call update()
on the document with {$inc:{post_order:1}}
. This will advance it by 1 in the order of result sorting. If two threads have the same value for post_order, created_at will differentiate which one comes first. So you will sort by post_order, created_at
.
You will want to have an index on post_order
and created_at
.
Upvotes: 2
Reputation: 9504
Not a pleasant answer, but the solution you request is unrealistic. Here's my suggestion:
Add an OrderPosition
property to your object instead of Bump.
Think of "bumping" as an event. It is best represented as an event-handler function. When an item gets "bumped" by whatever trigger in your business logic, the collection of items needs to be adjusted.
var currentOrder = this.OrderPosition
this.OrderPosition = currentOrder - bump; // moves your object up the list
// write a foreach loop here, iterating every item AFTER the items unadjusted
// order, +1 to move them all down the list one notch.
This does require iterating through many items, and I know you are trying to prevent that, but I do not think there is any other way to safely ensure the integrity of your item ordering - especially when relative to other pulled collections that occur later down the road.
Upvotes: 1
Reputation: 5996
Let's guess your code is the variable response
(which is an array), then I would do:
response.sort(function(obj1, obj2){
return obj2.bump - obj1.bump;
});
or if you want to also take in mind name order:
response.sort(function(obj1, obj2){
var diff = obj2.bump - obj1.bump;
var nameDiff = (obj2.name > obj1.name)?-1:((obj2.name < obj1.name)?1:0);
return (diff == 0) ? nameDiff : diff;
});
Upvotes: 1