| 
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -875,30 +875,55 @@ func substring(s string, length int) string {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// parseMemoOrderBy parses the order_by field and sets the appropriate ordering in memoFind.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// Follows AIP-132: supports comma-separated list of fields with optional "desc" suffix.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// Example: "pinned desc, display_time desc" or "create_time asc".
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (*APIV1Service) parseMemoOrderBy(orderBy string, memoFind *store.FindMemo) error {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					// Parse order_by field like "display_time desc" or "create_time asc"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					parts := strings.Fields(strings.TrimSpace(orderBy))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if len(parts) == 0 {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if strings.TrimSpace(orderBy) == "" {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return errors.New("empty order_by")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					field := parts[0]
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					direction := "desc" // default
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					// Split by comma to support multiple sort fields per AIP-132.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					fields := strings.Split(orderBy, ",")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					// Track if we've seen pinned field.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					hasPinned := false
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					for _, field := range fields {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						parts := strings.Fields(strings.TrimSpace(field))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if len(parts) == 0 {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							continue
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						fieldName := parts[0]
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						fieldDirection := "desc" // default per AIP-132 (we use desc as default for time fields)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if len(parts) > 1 {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						direction = strings.ToLower(parts[1])
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if direction != "asc" && direction != "desc" {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							fieldDirection = strings.ToLower(parts[1])
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							if fieldDirection != "asc" && fieldDirection != "desc" {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								return errors.Errorf("invalid order direction: %s, must be 'asc' or 'desc'", parts[1])
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					switch field {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						switch fieldName {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						case "pinned":
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							hasPinned = true
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							memoFind.OrderByPinned = true
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							// Note: pinned is always DESC (true first) regardless of direction specified.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						case "display_time", "create_time", "name":
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						memoFind.OrderByTimeAsc = direction == "asc"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							// Only set if this is the first time field we encounter.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							if !memoFind.OrderByUpdatedTs {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								memoFind.OrderByTimeAsc = fieldDirection == "asc"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						case "update_time":
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							memoFind.OrderByUpdatedTs = true
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						memoFind.OrderByTimeAsc = direction == "asc"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							memoFind.OrderByTimeAsc = fieldDirection == "asc"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						default:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return errors.Errorf("unsupported order field: %s, supported fields are: display_time, create_time, update_time, name", field)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							return errors.Errorf("unsupported order field: %s, supported fields are: pinned, display_time, create_time, update_time, name", fieldName)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					// If only pinned was specified, still need to set a default time ordering.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if hasPinned && !memoFind.OrderByUpdatedTs && len(fields) == 1 {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						memoFind.OrderByTimeAsc = false // default to desc
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					return nil
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
				
			
			 | 
			 | 
			
				
 
 |