I have recreated your setup.
>>> Post.query.whoosh_search('fourth not').all()
>>> [<Post u'not my fourth'>, <Post u'my fourth and last post'>]
The question you should have asked is: Why can't whoosh_search find not? Try this.
>>> Post.query.whoosh_search('not').all()
>>> []
This should have returned the post 'not my fourth', right?
According to the "Stop Words" section in this document, “Stop” words are words that are so common it’s often counter-productive to index them.
This question has a link which shows that by default 'not' is a stop word, and whoosh_search does not index it.
So lets add another post with 'fourth' and a less common word - how about 'cheese'.
>>> p = Post(body='cheese is the fourth food group', timestamp=datetime.datetime.utcnow(), author=u)
>>> db.session.add(p)
>>> db.session.commit()
And now lets search for all posts with 'fourth' AND 'cheese' in the body.
>>> Post.query.whoosh_search('fourth cheese').all()
>>> [<Post u'cheese is the fourth food group'>]
Perfect.
BONUS: if you want to get all posts with 'fourth' OR 'cheese', do this:
>>> Post.query.whoosh_search('cheese fourth', or_=True).all()
>>> [<Post u'cheese is the fourth food group'>, <Post u'not my fourth'>, <Post u'my fourth and last post'>]