fix: tags: also match accounts declared but not used (#1857)
By default, all account declarations and all transactions are searched; but when there's a query involving transaction fields, account declarations unrelated to the matched transactions are not searched. added: queryIsCode queryIsTransactionRelated
This commit is contained in:
		
							parent
							
								
									4f26309328
								
							
						
					
					
						commit
						5af224d534
					
				@ -29,20 +29,21 @@ module Hledger.Query (
 | 
				
			|||||||
  matchesQuery,
 | 
					  matchesQuery,
 | 
				
			||||||
  -- * predicates
 | 
					  -- * predicates
 | 
				
			||||||
  queryIsNull,
 | 
					  queryIsNull,
 | 
				
			||||||
  queryIsAcct,
 | 
					 | 
				
			||||||
  queryIsAmt,
 | 
					 | 
				
			||||||
  queryIsDepth,
 | 
					 | 
				
			||||||
  queryIsDate,
 | 
					  queryIsDate,
 | 
				
			||||||
  queryIsDate2,
 | 
					  queryIsDate2,
 | 
				
			||||||
  queryIsDateOrDate2,
 | 
					  queryIsDateOrDate2,
 | 
				
			||||||
  queryIsStartDateOnly,
 | 
					 | 
				
			||||||
  queryIsSym,
 | 
					 | 
				
			||||||
  queryIsReal,
 | 
					 | 
				
			||||||
  queryIsStatus,
 | 
					  queryIsStatus,
 | 
				
			||||||
  queryIsType,
 | 
					  queryIsCode,
 | 
				
			||||||
 | 
					  queryIsDesc,
 | 
				
			||||||
  queryIsTag,
 | 
					  queryIsTag,
 | 
				
			||||||
  queryIsAccountRelated,
 | 
					  queryIsAcct,
 | 
				
			||||||
  queryIsTransactionOrPostingRelated,
 | 
					  queryIsType,
 | 
				
			||||||
 | 
					  queryIsDepth,
 | 
				
			||||||
 | 
					  queryIsReal,
 | 
				
			||||||
 | 
					  queryIsAmt,
 | 
				
			||||||
 | 
					  queryIsSym,
 | 
				
			||||||
 | 
					  queryIsStartDateOnly,
 | 
				
			||||||
 | 
					  queryIsTransactionRelated,
 | 
				
			||||||
  -- * accessors
 | 
					  -- * accessors
 | 
				
			||||||
  queryStartDate,
 | 
					  queryStartDate,
 | 
				
			||||||
  queryEndDate,
 | 
					  queryEndDate,
 | 
				
			||||||
@ -478,13 +479,9 @@ queryIsNull (And []) = True
 | 
				
			|||||||
queryIsNull (Not (Or [])) = True
 | 
					queryIsNull (Not (Or [])) = True
 | 
				
			||||||
queryIsNull _ = False
 | 
					queryIsNull _ = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- | Is this a simple query of this type ("depth:D") ? 
 | 
					-- | Is this a simple query of this type (date:) ? 
 | 
				
			||||||
-- Note, does not match a compound query like "not:depth:D" or "depth:D acct:A".
 | 
					-- Does not match a compound query involving and/or/not.
 | 
				
			||||||
-- Likewise for the following functions.
 | 
					-- Likewise for the following functions.
 | 
				
			||||||
queryIsDepth :: Query -> Bool
 | 
					 | 
				
			||||||
queryIsDepth (Depth _) = True
 | 
					 | 
				
			||||||
queryIsDepth _ = False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
queryIsDate :: Query -> Bool
 | 
					queryIsDate :: Query -> Bool
 | 
				
			||||||
queryIsDate (Date _) = True
 | 
					queryIsDate (Date _) = True
 | 
				
			||||||
queryIsDate _ = False
 | 
					queryIsDate _ = False
 | 
				
			||||||
@ -498,14 +495,38 @@ queryIsDateOrDate2 (Date _) = True
 | 
				
			|||||||
queryIsDateOrDate2 (Date2 _) = True
 | 
					queryIsDateOrDate2 (Date2 _) = True
 | 
				
			||||||
queryIsDateOrDate2 _ = False
 | 
					queryIsDateOrDate2 _ = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					queryIsStatus :: Query -> Bool
 | 
				
			||||||
 | 
					queryIsStatus (StatusQ _) = True
 | 
				
			||||||
 | 
					queryIsStatus _ = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					queryIsCode :: Query -> Bool
 | 
				
			||||||
 | 
					queryIsCode (Code _) = True
 | 
				
			||||||
 | 
					queryIsCode _ = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
queryIsDesc :: Query -> Bool
 | 
					queryIsDesc :: Query -> Bool
 | 
				
			||||||
queryIsDesc (Desc _) = True
 | 
					queryIsDesc (Desc _) = True
 | 
				
			||||||
queryIsDesc _ = False
 | 
					queryIsDesc _ = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					queryIsTag :: Query -> Bool
 | 
				
			||||||
 | 
					queryIsTag (Tag _ _) = True
 | 
				
			||||||
 | 
					queryIsTag _ = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
queryIsAcct :: Query -> Bool
 | 
					queryIsAcct :: Query -> Bool
 | 
				
			||||||
queryIsAcct (Acct _) = True
 | 
					queryIsAcct (Acct _) = True
 | 
				
			||||||
queryIsAcct _ = False
 | 
					queryIsAcct _ = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					queryIsType :: Query -> Bool
 | 
				
			||||||
 | 
					queryIsType (Type _) = True
 | 
				
			||||||
 | 
					queryIsType _ = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					queryIsDepth :: Query -> Bool
 | 
				
			||||||
 | 
					queryIsDepth (Depth _) = True
 | 
				
			||||||
 | 
					queryIsDepth _ = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					queryIsReal :: Query -> Bool
 | 
				
			||||||
 | 
					queryIsReal (Real _) = True
 | 
				
			||||||
 | 
					queryIsReal _ = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
queryIsAmt :: Query -> Bool
 | 
					queryIsAmt :: Query -> Bool
 | 
				
			||||||
queryIsAmt (Amt _ _) = True
 | 
					queryIsAmt (Amt _ _) = True
 | 
				
			||||||
queryIsAmt _         = False
 | 
					queryIsAmt _         = False
 | 
				
			||||||
@ -514,22 +535,6 @@ queryIsSym :: Query -> Bool
 | 
				
			|||||||
queryIsSym (Sym _) = True
 | 
					queryIsSym (Sym _) = True
 | 
				
			||||||
queryIsSym _ = False
 | 
					queryIsSym _ = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
queryIsReal :: Query -> Bool
 | 
					 | 
				
			||||||
queryIsReal (Real _) = True
 | 
					 | 
				
			||||||
queryIsReal _ = False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
queryIsStatus :: Query -> Bool
 | 
					 | 
				
			||||||
queryIsStatus (StatusQ _) = True
 | 
					 | 
				
			||||||
queryIsStatus _ = False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
queryIsType :: Query -> Bool
 | 
					 | 
				
			||||||
queryIsType (Type _) = True
 | 
					 | 
				
			||||||
queryIsType _ = False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
queryIsTag :: Query -> Bool
 | 
					 | 
				
			||||||
queryIsTag (Tag _ _) = True
 | 
					 | 
				
			||||||
queryIsTag _ = False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-- | Does this query specify a start date and nothing else (that would
 | 
					-- | Does this query specify a start date and nothing else (that would
 | 
				
			||||||
-- filter postings prior to the date) ?
 | 
					-- filter postings prior to the date) ?
 | 
				
			||||||
-- When the flag is true, look for a starting secondary date instead.
 | 
					-- When the flag is true, look for a starting secondary date instead.
 | 
				
			||||||
@ -542,29 +547,18 @@ queryIsStartDateOnly False (Date (DateSpan (Just _) _)) = True
 | 
				
			|||||||
queryIsStartDateOnly True (Date2 (DateSpan (Just _) _)) = True
 | 
					queryIsStartDateOnly True (Date2 (DateSpan (Just _) _)) = True
 | 
				
			||||||
queryIsStartDateOnly _ _ = False
 | 
					queryIsStartDateOnly _ _ = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- | Does this query involve a property which only accounts (without their balances) have,
 | 
					-- | Does this query involve a property of transactions (or their postings),
 | 
				
			||||||
-- making it inappropriate for matching other things ?
 | 
					-- making it inapplicable to account declarations ?
 | 
				
			||||||
queryIsAccountRelated :: Query -> Bool
 | 
					queryIsTransactionRelated :: Query -> Bool
 | 
				
			||||||
queryIsAccountRelated = matchesQuery (
 | 
					queryIsTransactionRelated = matchesQuery (
 | 
				
			||||||
      queryIsAcct
 | 
					      queryIsDate
 | 
				
			||||||
  ||| queryIsDepth
 | 
					 | 
				
			||||||
  ||| queryIsType
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-- | Does this query involve a property which only transactions or postings have,
 | 
					 | 
				
			||||||
-- making it inappropriate for matching other things ?
 | 
					 | 
				
			||||||
queryIsTransactionOrPostingRelated :: Query -> Bool
 | 
					 | 
				
			||||||
queryIsTransactionOrPostingRelated = matchesQuery (
 | 
					 | 
				
			||||||
      queryIsAmt
 | 
					 | 
				
			||||||
  -- ||| queryIsCode
 | 
					 | 
				
			||||||
  -- ||| queryIsCur
 | 
					 | 
				
			||||||
  ||| queryIsDesc
 | 
					 | 
				
			||||||
  ||| queryIsDate
 | 
					 | 
				
			||||||
  ||| queryIsDate2
 | 
					  ||| queryIsDate2
 | 
				
			||||||
  -- ||| queryIsNote
 | 
					 | 
				
			||||||
  -- ||| queryIsPayee
 | 
					 | 
				
			||||||
  ||| queryIsReal
 | 
					 | 
				
			||||||
  ||| queryIsStatus
 | 
					  ||| queryIsStatus
 | 
				
			||||||
 | 
					  ||| queryIsCode
 | 
				
			||||||
 | 
					  ||| queryIsDesc
 | 
				
			||||||
 | 
					  ||| queryIsReal
 | 
				
			||||||
 | 
					  ||| queryIsAmt
 | 
				
			||||||
 | 
					  ||| queryIsSym
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(|||) :: (a->Bool) -> (a->Bool) -> (a->Bool)
 | 
					(|||) :: (a->Bool) -> (a->Bool) -> (a->Bool)
 | 
				
			||||||
 | 
				
			|||||||
@ -29,21 +29,28 @@ tags :: CliOpts -> Journal -> IO ()
 | 
				
			|||||||
tags CliOpts{rawopts_=rawopts,reportspec_=rspec} j = do
 | 
					tags CliOpts{rawopts_=rawopts,reportspec_=rspec} j = do
 | 
				
			||||||
  let today = _rsDay rspec
 | 
					  let today = _rsDay rspec
 | 
				
			||||||
      args = listofstringopt "args" rawopts
 | 
					      args = listofstringopt "args" rawopts
 | 
				
			||||||
 | 
					  -- first argument is a tag name pattern, others are a hledger query: hledger tags [TAGREGEX [QUERYARGS..]]
 | 
				
			||||||
  mtagpat <- mapM (either Fail.fail pure . toRegexCI . T.pack) $ headMay args
 | 
					  mtagpat <- mapM (either Fail.fail pure . toRegexCI . T.pack) $ headMay args
 | 
				
			||||||
  let
 | 
					  let
 | 
				
			||||||
    querystring = map T.pack $ drop 1 args
 | 
					    querystr = map T.pack $ drop 1 args
 | 
				
			||||||
    values   = boolopt "values" rawopts
 | 
					    values   = boolopt "values" rawopts
 | 
				
			||||||
    parsed   = boolopt "parsed" rawopts
 | 
					    parsed   = boolopt "parsed" rawopts
 | 
				
			||||||
    empty    = empty_ $ _rsReportOpts rspec
 | 
					    empty    = empty_ $ _rsReportOpts rspec
 | 
				
			||||||
 | 
					  query <- either usageError (return . fst) $ parseQueryList today querystr
 | 
				
			||||||
  argsquery <- either usageError (return . fst) $ parseQueryList today querystring
 | 
					 | 
				
			||||||
  let
 | 
					  let
 | 
				
			||||||
    q = simplifyQuery $ And [queryFromFlags $ _rsReportOpts rspec, argsquery]
 | 
					    q = simplifyQuery $ And [queryFromFlags $ _rsReportOpts rspec, query]
 | 
				
			||||||
    txns = filter (q `matchesTransaction`) $ jtxns $ journalApplyValuationFromOpts rspec j
 | 
					    matchedtxns = filter (q `matchesTransaction`) $ jtxns $ journalApplyValuationFromOpts rspec j
 | 
				
			||||||
 | 
					    -- also list tags from matched account declarations, but not if there is
 | 
				
			||||||
 | 
					    -- a query for something transaction-related, like date: or amt:.
 | 
				
			||||||
 | 
					    matchedaccts = dbg4 "accts" $
 | 
				
			||||||
 | 
					      if dbg4 "queryIsTransactionRelated" $ queryIsTransactionRelated $ dbg4 "q" q
 | 
				
			||||||
 | 
					      then []
 | 
				
			||||||
 | 
					      else filter (matchesAccountExtra (journalAccountType j) (journalInheritedAccountTags j) q) $
 | 
				
			||||||
 | 
					           map fst $ jdeclaredaccounts j
 | 
				
			||||||
    tagsorvalues =
 | 
					    tagsorvalues =
 | 
				
			||||||
      (if parsed then id else nubSort)
 | 
					      (if parsed then id else nubSort)
 | 
				
			||||||
      [ r
 | 
					      [ r
 | 
				
			||||||
      | (t,v) <- concatMap transactionAllTags txns
 | 
					      | (t,v) <- concatMap (journalAccountTags j) matchedaccts ++ concatMap transactionAllTags matchedtxns
 | 
				
			||||||
      , maybe True (`regexMatchText` t) mtagpat
 | 
					      , maybe True (`regexMatchText` t) mtagpat
 | 
				
			||||||
      , let r = if values then v else t
 | 
					      , let r = if values then v else t
 | 
				
			||||||
      , not (values && T.null v && not empty)
 | 
					      , not (values && T.null v && not empty)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,14 +1,23 @@
 | 
				
			|||||||
tags\
 | 
					tags\
 | 
				
			||||||
List the unique tag names used in the journal. With a TAGREGEX argument,
 | 
					List the unique tag names used in the journal, whether on transactions, postings,
 | 
				
			||||||
only tag names matching the regular expression (case insensitive) are shown. 
 | 
					or account declarations.
 | 
				
			||||||
With QUERY arguments, only transactions matching the query are considered.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
With the --values flag, the tags' unique values are listed instead.
 | 
					With a TAGREGEX argument, only tag names matching this regular expression
 | 
				
			||||||
 | 
					(case insensitive, infix matched) are shown.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
With --parsed flag, all tags or values are shown in the order they
 | 
					With QUERY arguments, only transactions and accounts matching this query are considered.
 | 
				
			||||||
are parsed from the input data, including duplicates.
 | 
					If the query involves transaction fields (date:, desc:, amt:, ...),
 | 
				
			||||||
 | 
					the search is restricted to the matched transactions and their accounts.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
With -E/--empty, any blank/empty values will also be shown, otherwise
 | 
					With the --values flag, the tags' unique non-empty values are listed instead.
 | 
				
			||||||
they are omitted.
 | 
					With -E/--empty, blank/empty values are also shown.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					With --parsed, tags or values are shown in the order they were parsed, with duplicates included.
 | 
				
			||||||
 | 
					(Except, tags from account declarations are always shown first.)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_FLAGS
 | 
					_FLAGS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Tip: remember,
 | 
				
			||||||
 | 
					accounts also acquire tags from their parents,
 | 
				
			||||||
 | 
					postings also acquire tags from their account and transaction,
 | 
				
			||||||
 | 
					transactions also acquire tags from their postings.
 | 
				
			||||||
 | 
				
			|||||||
@ -1,32 +1,56 @@
 | 
				
			|||||||
# tags command
 | 
					# tags command
 | 
				
			||||||
 | 
					
 | 
				
			||||||
account a   ; t1:v1
 | 
					account a     ; t1:v1, an account tag
 | 
				
			||||||
 | 
					account b:bb  ; t2:v2, an unused account, depth 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
2000/1/1    ; t2:v2
 | 
					2000/1/1      ; t3:v3, a transaction tag
 | 
				
			||||||
  (b)    1  ; t3:v3
 | 
					  (a:aa)   1  ; t4:v4, a posting tag
 | 
				
			||||||
 | 
					
 | 
				
			||||||
2000/1/2    ; t4:v4
 | 
					2000/1/2      ; t5:v4, a reused value
 | 
				
			||||||
  (b)    1  ; t5:v5
 | 
					  (c)      1  ; t6:v6, an undeclared account
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 1. list all tags
 | 
					# 1. show all tags
 | 
				
			||||||
$ hledger -f- tags
 | 
					$ hledger -f- tags
 | 
				
			||||||
 | 
					t1
 | 
				
			||||||
t2
 | 
					t2
 | 
				
			||||||
t3
 | 
					t3
 | 
				
			||||||
t4
 | 
					t4
 | 
				
			||||||
t5
 | 
					t5
 | 
				
			||||||
 | 
					t6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 2. list tag names matching a regex
 | 
					# 2. show all tag values
 | 
				
			||||||
$ hledger -f - tags '[24]'
 | 
					 | 
				
			||||||
t2
 | 
					 | 
				
			||||||
t4
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# 3. list tag values
 | 
					 | 
				
			||||||
$ hledger -f- tags --values
 | 
					$ hledger -f- tags --values
 | 
				
			||||||
 | 
					v1
 | 
				
			||||||
v2
 | 
					v2
 | 
				
			||||||
v3
 | 
					v3
 | 
				
			||||||
v4
 | 
					v4
 | 
				
			||||||
v5
 | 
					v6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 4. list values of tags matching a regex from transactions matching a query
 | 
					# 3. show tags matching a regex
 | 
				
			||||||
$ hledger -f - tags t3 date:2000/1/1 --values
 | 
					$ hledger -f- tags '[1-4]'
 | 
				
			||||||
v3
 | 
					t1
 | 
				
			||||||
 | 
					t2
 | 
				
			||||||
 | 
					t3
 | 
				
			||||||
 | 
					t4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 4. show tags matching (a regex and) a hledger query.
 | 
				
			||||||
 | 
					# If the query is applicable to both transactions and account declarations,
 | 
				
			||||||
 | 
					# both are searched for tags.
 | 
				
			||||||
 | 
					$ hledger -f- tags . b c
 | 
				
			||||||
 | 
					t2
 | 
				
			||||||
 | 
					t5
 | 
				
			||||||
 | 
					t6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 5. If the query involves transaction attributes,
 | 
				
			||||||
 | 
					# only accounts used by the matched transactions will contribute tags.
 | 
				
			||||||
 | 
					$ hledger -f- tags . date:2000/1/1
 | 
				
			||||||
 | 
					t1
 | 
				
			||||||
 | 
					t3
 | 
				
			||||||
 | 
					t4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 6. show account tags even when there are no transactions (#1857)
 | 
				
			||||||
 | 
					<
 | 
				
			||||||
 | 
					account a   ; t1:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$ hledger -f- tags
 | 
				
			||||||
 | 
					t1
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user