@@ -17,6 +17,7 @@ import (
1717 "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
1818
1919 "github.com/tink3rlabs/magic/logger"
20+ "github.com/tink3rlabs/magic/storage/search/lucene"
2021)
2122
2223type DynamoDBAdapter struct {
@@ -187,6 +188,81 @@ func (s *DynamoDBAdapter) List(dest any, sortKey string, filter map[string]any,
187188
188189 return nextToken , nil
189190}
191+ func (s * DynamoDBAdapter ) Search (dest any , sortKey string , filter string , limit int , cursor string ) (string , error ) {
192+ slog .Debug (fmt .Sprintf (`Searching in DynamoDB with filter: %s` , filter ))
193+
194+ // Get model type from destination slice
195+ destType := reflect .TypeOf (dest ).Elem ().Elem ()
196+ model := reflect .New (destType ).Elem ().Interface ()
197+
198+ // Create Lucene parser from model type
199+ parser , err := lucene .NewParserFromType (model )
200+ if err != nil {
201+ slog .Error (`There was an error creating parser from model` )
202+ return "" , fmt .Errorf ("failed to create parser: %w" , err )
203+ }
204+
205+ // Parse Lucene query to DynamoDB PartiQL
206+ whereClause , params , err := parser .ParseToDynamoDBPartiQL (filter )
207+ if err != nil {
208+ slog .Error (`There was an error parsing filter to DynamoDB PartiQL` )
209+ return "" , fmt .Errorf ("invalid filter: %w" , err )
210+ }
211+ slog .Debug (fmt .Sprintf (`Where clause: %s` , whereClause ))
212+
213+ // Build the full query
214+ query := fmt .Sprintf (`SELECT * FROM "%v"` , s .getTableName (dest ))
215+ if whereClause != "" {
216+ query = query + fmt .Sprintf (` WHERE %s` , whereClause )
217+ }
218+
219+ // Add sorting if sortKey is provided
220+ if sortKey != "" {
221+ query = query + fmt .Sprintf (` ORDER BY %s` , sortKey )
222+ }
223+
224+ // Prepare the input for the ExecuteStatement operation
225+ input := dynamodb.ExecuteStatementInput {
226+ Statement : aws .String (query ),
227+ Parameters : params ,
228+ Limit : aws .Int32 (int32 (limit + 1 )), // Get one extra item for cursor
229+ }
230+
231+ // Set the cursor if provided
232+ if cursor != "" {
233+ input .NextToken = aws .String (cursor )
234+ }
235+
236+ // Execute the query
237+ response , err := s .DB .ExecuteStatement (context .TODO (), & input )
238+ if err != nil {
239+ slog .Error (fmt .Sprintf (`There was an error executing query: %s` , err ))
240+ return "" , err
241+ }
242+
243+ // Set next token from response
244+ nextToken := ""
245+ if response .NextToken != nil {
246+ nextToken = * response .NextToken
247+ }
248+
249+ // Handle the case when we have more items than the limit
250+ if len (response .Items ) > limit {
251+ // Remove the extra item
252+ response .Items = response .Items [:limit ]
253+ }
254+
255+ // Unmarshal the response into the destination
256+ err = attributevalue .UnmarshalListOfMapsWithOptions (response .Items , dest , func (eo * attributevalue.DecoderOptions ) {
257+ eo .TagKey = "json"
258+ })
259+ if err != nil {
260+ return "" , fmt .Errorf ("failed to unmarshal response: %w" , err )
261+ }
262+
263+ slog .Debug (fmt .Sprintf (`Next token: %s` , nextToken ))
264+ return nextToken , nil
265+ }
190266
191267func (s * DynamoDBAdapter ) getTableName (obj any ) string {
192268 // Get the type of obj
0 commit comments