The first query tests each text node in the document separately, so <P><B>Tom</B> and <I>Jerry</I></P>
would match because the first text node contains Tom but not Jerry.
In the second query the full-text search is performed on all the text contents of the Doc
elements as if they were concatenated into one string. This cannot (currently) be answered by BaseX's fulltext index, which indexes each text node separately.
A solution would be to perform the fulltext searches for each term separately and merging the results in the end. This can be done for each text node separately, so the index can be used:
for $x in (doc('Documents')/Docs/Doc[.//text() contains text 'Tom']
except doc('Documents')/Docs/Doc[.//text() contains text 'Jerry'])
return $x/Name
The above query is rewritten by the query optimizer to this equivalent one using two index accesses:
for $x in (db:fulltext("Documents", "Tom")/ancestor::*:Doc
except db:fulltext("Documents", "Jerry")/ancestor::*:Doc)
return $x/Name
You can even tweak the order in which you are merging the results in order to keep intermediate results small if you want.