1- import type { Filter , ModelFieldKey , OrderBy } from './types'
1+ import type { Filter , ModelFieldKey , OrderBy , TransactionOrMode } from './types'
22import type { Model } from './models'
33import { _objectStore } from './transaction'
44
@@ -74,6 +74,28 @@ export class Query<T extends Model> {
7474 return this
7575 }
7676
77+ /**
78+ * Add a limit to the query.
79+ * This means that the query will only return a maximum of `limit` items.
80+ * @param limit - The maximum amount of items to return
81+ * @returns - The query itself, to allow chaining
82+ */
83+ public limit ( limit : number ) : Query < T > {
84+ this . _limit = limit
85+ return this
86+ }
87+
88+ /**
89+ * Add an offset to the query.
90+ * This means that the query will skip the first `offset` items.
91+ * @param offset - The amount of items to skip
92+ * @returns - The query itself, to allow chaining
93+ */
94+ public offset ( offset : number ) : Query < T > {
95+ this . _skip = offset
96+ return this
97+ }
98+
7799 /**
78100 * Checks if the given instance fits the filters of the query.
79101 * @param instance - The instance to check
@@ -100,7 +122,7 @@ export class Query<T extends Model> {
100122 * Utility function to get the cursor of the query.
101123 * @returns
102124 */
103- private _getCursor ( transactionOrMode : IDBTransactionMode | IDBTransaction = 'readonly' ) : IDBRequest < IDBCursorWithValue | null > {
125+ private _getCursor ( transactionOrMode : TransactionOrMode = 'readonly' ) : IDBRequest < IDBCursorWithValue | null > {
104126 const store = _objectStore ( this . TargetModel . name , transactionOrMode )
105127 if ( this . _orderBy ) {
106128 const index = store . index ( this . _orderBy )
@@ -113,106 +135,107 @@ export class Query<T extends Model> {
113135 }
114136
115137 /**
116- * Executes the query and returns the first result.
117- * @returns - The first result of the query, or null if no result was found
138+ * Utility function to handle a cursor's cycle.
139+ * This implement the limit, offset and filter.
140+ * @param valueCallback - The callback to call for each value
141+ * @param transactionOrMode - The transaction or mode to use
142+ * @returns - A promise that resolves when the cursor is done
118143 */
119- async first ( transaction ?: IDBTransaction ) : Promise < T | null > {
120- const cursor = this . _getCursor ( transaction )
121- return new Promise < T | null > ( ( resolve , reject ) => {
122- cursor . onsuccess = ( ) => {
123- if ( ! cursor . result ) {
124- resolve ( null )
144+ private _cursorLogic ( valueCallback : ( value : IDBCursorWithValue ) => void , transactionOrMode ?: TransactionOrMode ) : Promise < void > {
145+ return new Promise < void > ( ( resolve , reject ) => {
146+ const request = this . _getCursor ( transactionOrMode )
147+ let matches = 0
148+ let skipped = 0
149+
150+ request . onsuccess = ( ) => {
151+ if ( ! request . result ) {
152+ resolve ( )
125153 }
126154 else {
127- if ( this . _fitsFilters ( cursor . result . value as T ) ) {
128- const instance = new this . TargetModel ( )
129- Object . assign ( instance , cursor . result . value )
130- resolve ( instance )
155+ // If we have a limit, we check if we have reached it
156+ if ( ( matches - skipped ) === this . _limit ) {
157+ resolve ( )
158+ return
159+ }
160+ // Apply the filters
161+ if ( this . _fitsFilters ( request . result . value as T ) ) {
162+ // If we have an offset, we wait until we have reached it
163+ if ( ! this . _skip || matches >= this . _skip )
164+ valueCallback ( request . result )
165+ else
166+ skipped += 1
167+
168+ matches += 1
131169 }
132- else { cursor . result . continue ( ) }
170+
171+ request . result . continue ( )
133172 }
134173 }
135- cursor . onerror = ( event ) => {
174+ request . onerror = ( event ) => {
136175 reject ( event )
137176 }
138177 } )
139178 }
140179
180+ /**
181+ * Executes the query and returns the first result.
182+ * @returns - The first result of the query, or null if no result was found
183+ */
184+ async first ( transaction ?: IDBTransaction ) : Promise < T | null > {
185+ let result : T | null = null
186+ this . _limit = 1
187+
188+ await this . _cursorLogic ( ( cursor ) => {
189+ const instance = new this . TargetModel ( )
190+ Object . assign ( instance , cursor . value )
191+ result = instance
192+ } , transaction )
193+
194+ return result
195+ }
196+
141197 /**
142198 * Executes the query and returns all the results.
143199 * @returns - All the results of the query
144200 */
145201 async all ( transaction ?: IDBTransaction ) : Promise < T [ ] > {
146- const cursor = this . _getCursor ( transaction )
147202 const result : T [ ] = [ ]
148- return new Promise < T [ ] > ( ( resolve , reject ) => {
149- cursor . onsuccess = ( ) => {
150- if ( ! cursor . result ) {
151- resolve ( result )
152- }
153- else {
154- if ( this . _fitsFilters ( cursor . result . value as T ) ) {
155- const instance = new this . TargetModel ( )
156- Object . assign ( instance , cursor . result . value )
157- result . push ( instance )
158- }
159- cursor . result . continue ( )
160- }
161- }
162- cursor . onerror = ( event ) => {
163- reject ( event )
164- }
165- } )
203+
204+ await this . _cursorLogic ( ( cursor ) => {
205+ const instance = new this . TargetModel ( )
206+ Object . assign ( instance , cursor . value )
207+ result . push ( instance )
208+ } , transaction )
209+
210+ return result
166211 }
167212
168213 /**
169214 * Executes the query and returns the number of results.
170215 * @returns - The amount of results of the query
171216 */
172217 async count ( transaction ?: IDBTransaction ) : Promise < number > {
173- const cursor = this . _getCursor ( transaction )
174218 let count = 0
175- return new Promise < number > ( ( resolve , reject ) => {
176- cursor . onsuccess = ( ) => {
177- if ( ! cursor . result ) {
178- resolve ( count )
179- }
180- else {
181- if ( this . _fitsFilters ( cursor . result . value as T ) )
182- count ++
183219
184- cursor . result . continue ( )
185- }
186- }
187- cursor . onerror = ( event ) => {
188- reject ( event )
189- }
190- } )
220+ await this . _cursorLogic ( ( ) => {
221+ count += 1
222+ } , transaction )
223+
224+ return count
191225 }
192226
193227 /**
194228 * Executes the query and deletes all the results.
195229 * @returns - The amount of results deleted
196230 */
197231 async delete ( transaction ?: IDBTransaction ) : Promise < number > {
198- const cursor = this . _getCursor ( transaction || 'readwrite' )
199232 let amount = 0
200- return new Promise < number > ( ( resolve , reject ) => {
201- cursor . onsuccess = ( ) => {
202- if ( ! cursor . result ) {
203- resolve ( amount )
204- }
205- else {
206- if ( this . _fitsFilters ( cursor . result . value as T ) ) {
207- amount += 1
208- cursor . result . delete ( )
209- }
210- cursor . result . continue ( )
211- }
212- }
213- cursor . onerror = ( event ) => {
214- reject ( event )
215- }
216- } )
233+
234+ await this . _cursorLogic ( ( cursor ) => {
235+ cursor . delete ( )
236+ amount += 1
237+ } , transaction || 'readwrite' )
238+
239+ return amount
217240 }
218241}
0 commit comments