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:
Simon Michael 2022-04-14 10:46:57 -10:00
parent 4f26309328
commit 5af224d534
4 changed files with 121 additions and 87 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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.

View File

@ -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]' $ hledger -f- tags --values
t2 v1
t4
# 3. list tag values
$ hledger -f - tags --values
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