github.com/Jeffail/benthos/v3@v3.65.0/lib/processor/select_parts.go (about)

     1  package processor
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/Jeffail/benthos/v3/internal/docs"
     7  	"github.com/Jeffail/benthos/v3/lib/log"
     8  	"github.com/Jeffail/benthos/v3/lib/message"
     9  	"github.com/Jeffail/benthos/v3/lib/metrics"
    10  	"github.com/Jeffail/benthos/v3/lib/response"
    11  	"github.com/Jeffail/benthos/v3/lib/types"
    12  )
    13  
    14  //------------------------------------------------------------------------------
    15  
    16  func init() {
    17  	Constructors[TypeSelectParts] = TypeSpec{
    18  		constructor: NewSelectParts,
    19  		Categories: []Category{
    20  			CategoryUtility,
    21  		},
    22  		Summary: `
    23  Cherry pick a set of messages from a batch by their index. Indexes larger than
    24  the number of messages are simply ignored.`,
    25  		Description: `
    26  The selected parts are added to the new message batch in the same order as the
    27  selection array. E.g. with 'parts' set to [ 2, 0, 1 ] and the message parts
    28  [ '0', '1', '2', '3' ], the output will be [ '2', '0', '1' ].
    29  
    30  If none of the selected parts exist in the input batch (resulting in an empty
    31  output message) the batch is dropped entirely.
    32  
    33  Message indexes can be negative, and if so the part will be selected from the
    34  end counting backwards starting from -1. E.g. if index = -1 then the selected
    35  part will be the last part of the message, if index = -2 then the part before
    36  the last element with be selected, and so on.`,
    37  		UsesBatches: true,
    38  		FieldSpecs: docs.FieldSpecs{
    39  			PartsFieldSpec,
    40  		},
    41  	}
    42  }
    43  
    44  //------------------------------------------------------------------------------
    45  
    46  // SelectPartsConfig contains configuration fields for the SelectParts
    47  // processor.
    48  type SelectPartsConfig struct {
    49  	Parts []int `json:"parts" yaml:"parts"`
    50  }
    51  
    52  // NewSelectPartsConfig returns a SelectPartsConfig with default values.
    53  func NewSelectPartsConfig() SelectPartsConfig {
    54  	return SelectPartsConfig{
    55  		Parts: []int{0},
    56  	}
    57  }
    58  
    59  //------------------------------------------------------------------------------
    60  
    61  // SelectParts is a processor that selects parts from a message to append to a
    62  // new message.
    63  type SelectParts struct {
    64  	conf  Config
    65  	log   log.Modular
    66  	stats metrics.Type
    67  
    68  	mCount     metrics.StatCounter
    69  	mSkipped   metrics.StatCounter
    70  	mSelected  metrics.StatCounter
    71  	mDropped   metrics.StatCounter
    72  	mSent      metrics.StatCounter
    73  	mBatchSent metrics.StatCounter
    74  }
    75  
    76  // NewSelectParts returns a SelectParts processor.
    77  func NewSelectParts(
    78  	conf Config, mgr types.Manager, log log.Modular, stats metrics.Type,
    79  ) (Type, error) {
    80  	return &SelectParts{
    81  		conf:  conf,
    82  		log:   log,
    83  		stats: stats,
    84  
    85  		mCount:     stats.GetCounter("count"),
    86  		mSkipped:   stats.GetCounter("skipped"),
    87  		mSelected:  stats.GetCounter("selected"),
    88  		mDropped:   stats.GetCounter("dropped"),
    89  		mSent:      stats.GetCounter("sent"),
    90  		mBatchSent: stats.GetCounter("batch.sent"),
    91  	}, nil
    92  }
    93  
    94  //------------------------------------------------------------------------------
    95  
    96  // ProcessMessage applies the processor to a message, either creating >0
    97  // resulting messages or a response to be sent back to the message source.
    98  func (m *SelectParts) ProcessMessage(msg types.Message) ([]types.Message, types.Response) {
    99  	m.mCount.Incr(1)
   100  
   101  	newMsg := message.New(nil)
   102  
   103  	lParts := msg.Len()
   104  	for _, index := range m.conf.SelectParts.Parts {
   105  		if index < 0 {
   106  			// Negative indexes count backwards from the end.
   107  			index = lParts + index
   108  		}
   109  
   110  		// Check boundary of part index.
   111  		if index < 0 || index >= lParts {
   112  			m.mSkipped.Incr(1)
   113  		} else {
   114  			m.mSelected.Incr(1)
   115  			newMsg.Append(msg.Get(index).Copy())
   116  		}
   117  	}
   118  
   119  	if newMsg.Len() == 0 {
   120  		m.mDropped.Incr(1)
   121  		return nil, response.NewAck()
   122  	}
   123  
   124  	m.mBatchSent.Incr(1)
   125  	m.mSent.Incr(int64(newMsg.Len()))
   126  	msgs := [1]types.Message{newMsg}
   127  	return msgs[:], nil
   128  }
   129  
   130  // CloseAsync shuts down the processor and stops processing requests.
   131  func (m *SelectParts) CloseAsync() {
   132  }
   133  
   134  // WaitForClose blocks until the processor has closed down.
   135  func (m *SelectParts) WaitForClose(timeout time.Duration) error {
   136  	return nil
   137  }
   138  
   139  //------------------------------------------------------------------------------