This is not a site to provide explanations of regular expressions, but I can explain some terms that might help you get started elsewhere
| is used for alternation. So (?:-|%{NUMBER:bytes})
matches either - or a number, which is what is used for fields like byte counts in HTTP logs.
() are used to create a capture group. It captures part of the regular expression so that it can be referenced when doing a substitution. See this post for an example.
Sometimes you need to use () to surround part of a pattern (as in that alternation above), but you do not want to capture it to reference later. In that case you would use (?: and ) around the pattern to create a non-capture group.
Standard web logs will record the user request surrounded by double quotes., like this: "GET /foo/ HTTP/1.0"
The first part of the pattern you mentioned is trying to match that. Within the double quotes it first tries to match the verb and then the URI (which cannot contain a space). The HTTP/1.0 after that is actually optional, so the HTTP/%{NUMBER:httpversion}
is made into a non-capture group using (?: and ) and then followed by ? which means it occurs 0 or 1 times (i.e. it is optional).
If matching the verb, URI and optional version fails then it says (using alternation) to just capture everything within the double quotes in the field [rawrequest].
Lastly, there are many slightly different flavours of regexps -- Ruby, Java, perl, POSIX, shell, csh, ed, and many more. logstash uses Ruby regexps for grok and mutate+gsub filters.
There are places in logstash where Java regexps are used (some file paths) but the differences between Java and Ruby are unlikely to matter in those places.