|
| 1 | +package aggregations |
| 2 | + |
| 3 | +import ( |
| 4 | + "common" |
| 5 | + "context" |
| 6 | + "dodec/types" |
| 7 | + "dodec/utils" |
| 8 | + "fmt" |
| 9 | + "time" |
| 10 | + |
| 11 | + "go.mongodb.org/mongo-driver/v2/bson" |
| 12 | + "go.mongodb.org/mongo-driver/v2/mongo" |
| 13 | +) |
| 14 | + |
| 15 | +// FindUsageExamplesForMonth looks for docs pages in Atlas that have had a new usage example added during the target month. |
| 16 | +// We get a count of new usage examples matching this criteria, return the count and the page_id, and |
| 17 | +// track the product and sub-product in the types.NewAppliedUsageExampleCounterByProductSubProduct |
| 18 | +func FindUsageExamplesForMonth(db *mongo.Database, collectionName string, productSubProductCounter types.NewAppliedUsageExampleCounterByProductSubProduct, monthForReporting time.Month, ctx context.Context) types.NewAppliedUsageExampleCounterByProductSubProduct { |
| 19 | + // Target a specific month (example for November 2025): |
| 20 | + targetYear := 2025 |
| 21 | + monthStart := time.Date(targetYear, monthForReporting, 1, 0, 0, 0, 0, time.UTC) |
| 22 | + monthEnd := monthStart.AddDate(0, 1, 0) // First day of next month |
| 23 | + // Define the aggregation pipeline |
| 24 | + pipeline := mongo.Pipeline{ |
| 25 | + // Find only page documents where the `nodes` value is not null |
| 26 | + {{"$match", bson.D{ |
| 27 | + {"_id", bson.D{{"$ne", "summaries"}}}, |
| 28 | + {"nodes", bson.D{{"$ne", nil}}}, // Ensure nodes is not null |
| 29 | + }}}, |
| 30 | + |
| 31 | + // Unwind the `nodes` value to match on specific node elements |
| 32 | + {{"$unwind", bson.D{{"path", "$nodes"}}}}, |
| 33 | + |
| 34 | + {{"$match", bson.D{ |
| 35 | + {"$and", bson.A{ |
| 36 | + // Filter for nodes that have been added during the target month |
| 37 | + bson.D{{"$and", bson.A{ |
| 38 | + bson.D{{"nodes.date_added", bson.D{{"$gte", monthStart}}}}, |
| 39 | + bson.D{{"nodes.date_added", bson.D{{"$lt", monthEnd}}}}, |
| 40 | + }}}, |
| 41 | + // Consider only usage examples |
| 42 | + bson.D{{"nodes.category", common.UsageExample}}, |
| 43 | + }}, |
| 44 | + }}}, |
| 45 | + |
| 46 | + // First group by Product and SubProduct |
| 47 | + bson.D{{"$group", bson.D{ |
| 48 | + {"_id", bson.D{ |
| 49 | + {"product", "$product"}, |
| 50 | + {"subProduct", bson.D{{"$ifNull", bson.A{"$sub_product", "None"}}}}, |
| 51 | + }}, |
| 52 | + {"nodesPerProduct", bson.D{{"$push", bson.D{ |
| 53 | + {"_id", "$_id"}, // Preserve original document _id |
| 54 | + {"nodes", "$nodes"}, // Collect nodes |
| 55 | + }}}}, |
| 56 | + }}}, |
| 57 | + // Unwind after the first group to regroup by original _id |
| 58 | + bson.D{{"$unwind", bson.D{{"path", "$nodesPerProduct"}}}}, |
| 59 | + // Regroup by original document _id within each Product and SubProduct |
| 60 | + bson.D{{"$group", bson.D{ |
| 61 | + {"_id", bson.D{ |
| 62 | + {"product", "$_id.product"}, |
| 63 | + {"subProduct", "$_id.subProduct"}, |
| 64 | + {"documentId", "$nodesPerProduct._id"}, |
| 65 | + }}, |
| 66 | + {"new_applied_usage_examples", bson.D{{"$push", "$nodesPerProduct.nodes"}}}, |
| 67 | + {"count", bson.D{{"$sum", 1}}}, |
| 68 | + }}}, |
| 69 | + // Optionally sort by count in descending order |
| 70 | + bson.D{{"$sort", bson.D{{"count", -1}}}}, |
| 71 | + } |
| 72 | + // Execute the aggregation |
| 73 | + collection := db.Collection(collectionName) |
| 74 | + cursor, err := collection.Aggregate(ctx, pipeline) |
| 75 | + if err != nil { |
| 76 | + println(fmt.Errorf("failed to execute aggregate query: %v", err)) |
| 77 | + return productSubProductCounter |
| 78 | + } |
| 79 | + defer cursor.Close(ctx) |
| 80 | + collectionPagesWithNewAppliedUsageExamples := make([]types.PageIdNewAppliedUsageExamples, 0) |
| 81 | + for cursor.Next(ctx) { |
| 82 | + var result types.PageIdNewAppliedUsageExamples |
| 83 | + if err = cursor.Decode(&result); err != nil { |
| 84 | + println(fmt.Errorf("failed to decode result document: %v", err)) |
| 85 | + return productSubProductCounter |
| 86 | + } |
| 87 | + |
| 88 | + // If a sub-product map for the product does not exist yet, create one |
| 89 | + if _, ok := productSubProductCounter.ProductSubProductCounts[result.ID.Product]; !ok { |
| 90 | + productSubProductCounter.ProductSubProductCounts[result.ID.Product] = make(map[string]int) |
| 91 | + } |
| 92 | + |
| 93 | + // The docs org would like to see a breakdown of focus areas. For the purpose of reporting this result, I'm arbitrarily |
| 94 | + // assigning some of the key focus areas as "sub-product" if a page ID contains a substring related to these focus |
| 95 | + // areas. That makes it easy to report on these things as sub-products even if they're not officially sub-products. |
| 96 | + resultAdjustedForFocusAreas := utils.GetFocusAreaAsSubProduct(result) |
| 97 | + if resultAdjustedForFocusAreas.ID.SubProduct != "None" { |
| 98 | + productSubProductCounter.ProductSubProductCounts[result.ID.Product][resultAdjustedForFocusAreas.ID.SubProduct] += resultAdjustedForFocusAreas.Count |
| 99 | + |
| 100 | + // Add the adjusted for focus area count to the product accumulator |
| 101 | + productSubProductCounter.ProductAggregateCount[result.ID.Product] += resultAdjustedForFocusAreas.Count |
| 102 | + } else { |
| 103 | + // If the subproduct is "None", just append the original count |
| 104 | + productSubProductCounter.ProductSubProductCounts[result.ID.Product][result.ID.SubProduct] += result.Count |
| 105 | + // Add the non-adjusted subproduct count to the product accumulator |
| 106 | + productSubProductCounter.ProductAggregateCount[result.ID.Product] += result.Count |
| 107 | + } |
| 108 | + collectionPagesWithNewAppliedUsageExamples = append(collectionPagesWithNewAppliedUsageExamples, resultAdjustedForFocusAreas) |
| 109 | + } |
| 110 | + if err = cursor.Err(); err != nil { |
| 111 | + println(fmt.Errorf("cursor encountered an error: %v", err)) |
| 112 | + return productSubProductCounter |
| 113 | + } |
| 114 | + if collectionPagesWithNewAppliedUsageExamples != nil && len(collectionPagesWithNewAppliedUsageExamples) > 0 { |
| 115 | + productSubProductCounter.PagesInCollections[collectionName] = collectionPagesWithNewAppliedUsageExamples |
| 116 | + } |
| 117 | + return productSubProductCounter |
| 118 | +} |
0 commit comments