MongoDB中为每个文档统计数组内各值的出现次数
Hey there! Let's figure out how to add that occurrences field you need. You want to count each value's frequency in the types array and store it as an object—here are two straightforward ways to do this with MongoDB:
1. Aggregation Query (Real-Time Calculation, No Permanent Changes)
If you just need to retrieve documents with the occurrences field without modifying your collection, use the aggregation framework with $reduce (this is more efficient than unwinding large arrays):
db.collection.aggregate([ { $addFields: { occurrences: { $arrayToObject: { $reduce: { input: "$types", initialValue: [], in: { $let: { vars: { existingEntry: { $indexOfArray: ["$$value.k", "$$this"] } }, in: { $cond: { if: { $ne: ["$$existingEntry", -1] }, then: { $map: { input: "$$value", as: "item", in: { $cond: { if: { $eq: ["$$item.k", "$$this"] }, then: { k: "$$item.k", v: { $add: ["$$item.v", 1] } }, else: "$$item" } } } }, else: { $concatArrays: ["$$value", [{ k: "$$this", v: 1 }]] } } } } } } } } } }, // Optional: Convert numeric counts to strings (matches your desired output) { $addFields: { occurrences: { $arrayToObject: { $map: { input: { $objectToArray: "$occurrences" }, as: "count", in: { k: "$$count.k", v: { $toString: "$$count.v" } } } } } } } ])
How this works:
$reduceiterates over each element in thetypesarray, building up an array of key-value pairs where the key is the type and the value is its count.$arrayToObjectconverts that array into theoccurrencesobject you want.- The optional final step converts numeric counts to strings to match your exact desired output.
2. Permanent Update (Add occurrences to Documents)
If you want to permanently add the occurrences field to every document in your collection (MongoDB 4.2+ required, since we use an aggregation pipeline in updateMany):
db.collection.updateMany( {}, // Matches all documents in the collection [ { $set: { occurrences: { $arrayToObject: { $reduce: { input: "$types", initialValue: [], in: { $let: { vars: { existingEntry: { $indexOfArray: ["$$value.k", "$$this"] } }, in: { $cond: { if: { $ne: ["$$existingEntry", -1] }, then: { $map: { input: "$$value", as: "item", in: { $cond: { if: { $eq: ["$$item.k", "$$this"] }, then: { k: "$$item.k", v: { $add: ["$$item.v", 1] } }, else: "$$item" } } } }, else: { $concatArrays: ["$$value", [{ k: "$$this", v: 1 }]] } } } } } } } } } }, // Optional: Convert counts to strings { $set: { occurrences: { $arrayToObject: { $map: { input: { $objectToArray: "$occurrences" }, as: "count", in: { k: "$$count.k", v: { $toString: "$$count.v" } } } } } } } ] )
Notes for older MongoDB versions:
If you're on a version before 4.2 (which doesn't support aggregation pipelines in updates), you can either:
- Use a client-side script (e.g., Node.js, Python) to fetch each document, calculate the counts, and update the document.
- Run the aggregation query from Method 1, export the results, and re-insert them into the collection (make sure to handle
_idcorrectly to avoid duplicates).
内容的提问来源于stack exchange,提问作者sabfon

