Support --metadata field<number, --metadata field>number etc to match ranges of numeric values.

Similarly (well, for free), support preferred content expressions like
metadata=field<number and metadata=field>number
This commit is contained in:
Joey Hess 2016-02-27 10:55:02 -04:00
parent 01f1de0c3c
commit b946ca44c3
Failed to extract signature
8 changed files with 67 additions and 30 deletions

View file

@ -1,6 +1,6 @@
{- git-annex metadata {- git-annex metadata
- -
- Copyright 2014 Joey Hess <id@joeyh.name> - Copyright 2014-2016 Joey Hess <id@joeyh.name>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
@ -8,6 +8,8 @@
module Annex.MetaData ( module Annex.MetaData (
genMetaData, genMetaData,
dateMetaData, dateMetaData,
parseModMeta,
parseMetaDataMatcher,
module X module X
) where ) where
@ -17,6 +19,7 @@ import Types.MetaData as X
import Annex.MetaData.StandardFields as X import Annex.MetaData.StandardFields as X
import Logs.MetaData import Logs.MetaData
import Annex.CatFile import Annex.CatFile
import Utility.Glob
import qualified Data.Set as S import qualified Data.Set as S
import qualified Data.Map as M import qualified Data.Map as M
@ -53,3 +56,37 @@ dateMetaData mtime old = MetaData $ M.fromList $ filter isnew
where where
isnew (f, _) = S.null (currentMetaDataValues f old) isnew (f, _) = S.null (currentMetaDataValues f old)
(y, m, _d) = toGregorian $ utctDay mtime (y, m, _d) = toGregorian $ utctDay mtime
{- Parses field=value, field+=value, field-=value, field?=value -}
parseModMeta :: String -> Either String ModMeta
parseModMeta p = case lastMaybe f of
Just '+' -> AddMeta <$> mkMetaField f' <*> v
Just '-' -> DelMeta <$> mkMetaField f' <*> v
Just '?' -> MaybeSetMeta <$> mkMetaField f' <*> v
_ -> SetMeta <$> mkMetaField f <*> v
where
(f, sv) = separate (== '=') p
f' = beginning f
v = pure (toMetaValue sv)
{- Parses field=value, field<value, field<=value, field>value, field>=value -}
parseMetaDataMatcher :: String -> Either String (MetaField, MetaValue -> Bool)
parseMetaDataMatcher p = (,)
<$> mkMetaField f
<*> pure matcher
where
(f, op_v) = break (`elem` "=<>") p
matcher = case op_v of
('=':v) -> checkglob v
('<':'=':v) -> checkcmp (<=) v
('<':v) -> checkcmp (<) v
('>':'=':v) -> checkcmp (>=) v
('>':v) -> checkcmp (>) v
_ -> checkglob ""
checkglob v =
let cglob = compileGlob v CaseInsensative
in matchGlob cglob . fromMetaValue
checkcmp cmp v v' = case (doubleval v, doubleval (fromMetaValue v')) of
(Just d, Just d') -> d' `cmp` d
_ -> False
doubleval v = readish v :: Maybe Double

View file

@ -5,7 +5,7 @@
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
{-# LANGUAGE CPP, BangPatterns #-} {-# LANGUAGE CPP #-}
module Backend.Hash ( module Backend.Hash (
backends, backends,

View file

@ -23,6 +23,7 @@ import Types.TrustLevel
import Types.Group import Types.Group
import Types.FileMatcher import Types.FileMatcher
import Types.MetaData import Types.MetaData
import Annex.MetaData
import Logs.MetaData import Logs.MetaData
import Logs.Group import Logs.Group
import Logs.Unused import Logs.Unused
@ -278,14 +279,12 @@ addMetaData :: String -> Annex ()
addMetaData = addLimit . limitMetaData addMetaData = addLimit . limitMetaData
limitMetaData :: MkLimit Annex limitMetaData :: MkLimit Annex
limitMetaData s = case parseMetaData s of limitMetaData s = case parseMetaDataMatcher s of
Left e -> Left e Left e -> Left e
Right (f, v) -> Right (f, matching) -> Right $ const $ checkKey (check f matching)
let cglob = compileGlob (fromMetaValue v) CaseInsensative
in Right $ const $ checkKey (check f cglob)
where where
check f cglob k = not . S.null check f matching k = not . S.null
. S.filter (matchGlob cglob . fromMetaValue) . S.filter matching
. metaDataValues f <$> getCurrentMetaData k . metaDataValues f <$> getCurrentMetaData k
addTimeLimit :: String -> Annex () addTimeLimit :: String -> Annex ()

View file

@ -36,8 +36,6 @@ module Types.MetaData (
metaDataValues, metaDataValues,
ModMeta(..), ModMeta(..),
modMeta, modMeta,
parseModMeta,
parseMetaData,
prop_metadata_sane, prop_metadata_sane,
prop_metadata_serialize prop_metadata_serialize
) where ) where
@ -239,26 +237,6 @@ modMeta m (MaybeSetMeta f v)
| S.null (currentMetaDataValues f m) = updateMetaData f v emptyMetaData | S.null (currentMetaDataValues f m) = updateMetaData f v emptyMetaData
| otherwise = emptyMetaData | otherwise = emptyMetaData
{- Parses field=value, field+=value, field-=value, field?=value -}
parseModMeta :: String -> Either String ModMeta
parseModMeta p = case lastMaybe f of
Just '+' -> AddMeta <$> mkMetaField f' <*> v
Just '-' -> DelMeta <$> mkMetaField f' <*> v
Just '?' -> MaybeSetMeta <$> mkMetaField f' <*> v
_ -> SetMeta <$> mkMetaField f <*> v
where
(f, sv) = separate (== '=') p
f' = beginning f
v = pure (toMetaValue sv)
{- Parses field=value -}
parseMetaData :: String -> Either String (MetaField, MetaValue)
parseMetaData p = (,)
<$> mkMetaField f
<*> pure (toMetaValue v)
where
(f, v) = separate (== '=') p
{- Avoid putting too many fields in the map; extremely large maps make {- Avoid putting too many fields in the map; extremely large maps make
- the seriaization test slow due to the sheer amount of data. - the seriaization test slow due to the sheer amount of data.
- It's unlikely that more than 100 fields of metadata will be used. -} - It's unlikely that more than 100 fields of metadata will be used. -}

4
debian/changelog vendored
View file

@ -15,6 +15,10 @@ git-annex (6.20160218) UNRELEASED; urgency=medium
* Fix memory leak when hashing files, which triggered during fsck * Fix memory leak when hashing files, which triggered during fsck
when an external hash program was not used. when an external hash program was not used.
(This leak was introduced in version 6.20160114.) (This leak was introduced in version 6.20160114.)
* Support --metadata field<number, --metadata field>number etc
to match ranges of numeric values.
* Similarly, support preferred content expressions like
metadata=field<number and metadata=field>number
-- Joey Hess <id@joeyh.name> Thu, 18 Feb 2016 13:09:21 -0400 -- Joey Hess <id@joeyh.name> Thu, 18 Feb 2016 13:09:21 -0400

View file

@ -115,6 +115,15 @@ file contents are present at either of two repositories.
matches the glob. The values of metadata fields are matched case matches the glob. The values of metadata fields are matched case
insensitively. insensitively.
* `--metadata field<number` / `--metadata field>number`
* `--metadata field<=number` / `--metadata field>=number`
Matches only files that have a metadata field attached with a value that
is a number and is less than or greater than the specified number.
(Note that you will need to quote the second parameter to avoid
the shell doing redirection.)
* `--want-get` * `--want-get`
Matches files that the preferred content settings for the repository Matches files that the preferred content settings for the repository

View file

@ -119,6 +119,15 @@ elsewhere to allow removing it).
To match author metadata, use `metadata=author=*Smith` To match author metadata, use `metadata=author=*Smith`
* `metadata=field<number` / `metadata=field>number`
* `metadata=field<=number` / `metadata=field>=number`
Matches only files that have a metadata field attached with a value that
is a number and is less than or greater than the specified number.
To match PDFs with between 100 and 200 pages (assuming something has set
that metadata), use `metadata=pagecount>=100 and metadata=pagecount<=200`
* `present` * `present`
Makes content be wanted if it's present, but not otherwise. Makes content be wanted if it's present, but not otherwise.

View file

@ -67,3 +67,4 @@ they were added in.
* "metadata=" 5.20140221 * "metadata=" 5.20140221
* "lackingcopies=", "approxlackingcopies=", "unused=" 5.20140127 * "lackingcopies=", "approxlackingcopies=", "unused=" 5.20140127
* "inpreferreddir=" 4.20130501 * "inpreferreddir=" 4.20130501
* "metadata=field&lt;number" etc 6.20160227