diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,9 @@
 # rhbzquery releases
 
+## 0.4 (2020-12-11)
+- add --list-operators
+- rename longer operators to be more memorable
+
 ## 0.3 (2020-12-10)
 - add --list-fields option
 - field validation: error for unknown fields
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -27,7 +27,9 @@
 ### Help
 `rhbzquery --help` describes arguments
 
-`rhbzquery --list-fields` : lists the many fields (not all well supported yet, eg timestamps).
+`rhbzquery --list-fields` : lists the many fields (not all well supported yet, eg timestamps)
+
+`rhbzquery --list-operators` : lists the search operator types
 
 ## Installation
 Run `stack install` or `cabal install`.
diff --git a/TODO b/TODO
--- a/TODO
+++ b/TODO
@@ -1,4 +1,3 @@
-- --list-operators?
 - --url for server
 - tui bug browser
 - --json output
diff --git a/rhbzquery.cabal b/rhbzquery.cabal
--- a/rhbzquery.cabal
+++ b/rhbzquery.cabal
@@ -1,5 +1,5 @@
 name:                rhbzquery
-version:             0.3
+version:             0.4
 synopsis:            Bugzilla query tool
 description:
         A CLI tool for creating bugzilla queries for bugzilla.redhat.com.
diff --git a/src/Fields.hs b/src/Fields.hs
--- a/src/Fields.hs
+++ b/src/Fields.hs
@@ -6,6 +6,7 @@
   )
 where
 
+import Data.List.Extra as L
 import Data.Version
 import Numeric.Natural
 
@@ -90,15 +91,16 @@
           "itm" -> "cf_internal_target_milestone"
           "itr" -> "cf_internal_target_release"
           "status" -> "bug_status"
-          "verified" -> "cf_verified"
           "sst" -> "agile_team.name"
           "summary" -> "short_desc"
           "flag" -> "flagtypes.name"
           "flags" -> "flagtypes.name"
-          p -> p
+          p -> L.replace "-" "_" p
   in if longname `elem` allBzFields
      then longname
-     else error' $ "unknown field: " ++ f
+     else if "cf_" ++ longname `elem` allBzFields
+          then "cf_" ++ longname
+          else error' $ "unknown field: " ++ f
 
 -- https://bugzilla.redhat.com/query.cgi?query_format=advanced
 -- https://bugzilla.redhat.com/rest/field/bug
diff --git a/src/Help.hs b/src/Help.hs
--- a/src/Help.hs
+++ b/src/Help.hs
@@ -8,7 +8,7 @@
 #endif
 import qualified Options.Applicative.Help.Pretty as P
 
-import ParseArg (operators, showOpHelp, statusList)
+import ParseArg (statusList)
 
 detailedHelp :: P.Doc
 detailedHelp =
