Nel precedente script (https://www.aspitalia.com/script/1268/Analisi-Dati-Aggregation-Framework-MongoDB.aspx) abbiamo esaminato alcune delle operazioni che possiamo usare in una pipeline di Aggregation Framework di MongoDB.
Ci sono situazioni in cui la nostra query di aggregazione è talmente complessa o coinvolge così tante collezioni da richiedere svariati secondi per essere completata. Questo periodo di attesa avrà un effetto negativo sulla navigazione dei nostri utenti, nel momento in cui desiderano visualizzare i risultati di tale aggregazione.
Fortunatamente, MongoDB ci permette di "esportare" i risultati della query di aggregazione in una propria collezione che, analogamente ad una cache, ci consentirà di recuperarli velocemente in ogni successiva query.
Per far questo, usiamo il metodo Out che deve necessariamente essere usato come ultima operazione nella pipeline.
_db.GetCollection<Restaurant>("restaurants") .Aggregate() //Qui operazioni di aggregazione, come illustrato nello script precedente //E, per ultima, l'invocazione al metodo Out in cui indichiamo il nome della //collection di destinazione, in cui saranno esportati i risultati .Out("aggregationResults");
Se stiamo realizzando un'applicazione ASP.NET Core MVC che visualizza i risultati dell'aggregazione, dobbiamo tener presente che:
- Più utenti potrebbero visitare la pagina contemporaneamente ma questo non deve scatenare esportazioni concorrenti dei dati, che causerebbero un errore;
- E' importante che la query di aggregazione venga rieseguita dopo un certo lasso di tempo, affinché gli utenti possano visualizzare dati freschi.
Per ottemperare ad entrambi questi requisiti, possiamo sfruttare il servizio di cache di ASP.NET Core che, grazie al suo meccanismo di scadenza delle chiavi, ci consentirà di capire quando è il momento di rieseguire l'esportazione.
Iniziamo configurando il servizio di cache dalla classe Startup. All'interno del suo metodo ConfigureServices aggiungiamo:
services.AddMemoryCache();
All'interno dello stesso metodo, configuriamo anche i servizi IMongoClient e IMongoDatabase del MongoDb.Driver come illustrato in questo precedente script:
https://www.aspitalia.com/script/1265/Utilizzare-MongoDB-ASP.NET-Core.aspx
Il nostro metodo ConfigureServices apparirà dunque così:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddMemoryCache(); services.AddSingleton<IMongoClient>(x => new MongoClient(Configuration.GetConnectionString("mongo"))); services.AddTransient<IMongoDatabase>(x => x.GetService<IMongoClient>().GetDatabase("test")); }
Ora creiamo un Controller per ASP.NET Core MVC e l'action che si occuperà di visualizzare i risultati della query di aggregazione.
public class AggregationResultsController : Controller { private readonly IMongoDatabase _db; private readonly IMemoryCache _cache; public AggregationResultsController(IMongoDatabase db, IMemoryCache cache) { _db = db; _cache = cache; } //Questa action supporta la paginazione dei risultati aggregati public async Task<IActionResult> Index(int page) { //Assicuriamoci che non sia stata fornita una pagina inferiore ad 1 page = Math.Max(1, page); //Impostiamo il numero dei documenti da visualizzare per pagina const int pageSize = 10; //Otteniamo il nome della collection che contiene i risultati dell'aggregazione var aggregationResultsCollectionName = GetAggregationResultsCollectionName(_db.GetCollection<Restaurant>("restaurants")); //Usiamo i metodi Find, Skip e Limit per ottenere una pagina dei risultati di aggregazione var results = await _db.GetCollection<Result>(aggregationResultsCollectionName) .Find(FilterDefinition<Result>.Empty) .Skip(pageSize*(page-1)) .Limit(pageSize) .ToListAsync(); //Visualizziamo i risultati nella view return View(results); } //Questo metodo si occupa di eseguire la query di aggregazione ed esportare i risultati private string GetAggregationResultsCollectionName(IMongoCollection<Restaurant> collection) { //Sfruttando il meccanismo di scadenza delle chiavi cache, possiamo //fare in modo che la query di aggregazione venga eseguita al massimo //una volta ogni 10 minuti TimeSpan refreshAggregationResultsEvery = TimeSpan.FromMinutes(10); //Nome della collection in cui esporteremo i risultati grazie al metodo Out string aggregationResultsCollectionName = "restaurantsAggregated"; //Se la chiave cache non è presente o è scaduta, la query di aggregazione //verrà eseguita ed esporterà i risultati nell'apposita collection return _cache.GetOrCreate(aggregationResultsCollectionName, entry => { //Impostiamo la scadenza della chiave cache entry.AbsoluteExpiration = DateTimeOffset.Now.Add(refreshAggregationResultsEvery); //Usiamo la classe Lazy che ci garantisce che due o più thread concorrenti non vadano //ad eseguire contemporaneamente l'esportazione della query di aggregazione return new Lazy<string>(() => { collection.Aggregate() //Qui le operazioni di aggregazione //Poi usiamo il metodo Out per esportare i risultati .Out(aggregationResultsCollectionName); return aggregationResultsCollectionName; }); }).Value; } }
In questo esempio, sfruttiamo sia la cache di ASP.NET Core che la classe Lazy<T> per assicurarci che venga eseguita solo un'esportazione alla volta. Alla prima esecuzione di pagina, l'utente dovrà attendere qualche secondo che l'aggregazione e l'esportazione dei risultati siano completate. Dalla successiva richiesta in poi, il caricamento sarà molto rapido.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Utilizzare il nuovo modello GPT-4o con Azure OpenAI
Utilizzare la versione generica di EntityTypeConfiguration in Entity Framework Core
Limitare le richieste lato server con l'interactive routing di Blazor 8
Utilizzare Tailwind CSS all'interno di React: installazione
Ottimizzazione dei block template in Angular 17
Esporre i propri servizi applicativi con Semantic Kernel e ASP.NET Web API
Effettuare il refresh dei dati di una QuickGrid di Blazor
Utilizzare Tailwind CSS all'interno di React: primi componenti
Esportare ed analizzare le issue di GitHub con la CLI e GraphQL
Proteggere le risorse Azure con private link e private endpoints
Gestione degli stili CSS con le regole @layer
Persistere la ChatHistory di Semantic Kernel in ASP.NET Core Web API per GPT