You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Node.js中为Firestore实现Reddit式热门帖子查询?

Great question! Implementing Reddit's hotness algorithm in Firestore needs a bit of tweaking because, unlike SQL, Firestore doesn't let you run arbitrary computed calculations directly in query sort clauses. The best way to handle this aligns with Firestore's strengths—let's break it down.

This is the go-to method for performance and scalability. Instead of calculating hotness on the fly during a query, you store the precomputed value in each article document and update it whenever factors that affect hotness change.

How It Works

Reddit's hotness formula relies on two variables: the number of likes and the submission time. You'll need to:

  • Add a hotness numeric field to every article document.
  • Calculate the hotness value using your formula whenever the post is submitted, or when its like count changes.
  • Optionally, set up a periodic task to refresh hotness values over time (since the submission time factor decays as time passes—though frequent like updates will naturally keep this somewhat in sync).

Node.js Code Examples

1. Calculate Hotness Value

First, write a helper function to compute the hotness score based on your formula:

function calculateHotness(likes, submittedTimestamp) {
  // submittedTimestamp should be a Unix timestamp in seconds (like UNIX_TIMESTAMP in SQL)
  const logPart = Math.log10(Math.max(likes + 1, 1)); // Avoid log10(0)
  const timePart = submittedTimestamp;
  // Reddit's formula: log10(likes+1) * 86400 / 0.30102999566 + submitted timestamp
  return logPart * (86400 / 0.301029995663981) + timePart;
}

2. Compute Hotness When Submitting a Post

When creating a new article document, calculate and include the hotness field:

const admin = require('firebase-admin');
admin.initializeApp();

const db = admin.firestore();

async function createArticle(title, likes = 0) {
  const submittedTimestamp = Math.floor(Date.now() / 1000); // Convert to Unix seconds
  const hotness = calculateHotness(likes, submittedTimestamp);
  
  const newArticleRef = await db.collection('article').add({
    Title: title,
    Likes: likes,
    Submitted: submittedTimestamp,
    hotness: hotness
  });
  
  console.log('New article created with ID:', newArticleRef.id);
  return newArticleRef;
}

3. Update Hotness When Likes Change

Whenever a user likes/unlikes a post, recalculate and update the hotness field (using a transaction to avoid race conditions):

async function updateArticleLikes(articleId, delta) {
  // delta is +1 for a like, -1 for an unlike
  const articleRef = db.collection('article').doc(articleId);
  
  await db.runTransaction(async (transaction) => {
    const articleDoc = await transaction.get(articleRef);
    if (!articleDoc.exists) {
      throw new Error('Article not found');
    }
    
    const currentLikes = articleDoc.data().Likes;
    const submittedTimestamp = articleDoc.data().Submitted;
    const newLikes = currentLikes + delta;
    const newHotness = calculateHotness(newLikes, submittedTimestamp);
    
    transaction.update(articleRef, {
      Likes: newLikes,
      hotness: newHotness
    });
  });
  
  console.log(`Updated likes for article ${articleId}, new hotness calculated`);
}

4. Optional: Periodic Hotness Refresh (Cloud Function)

To keep hotness accurate as time passes (even for posts with no new likes), set up a scheduled Cloud Function to recalculate hotness for all articles:

const functions = require('firebase-functions');

exports.refreshHotness = functions.pubsub.schedule('every 1 hours').onRun(async (context) => {
  const articlesSnapshot = await db.collection('article').get();
  
  const batch = db.batch();
  articlesSnapshot.forEach((doc) => {
    const data = doc.data();
    const newHotness = calculateHotness(data.Likes, data.Submitted);
    batch.update(doc.ref, { hotness: newHotness });
  });
  
  await batch.commit();
  console.log('Hotness values refreshed for all articles');
  return null;
});
Alternative: Compute Hotness Client/Server-Side (Small Datasets Only)

If you're working with a tiny dataset (hundreds of posts max), you could fetch all articles, compute hotness in your Node.js code, then sort them. This is not scalable for large collections, since you'll have to load every document into memory.

Code Example

async function getHotArticles() {
  const articlesSnapshot = await db.collection('article').get();
  
  const articlesWithHotness = articlesSnapshot.docs.map((doc) => {
    const data = doc.data();
    const hotness = calculateHotness(data.Likes, data.Submitted);
    return {
      ...data,
      id: doc.id,
      hotness: hotness
    };
  });
  
  // Sort by hotness descending
  articlesWithHotness.sort((a, b) => b.hotness - a.hotness);
  return articlesWithHotness;
}
Final Recommendation

Stick with the precomputed hotness field approach. It's efficient, aligns with Firestore's query model, and scales well as your collection grows. The periodic refresh is optional but helpful for older posts that don't get new likes regularly.

内容的提问来源于stack exchange,提问作者Joanthan Ahrenkiel-Frellsen

火山引擎 最新活动