Ottieni righe "circostanti" nella query NHibernate
-
22-07-2019 - |
Domanda
Sto cercando un modo per recuperare il "circostante". righe in una query NHibernate con una chiave primaria e un ordinamento?
es. Ho una tabella con voci di registro e voglio visualizzare la voce con la chiave primaria 4242 e le precedenti 5 voci nonché le seguenti 5 voci ordinate per data (non esiste una relazione diretta tra data e chiave primaria). Tale query dovrebbe restituire 11 righe in totale (purché non siamo vicini a nessuna delle due estremità).
La tabella delle voci di registro può essere enorme e non è possibile recuperare tutto per capirlo.
Esiste un concetto come il numero di riga che può essere utilizzato da NHibernate? Il database sottostante sarà SQlite o Microsoft SQL Server.
Modificato Esempio aggiunto
Immagina dati come i seguenti:
Id Time
4237 10:00
4238 10:00
1236 10:01
1237 10:01
1238 10:02
4239 10:03
4240 10:04
4241 10:04
4242 10:04 <-- requested "center" row
4243 10:04
4244 10:05
4245 10:06
4246 10:07
4247 10:08
Quando richiediamo la voce con la chiave primaria 4242 dovremmo ottenere le righe da 1237, 1238 e 4239 a 4247. L'ordine è in base a Time, Id.
È possibile recuperare le voci in una singola query (che ovviamente può includere sottoquery)? Time è una colonna non univoca, quindi diverse voci hanno lo stesso valore e in questo esempio non è possibile modificare la risoluzione in modo da renderla unica!
Soluzione
"non esiste una relazione diretta tra data e chiave primaria" significa che le chiavi primarie non sono in un ordine sequenziale?
Quindi lo farei così:
Item middleItem = Session.Get(id);
IList<Item> previousFiveItems = Session.CreateCriteria((typeof(Item))
.Add(Expression.Le("Time", middleItem.Time))
.AddOrder(Order.Desc("Time"))
.SetMaxResults(5);
IList<Item> nextFiveItems = Session.CreateCriteria((typeof(Item))
.Add(Expression.Gt("Time", middleItem.Time))
.AddOrder(Order.Asc("Time"))
.SetMaxResults(5);
Esiste il rischio di avere più articoli contemporaneamente.
Modifica
Ora dovrebbe funzionare.
Item middleItem = Session.Get(id);
IList<Item> previousFiveItems = Session.CreateCriteria((typeof(Item))
.Add(Expression.Le("Time", middleItem.Time)) // less or equal
.Add(Expression.Not(Expression.IdEq(middleItem.id))) // but not the middle
.AddOrder(Order.Desc("Time"))
.SetMaxResults(5);
IList<Item> nextFiveItems = Session.CreateCriteria((typeof(Item))
.Add(Expression.Gt("Time", middleItem.Time)) // greater
.AddOrder(Order.Asc("Time"))
.SetMaxResults(5);
Altri suggerimenti
Questo dovrebbe essere relativamente semplice con l'API dei criteri di NHibernate:
List<LogEntry> logEntries = session.CreateCriteria(typeof(LogEntry))
.Add(Expression.InG<int>(Projections.Property("Id"), listOfIds))
.AddOrder(Order.Desc("EntryDate"))
.List<LogEntry>();
Qui il tuo listOfIds
è solo un elenco fortemente tipizzato di numeri interi che rappresentano gli ID delle voci che desideri recuperare (numeri interi da 4242-5 a 4242 + 5).
Naturalmente puoi anche aggiungere espressioni
che ti consentono di recuperare ID maggiori di 4242-5 e inferiori a 4242 + 5.
La soluzione di Stefan funziona sicuramente, ma esiste un modo migliore utilizzando una singola subquery selezionata e nidificata:
ICriteria crit = NHibernateSession.CreateCriteria(typeof(Item));
DetachedCriteria dcMiddleTime =
DetachedCriteria.For(typeof(Item)).SetProjection(Property.ForName("Time"))
.Add(Restrictions.Eq("Id", id));
DetachedCriteria dcAfterTime =
DetachedCriteria.For(typeof(Item)).SetMaxResults(5).SetProjection(Property.ForName("Id"))
.Add(Subqueries.PropertyGt("Time", dcMiddleTime));
DetachedCriteria dcBeforeTime =
DetachedCriteria.For(typeof(Item)).SetMaxResults(5).SetProjection(Property.ForName("Id"))
.Add(Subqueries.PropertyLt("Time", dcMiddleTime));
crit.AddOrder(Order.Asc("Time"));
crit.Add(Restrictions.Eq("Id", id) || Subqueries.PropertyIn("Id", dcAfterTime) ||
Subqueries.PropertyIn("Id", dcBeforeTime));
return crit.List<Item>();
Questa è la sintassi di NHibernate 2.0 ma lo stesso vale per le versioni precedenti in cui invece di Restrizioni si usa Expression.
L'ho testato su un'applicazione di test e funziona come pubblicizzato