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,
-- * predicates
queryIsNull,
queryIsAcct,
queryIsAmt,
queryIsDepth,
queryIsDate,
queryIsDate2,
queryIsDateOrDate2,
queryIsStartDateOnly,
queryIsSym,
queryIsReal,
queryIsStatus,
queryIsType,
queryIsCode,
queryIsDesc,
queryIsTag,
queryIsAccountRelated,
queryIsTransactionOrPostingRelated,
queryIsAcct,
queryIsType,
queryIsDepth,
queryIsReal,
queryIsAmt,
queryIsSym,
queryIsStartDateOnly,
queryIsTransactionRelated,
-- * accessors
queryStartDate,
queryEndDate,
@ -478,13 +479,9 @@ queryIsNull (And []) = True
queryIsNull (Not (Or [])) = True
queryIsNull _ = False
-- | Is this a simple query of this type ("depth:D") ?
-- Note, does not match a compound query like "not:depth:D" or "depth:D acct:A".
-- | Is this a simple query of this type (date:) ?
-- Does not match a compound query involving and/or/not.
-- Likewise for the following functions.
queryIsDepth :: Query -> Bool
queryIsDepth (Depth _) = True
queryIsDepth _ = False
queryIsDate :: Query -> Bool
queryIsDate (Date _) = True
queryIsDate _ = False
@ -498,14 +495,38 @@ queryIsDateOrDate2 (Date _) = True
queryIsDateOrDate2 (Date2 _) = True
queryIsDateOrDate2 _ = False
queryIsStatus :: Query -> Bool
queryIsStatus (StatusQ _) = True
queryIsStatus _ = False
queryIsCode :: Query -> Bool
queryIsCode (Code _) = True
queryIsCode _ = False
queryIsDesc :: Query -> Bool
queryIsDesc (Desc _) = True
queryIsDesc _ = False
queryIsTag :: Query -> Bool
queryIsTag (Tag _ _) = True
queryIsTag _ = False
queryIsAcct :: Query -> Bool
queryIsAcct (Acct _) = True
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 (Amt _ _) = True
queryIsAmt _ = False
@ -514,22 +535,6 @@ queryIsSym :: Query -> Bool
queryIsSym (Sym _) = True
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
-- filter postings prior to the date) ?
-- 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 _ _ = False
-- | Does this query involve a property which only accounts (without their balances) have,
-- making it inappropriate for matching other things ?
queryIsAccountRelated :: Query -> Bool
queryIsAccountRelated = matchesQuery (
queryIsAcct
||| 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
-- | Does this query involve a property of transactions (or their postings),
-- making it inapplicable to account declarations ?
queryIsTransactionRelated :: Query -> Bool
queryIsTransactionRelated = matchesQuery (
queryIsDate
||| queryIsDate2
-- ||| queryIsNote
-- ||| queryIsPayee
||| queryIsReal
||| queryIsStatus
||| queryIsCode
||| queryIsDesc
||| queryIsReal
||| queryIsAmt
||| queryIsSym
)
(|||) :: (a->Bool) -> (a->Bool) -> (a->Bool)

View File

@ -29,21 +29,28 @@ tags :: CliOpts -> Journal -> IO ()
tags CliOpts{rawopts_=rawopts,reportspec_=rspec} j = do
let today = _rsDay rspec
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
let
querystring = map T.pack $ drop 1 args
querystr = map T.pack $ drop 1 args
values = boolopt "values" rawopts
parsed = boolopt "parsed" rawopts
empty = empty_ $ _rsReportOpts rspec
argsquery <- either usageError (return . fst) $ parseQueryList today querystring
query <- either usageError (return . fst) $ parseQueryList today querystr
let
q = simplifyQuery $ And [queryFromFlags $ _rsReportOpts rspec, argsquery]
txns = filter (q `matchesTransaction`) $ jtxns $ journalApplyValuationFromOpts rspec j
q = simplifyQuery $ And [queryFromFlags $ _rsReportOpts rspec, query]
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 =
(if parsed then id else nubSort)
[ r
| (t,v) <- concatMap transactionAllTags txns
| (t,v) <- concatMap (journalAccountTags j) matchedaccts ++ concatMap transactionAllTags matchedtxns
, maybe True (`regexMatchText` t) mtagpat
, let r = if values then v else t
, not (values && T.null v && not empty)

View File

@ -1,14 +1,23 @@
tags\
List the unique tag names used in the journal. With a TAGREGEX argument,
only tag names matching the regular expression (case insensitive) are shown.
With QUERY arguments, only transactions matching the query are considered.
List the unique tag names used in the journal, whether on transactions, postings,
or account declarations.
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
are parsed from the input data, including duplicates.
With QUERY arguments, only transactions and accounts matching this query are considered.
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
they are omitted.
With the --values flag, the tags' unique non-empty values are listed instead.
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
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
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
(b) 1 ; t3:v3
2000/1/1 ; t3:v3, a transaction tag
(a:aa) 1 ; t4:v4, a posting tag
2000/1/2 ; t4:v4
(b) 1 ; t5:v5
2000/1/2 ; t5:v4, a reused value
(c) 1 ; t6:v6, an undeclared account
# 1. list all tags
$ hledger -f - tags
# 1. show all tags
$ hledger -f- tags
t1
t2
t3
t4
t5
t6
# 2. list tag names matching a regex
$ hledger -f - tags '[24]'
t2
t4
# 3. list tag values
$ hledger -f - tags --values
# 2. show all tag values
$ hledger -f- tags --values
v1
v2
v3
v4
v5
v6
# 4. list values of tags matching a regex from transactions matching a query
$ hledger -f - tags t3 date:2000/1/1 --values
v3
# 3. show tags matching a regex
$ hledger -f- tags '[1-4]'
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