@@ -40,14 +40,28 @@ user = db.records.create(
40
40
)
41
41
42
42
# Find records
43
- results = db.records.find({
43
+ result = db.records.find({
44
44
" where" : {
45
45
" age" : {" $gte" : 18 },
46
46
" name" : {" $startsWith" : " J" }
47
47
},
48
48
" limit" : 10
49
49
})
50
50
51
+ # Work with SearchResult
52
+ print (f " Found { len (result)} records out of { result.total} total " )
53
+
54
+ # Iterate over results
55
+ for record in result:
56
+ print (f " User: { record.get(' name' )} (Age: { record.get(' age' )} ) " )
57
+
58
+ # Check if there are more results
59
+ if result.has_more:
60
+ print (" There are more records available" )
61
+
62
+ # Access specific records
63
+ first_user = result[0 ] if result else None
64
+
51
65
# Create relationships
52
66
company = db.records.create(
53
67
label = " COMPANY" ,
@@ -83,6 +97,207 @@ db.records.create_many("COMPANY", {
83
97
})
84
98
```
85
99
100
+ ## SearchResult API
101
+
102
+ RushDB Python SDK uses a modern ` SearchResult ` container that follows Python SDK best practices similar to boto3, google-cloud libraries, and other popular SDKs.
103
+
104
+ ### SearchResult Features
105
+
106
+ - ** Generic type support** : Uses Python's typing generics (` SearchResult[T] ` ) with ` RecordSearchResult ` as a type alias for ` SearchResult[Record] `
107
+ - ** List-like access** : Index, slice, and iterate like a regular list
108
+ - ** Search context** : Access total count, pagination info, and the original search query
109
+ - ** Boolean conversion** : Use in if statements naturally (returns ` True ` if the result contains any items)
110
+ - ** Pagination support** : Built-in pagination information and ` has_more ` property
111
+
112
+ ### Basic Usage
113
+
114
+ ``` python
115
+ # Perform a search
116
+ result = db.records.find({
117
+ " where" : {" status" : " active" },
118
+ " limit" : 10 ,
119
+ " skip" : 20
120
+ })
121
+
122
+ # Check if we have results
123
+ if result:
124
+ print (f " Found { len (result)} records " )
125
+
126
+ # Access search result information
127
+ print (f " Total matching records: { result.total} " )
128
+ print (f " Current page size: { result.count} " )
129
+ print (f " Records skipped: { result.skip} " )
130
+ print (f " Has more results: { result.has_more} " )
131
+ print (f " Search query: { result.search_query} " )
132
+
133
+ # Iterate over results
134
+ for record in result:
135
+ print (f " Record: { record.get(' name' )} " )
136
+
137
+ # List comprehensions work
138
+ names = [r.get(' name' ) for r in result]
139
+
140
+ # Indexing and slicing
141
+ first_record = result[0 ] if result else None
142
+ first_five = result[:5 ]
143
+
144
+ # String representation
145
+ print (repr (result)) # SearchResult(count=10, total=42)
146
+ ```
147
+
148
+ ### SearchResult Constructor
149
+
150
+ ``` python
151
+ def __init__ (
152
+ self ,
153
+ data : List[T],
154
+ total : Optional[int ] = None ,
155
+ search_query : Optional[SearchQuery] = None ,
156
+ ):
157
+ """
158
+ Initialize search result.
159
+
160
+ Args:
161
+ data: List of result items
162
+ total: Total number of matching records (defaults to len(data) if not provided)
163
+ search_query: The search query used to generate this result (defaults to {})
164
+ """
165
+ ```
166
+
167
+ ### SearchResult Properties
168
+
169
+ | Property | Type | Description |
170
+ | -------------- | --------------- | ---------------------------------------- |
171
+ | ` data ` | ` List[T] ` | The list of result items (generic type) |
172
+ | ` total ` | ` int ` | Total number of matching records |
173
+ | ` count ` | ` int ` | Number of records in current result set |
174
+ | ` limit ` | ` Optional[int] ` | Limit that was applied to the search |
175
+ | ` skip ` | ` int ` | Number of records that were skipped |
176
+ | ` has_more ` | ` bool ` | Whether there are more records available |
177
+ | ` search_query ` | ` SearchQuery ` | The search query used to generate result |
178
+
179
+ > ** Implementation Notes:**
180
+ >
181
+ > - If ` search_query ` is not provided during initialization, it defaults to an empty dictionary ` {} `
182
+ > - The ` skip ` property checks if ` search_query ` is a dictionary and returns the "skip" value or 0
183
+ > - The ` has_more ` property is calculated as ` total > (skip + len(data)) ` , allowing for efficient pagination
184
+ > - The ` __bool__ ` method returns ` True ` if the result contains any items (` len(data) > 0 ` )
185
+
186
+ ### Pagination Example
187
+
188
+ ``` python
189
+ # Paginated search
190
+ page_size = 10
191
+ current_page = 0
192
+
193
+ while True :
194
+ result = db.records.find({
195
+ " where" : {" category" : " electronics" },
196
+ " limit" : page_size,
197
+ " skip" : current_page * page_size,
198
+ " orderBy" : {" created_at" : " desc" }
199
+ })
200
+
201
+ if not result:
202
+ break
203
+
204
+ print (f " Page { current_page + 1 } : { len (result)} records " )
205
+
206
+ for record in result:
207
+ process_record(record)
208
+
209
+ if not result.has_more:
210
+ break
211
+
212
+ current_page += 1
213
+ ```
214
+
215
+ ### RecordSearchResult Type
216
+
217
+ The SDK provides a specialized type alias for search results containing Record objects:
218
+
219
+ ``` python
220
+ # Type alias for record search results
221
+ RecordSearchResult = SearchResult[Record]
222
+ ```
223
+
224
+ This type is what's returned by methods like ` db.records.find() ` , providing type safety and specialized handling for Record objects while leveraging all the functionality of the generic SearchResult class.
225
+
226
+ ## Improved Record API
227
+
228
+ The Record class has been enhanced with better data access patterns and utility methods.
229
+
230
+ ### Enhanced Data Access
231
+
232
+ ``` python
233
+ # Create a record
234
+ user = db.records.create(" User" , {
235
+ " name" : " John Doe" ,
236
+ " email" : " john@example.com" ,
237
+ " age" : 30 ,
238
+ " department" : " Engineering"
239
+ })
240
+
241
+ # Safe field access with defaults
242
+ name = user.get(" name" ) # "John Doe"
243
+ phone = user.get(" phone" , " Not provided" ) # "Not provided"
244
+
245
+ # Get clean user data (excludes internal fields like __id, __label)
246
+ user_data = user.get_data()
247
+ # Returns: {"name": "John Doe", "email": "john@example.com", "age": 30, "department": "Engineering"}
248
+
249
+ # Get all data including internal fields
250
+ full_data = user.get_data(exclude_internal = False )
251
+ # Includes: __id, __label, __proptypes, etc.
252
+
253
+ # Convenient fields property
254
+ fields = user.fields # Same as user.get_data()
255
+
256
+ # Dictionary conversion
257
+ user_dict = user.to_dict() # Clean user data
258
+ full_dict = user.to_dict(exclude_internal = False ) # All data
259
+
260
+ # Direct field access
261
+ user_name = user[" name" ] # Direct access
262
+ user_id = user[" __id" ] # Internal field access
263
+ ```
264
+
265
+ ### Record Existence Checking
266
+
267
+ ``` python
268
+ # Safe existence checking (no exceptions)
269
+ if user.exists():
270
+ print (" Record is valid and accessible" )
271
+ user.update({" status" : " active" })
272
+ else :
273
+ print (" Record doesn't exist or is not accessible" )
274
+
275
+ # Perfect for validation workflows
276
+ def process_record_safely (record ):
277
+ if not record.exists():
278
+ return None
279
+ return record.get_data()
280
+
281
+ # Conditional operations
282
+ records = db.records.find({" where" : {" status" : " pending" }})
283
+ for record in records:
284
+ if record.exists():
285
+ record.update({" processed_at" : datetime.now()})
286
+ ```
287
+
288
+ ### String Representations
289
+
290
+ ``` python
291
+ user = db.records.create(" User" , {" name" : " Alice Johnson" })
292
+
293
+ print (repr (user)) # Record(id='abc-123', label='User')
294
+ print (str (user)) # User: Alice Johnson
295
+
296
+ # For records without names
297
+ product = db.records.create(" Product" , {" sku" : " ABC123" })
298
+ print (str (product)) # Product (product-id-here)
299
+ ```
300
+
86
301
## Complete Documentation
87
302
88
303
For comprehensive documentation, tutorials, and examples, please visit:
@@ -206,18 +421,18 @@ def find(
206
421
search_query : Optional[SearchQuery] = None ,
207
422
record_id : Optional[str ] = None ,
208
423
transaction : Optional[Transaction] = None
209
- ) -> List[Record]
424
+ ) -> RecordSearchResult
210
425
```
211
426
212
427
** Arguments:**
213
428
214
- - `query ` (Optional[SearchQuery]): Search query parameters
429
+ - `search_query ` (Optional[SearchQuery]): Search query parameters
215
430
- `record_id` (Optional[str ]): Optional record ID to search from
216
431
- `transaction` (Optional[Transaction]): Optional transaction object
217
432
218
433
** Returns:**
219
434
220
- - `List[Record] ` : List of matching records
435
+ - `RecordSearchResult ` : SearchResult container with matching records and metadata
221
436
222
437
** Example:**
223
438
@@ -235,7 +450,24 @@ query = {
235
450
" limit" : 10
236
451
}
237
452
238
- records = db.records.find(query = query)
453
+ result = db.records.find(query = query)
454
+
455
+ # Work with SearchResult
456
+ print (f " Found { len (result)} out of { result.total} total records " )
457
+
458
+ # Iterate over results
459
+ for record in result:
460
+ print (f " Employee: { record.get(' name' )} - { record.get(' department' )} " )
461
+
462
+ # Check pagination
463
+ if result.has_more:
464
+ print (" More results available" )
465
+
466
+ # Access specific records
467
+ first_employee = result[0 ] if result else None
468
+
469
+ # List operations
470
+ senior_employees = [r for r in result if r.get(' age' , 0 ) > 30 ]
239
471
```
240
472
241
473
### delete()
0 commit comments