MongoKitten:如何按生成字段Score进行排序
Hey there! I’ve worked with MongoKitten quite a bit, so let me help you sort by that computed Score field you’re generating.
The key thing to remember here is that when you’re working with computed fields, you need to use an aggregation pipeline instead of a basic find() + sort setup. Here’s why: computed fields are created after the initial query in projection, but sorting needs to happen on the database side before results are returned—so aggregation lets you sequence these steps correctly.
Step-by-Step Implementation
Let’s break this down with code examples:
First, define your aggregation pipeline
You’ll want to first generate theScorefield (using either$addFieldsto keep existing fields or$projectto explicitly select fields), then add a$sortstage to order by that new field.Here’s a basic example where we calculate
Scoreas a sum of two existing fields, then sort descending:import MongoKitten let database = try await MongoDatabase.connect("mongodb://localhost:27017/yourDB") let collection = database["yourCollection"] // Build the aggregation pipeline let pipeline: [AggregationStage] = [ // Stage 1: Compute the Score field (replace this with your actual calculation logic) .addFields([ "Score": .expression(.add([.field("upvotes"), .field("downvotes")])) ]), // Stage 2: Sort by the computed Score field (use .ascending for ascending order) .sort(["Score": .descending]) ] // Execute the pipeline and fetch results let sortedResults = try await collection.aggregate(pipeline).toArray()For more complex Score calculations
If yourScoreuses conditional logic or other operators (like multiplication, averages, etc.), you can expand the expression in theaddFieldsstage. For example:.addFields([ "Score": .expression(.cond( if: .greaterThan(.field("views"), .int(1000)), then: .multiply([.field("upvotes"), .double(1.2)]), else: .field("upvotes") )) ])
Common Pitfall to Avoid
You might have tried using a find() query with a projection and sort like this, which won’t work:
// ❌ This won't sort by Score because the field is only generated in the projection (after sorting) let badQuery = collection.find() .projection([ "Score": .expression(.add([.field("a"), .field("b")])), "_id": 1 ]) .sort(["Score": .descending])
The problem here is that the sort runs before the projection, so MongoDB doesn’t know about the Score field yet. Aggregation fixes this by enforcing the order of operations.
Pro Tip for Performance
If you’re filtering documents before calculating the score, add a $match stage as the first step in your pipeline. This reduces the number of documents that need to be processed in subsequent stages, making your query faster:
let pipeline: [AggregationStage] = [ .match("category" == "tech"), // Filter first! .addFields(["Score": .expression(...)]), .sort(["Score": .descending]) ]
Let me know if you need help adapting this to your specific Score calculation logic—I’m happy to tweak the examples further!
内容的提问来源于stack exchange,提问作者Burf2000