@@ -16,11 +16,9 @@
   [ P.empty
   , P.text "Tool for generating bugzilla queries"
   , P.empty
-  , P.text "STATUS = " <> P.lbrace <> P.align (P.fillCat (P.punctuate P.comma (map P.text (statusList ++ ["ALL"]))) <> P.rbrace)
-  , P.empty
+  , P.text "STATUS = " <> P.lbrace <> P.align (P.fillCat (P.punctuate P.comma (map P.text (statusList ++ ["ALL (open and closed)"]))) <> P.rbrace)
   , P.text "PRODUCTVERSION = " <> P.lbrace <> P.align (P.fillCat (P.punctuate P.comma (map P.text ["rawhide", "fedora", "fXY", "epel", "epelX", "rhel8", "rhel7", "rhelX.Z"])) <> P.rbrace)
-  , P.empty
-  , P.text "'op' is " <> P.align (P.fillCat (P.punctuate P.comma (map (P.text . showOpHelp) operators)) P.<//> P.text "[content~ & content!~ map to matches/notmatches]")
+  , P.text "op = search operator (eg '~' for substring: \"summary~akeyword\")"
   , P.empty
   , P.text "See https://github.com/juhp/rhbzquery#readme for examples"
   ]
diff --git a/src/Main.hs b/src/Main.hs
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -1,6 +1,8 @@
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE OverloadedStrings #-}
 
+module Main (main) where
+
 #if !MIN_VERSION_simple_cmd_args(0,1,3)
 import Control.Applicative ((<|>))
 #endif
@@ -30,7 +32,7 @@
 import Paths_rhbzquery
 import User
 
-data QueryMode = BugList | ListFields | CreateBug | APIQuery
+data QueryMode = BugList | ListFields | ListOperators | CreateBug | APIQuery
 
 main :: IO ()
 main =
@@ -38,15 +40,20 @@
   run <$>
   switchWith 'n' "dryrun" "Do not open url" <*>
   switchWith 'm' "mine" "My bugs" <*>
-  (flagWith' ListFields 'l' "list-fields" "List the query FIELDs" <|>
+  (flagWith' ListFields 'l' "list-fields" "List query FIELDs" <|>
+   flagWith' ListOperators 'o' "list-operators" "List op search operator types" <|>
    flagWith' CreateBug 'f' "file" "File a bug" <|>
    flagWith BugList APIQuery 'w' "api" "Web API query") <*>
   many (strArg argHelp)
   where
     run :: Bool -> Bool -> QueryMode -> [String] -> IO ()
     run _ _ ListFields _ = mapM_ putStrLn allBzFields
+    run _ _ ListOperators _ = do
+      mapM_ putStrLn $ map showOpHelp operators ++
+        ["", "content~ uses matches", "content!~ uses notmatches"]
+    -- FIXME should really use some and many
+    run _ _ _ [] = error' "please give an argument or --help"
     run dryrun mine mode args = do
-      when (null args) $ error' "Please specify at least one argument"
       user <- if mine
         then do
         mail <- getBzUser
@@ -74,6 +81,7 @@
         BugList -> "buglist.cgi"
         CreateBug -> "enter_bug.cgi"
         ListFields -> "query.cgi" -- unused
+        ListOperators -> "query.cgi" -- unused
       <> renderQuery True (bzQuery query)
 
     hasStatusSet :: [ArgType] -> Bool
diff --git a/src/ParseArg.hs b/src/ParseArg.hs
--- a/src/ParseArg.hs
+++ b/src/ParseArg.hs
@@ -23,12 +23,14 @@
 argHelp = "[COMPONENT|STATUS|PRODUCTVERSION|FIELD=VALUE|FIELDopVALUE]..."
 
 data Operator = Equals | NotEqual
-              | Regexp |NotRegexp
               | Substring | NotSubstring
+              | Regexp |NotRegexp
               | CaseSubstring
-              | AllWordsSubstr | NoWordsSubstr
-              | AllWords | NoWords
+              | AnyWordsSubstr | AllWordsSubstr | NoWordsSubstr
+              | AnyExact
+              | AnyWords | AllWords | NoWords
               | IsEmpty | IsNotEmpty
+              | NoOp
               -- | ContentMatches | ContentNotMatch
   deriving (Eq,Show,Enum,Bounded)
 
@@ -38,16 +40,25 @@
 operators :: [Operator]
 operators = enumFromTo minBound maxBound
 
-data OperatorData = OpUnary String | OpNull String
+data OperatorData = OpUnary String String | OpNull String String
   deriving Eq
 
 opSyntax :: OperatorData -> String
-opSyntax (OpUnary s) = s
-opSyntax (OpNull s) = s
+opSyntax (OpUnary s _) = s
+opSyntax (OpNull s _) = s
 
+opDescribe :: OperatorData -> String
+opDescribe (OpUnary _ h) = h
+opDescribe (OpNull _ h) = h
+
 showOpHelp :: Operator -> String
 showOpHelp op =
-  "'" ++ opSyntax (opData op) ++ "'(" ++ showOp op ++ ")"
+  let opd = opData op
+  in padSyntax opd ++ showOp op ++ " (" ++ opDescribe opd ++ ")"
+  where
+    padSyntax opd =
+      let ops = opSyntax opd
+      in replicate (10 - length ops) ' ' ++ "'" ++ ops ++ "' : "
 
 instance Ord OperatorData where
   compare op1 op2 =
@@ -62,19 +73,23 @@
 
 -- FIXME check all unique
 opData :: Operator -> OperatorData
-opData Equals = OpUnary "="
-opData NotEqual = OpUnary "!="
-opData Substring = OpUnary "~"
-opData NotSubstring = OpUnary "!~"
-opData CaseSubstring = OpUnary "~c~"
-opData Regexp = OpUnary "=~"
-opData NotRegexp = OpUnary "!=~"
-opData AllWordsSubstr = OpUnary "~a~"
-opData NoWordsSubstr = OpUnary "!a~"
-opData AllWords = OpUnary "~w~"
-opData NoWords = OpUnary "!w~"
-opData IsEmpty = OpNull "~e~"
-opData IsNotEmpty = OpNull "!e~"
+opData NoOp = OpNull "~noop~" "ignore (comment out query field)"
+opData Equals = OpUnary "=" "is equal to"
+opData NotEqual = OpUnary "!=" "is not equal to"
+opData AnyExact = OpUnary "~anyexact=" "is equal to any of the strings"
+opData Substring = OpUnary "~" "contains the string"
+opData NotSubstring = OpUnary "!~" "does not contain the string"
+opData CaseSubstring = OpUnary "~case~" "contains the string (exact case)"
+opData AnyWordsSubstr = OpUnary "~any~" "contains any of the strings"
+opData AllWordsSubstr = OpUnary "~all~" "contains all of the strings"
+opData NoWordsSubstr = OpUnary "~no~" "contains none of the strings"
+opData Regexp = OpUnary "=~" "matches regular expression"
+opData NotRegexp = OpUnary "!=~" "does not match regular expression"
+opData AnyWords = OpUnary "~anywords~" "contains any of the words"
+opData AllWords = OpUnary "~allwords~" "contains all of the words"
+opData NoWords = OpUnary "~nowords~" "contains none of the words"
+opData IsEmpty = OpNull "~empty~" "is empty"
+opData IsNotEmpty = OpNull "~notempty~" "is not empty"
 -- -- only for 'content':
 -- opData Matches = OpUnary "~m~"
 -- opData NotMatches = OpUnary "!m~"
@@ -124,13 +139,13 @@
     parseParam :: Operator -> Maybe ArgType
     parseParam oper =
       case opData oper of
-        OpUnary op ->
+        OpUnary op _ ->
           if op `isInfixOf` ps then
             case splitOn op ps of
               (f:val) -> Just (ArgParameter f oper (intercalate op val))
               _ -> Nothing
           else Nothing
-        OpNull op ->
+        OpNull op _ ->
           case stripSuffix op ps of
             Just a ->
               if null a
