Sane ranges for DB queries
The code isn't especially bad and it seems to cover all bases but I agree it's not nice to look at. Perhaps some architectural observations may assist you in making it cleaner. 

Firstly, I would not recommend using - 1 as a special value to indicate that a property is set or not. In this particular context it places an additional burden on you as the developer to remember what it means. Simply rely on the fact that a property is defined or it isn't. 

A second observation is that I shouldn't worry about type checking unless you permit different "kinds" of range objects. If you've published your API that states a range object should be passed to this function, then have confidence that it will be used that way. If you must have it, consider inverting it so you don't have to nest the chain of if statements inside it.

A pattern I have seen regularly in this kind of code is to merge the user object with a default one, and then ask the question "does this make sense? ". With that said, be careful with modifying what has been passed to the function, unless behaviour is clearly published somewhere. What can happen is the user of this function will create a dodgy range object that gets corrected by your function and used in the DB query. The key part of this story is the user didn't know what they did was wrong and will carry on regardless. If it's wrong, give an error or let the query take ages and timeout, they'll have to learn some time!