Break up the materials into a heirarchical database. Have a root node, and branches for each main type, INORGANIC, PLANT etc. Every node in the database becomes a token. You need to pre-prepare specialized data-structures to get this much faster than it is now.
Then, do matches on tokens that have already been split up, rather than an entire mat def with wildcards. e.g. PLANT would contain a vector referencing all PLANTs, so if your top-level search matches "PLANT" you only have to look inside the PLANT vector for future matches. Each PLANT would have a vector for all materials it contains, and this would point at a vector, e.g. a "LEAF" vector, which references all things with "LEAF" as a bottom-level material type.
For "LEAF" as a concept you could have all the plants with leaves containing a reference to a LEAF vector, this would tell you which plants have leaves, one way to do this would be by searching all the plants materials. But the LEAF data structure could actually contain references to all things that have leaves. So when you get the Query PLANT:*:LEAF, you could work backwards. Scan the LEAF vector, and for each item check if the mid-string matches the direct ancestor, you can actually skip the check altogether by detecting a pure '*' wildcard in the middle, then check for each one "is it a plant?" just in case some animal has leaves (Ents maybe).