Couchbase vol2, with/without Sync Gateway — better than your girlfriend :P

CouchbaseQueryExecutor vol2.

Welcome back (smile)! In a previous post I wrote about a library I made to dynamically build queries for Couchbase. This dynamic query-building solves the filterable + sortable list problem. Hope you liked that post. In case you didn’t read it, you totally should read it (big grin). Today I’m going to introduce the changes I made to that library. Since the last post I got into Java8 Black Magic, and I used it to improve the couchbase-query-executor.

First of all, we started to use vavr which has lots and lots of useful stuff for Java8, I’m going to talk about Either<L, R>

Also, I made a full example of how to use the library with and without Sync Gateway. You can take a look at the full source code here.

The fancy example app, and the new features in the library

Thanks to Either<L, R> let’s see our new controller function!

As you see (only) in the example application I postfixed everything (classes/variables/URLs/etc.) with Cb/Sg. The example application contains how to use the lib with and without Sync Gateway both. I removed the postfixes in this blogpost, because the listing API does not depend on whether or not you use Sync Gateway. There is only a little difference between the usage. If you want to know how to create users take a look at the source code (smile)

Okay, so thanks to vavr we have Either<L, R>, which is a pretty simple feature to use. UserServiceCb returning Either<ErrorDto, Page<UserListResponseDto>>(you can read it like Either ErrorDto or Page UserListResponseDto). It means that if the findAll function fails, it will give you the error in the L (Left) value of the Either. If it is executed succesfully, the return value will be in the R (Right) value. The fold method of Either has two parameters, the first is a function, that runs if the Left value is defined, the second one is a function that runs if the Right value is defined. So the controller function is pretty simple. If the findAll method successfully returns a Right value, we convert it to toResponse, if it doesn’t, we convert the errorToResponse.

The getParameterMapWithOnlyFirstValues does the same — as I mentioned in the last post.

Okay so this is the UserService. We have some static variables at the beginning, they hold the names of the variables in the queryString (REQUEST_PARAM) and the fields you wanna filter on (FILTER_PARAMS), nothing special. Thanks to jackpf‘s fork I added some more functionality to the library, like nested filtering (address.zipCode), and .isNull(), .isNotNull(), .isMissing(), .isNullOrMissing filters.

Basically what filtersFromParams does is mapping the queryString to queryParameters. After you made a new instance of Parameters, you can start telling the executor how to translate your queryString variables.

First, you should call the .on(String key) function to tell the executor what key you want to filter the documents with. After that, you should tell the executor what kind of filtering it is (is, isNot, from, to, etc.). calling the appropriate function with the value will tell the executor what you want to do.

After this you have three options. If you don’t want the executor to do anything with your parameter (add it everytime, and don’t parse from string to anyhing else) just call the .add() function.

Use it like this, and the executor will give you ACTIVE users only.

Otherwise, if you want your queryParameter to appear in the where clause, add an .onlyIf(Predicate<String> condition) in the next call. The executor has a built-in condition .onlyIfNonEmpty() – which checks whether the string is not empty. Executor will only add the parameter to the where clause if the value you gave matches the given condition. If there is no condition Executor will always add it. In the code you can see this example at ‘username’ which is only added if the value is not empty, and at ‘age’ and ‘zipCode’ which are only added if they are positive (I know that there could be a check for the ‘+’ sign at the beginning of the string, and so on, but this is just a fast example on how to use the .onlyIf(Predicate<String> condition) function (smile)).

If you don’t want to add a condition, but want the Executor to parse your value with a function (like at ‘age’ and ‘zipCode’) you can pass a parser function with the .andApply(Function<String, T> parser) where the T type parameter is generic and can be anything you want. In the example I parse the ‘age’ and ‘zipCode’ to an integer. IMPORTANT: parser will only apply if the value matches the condition, so you don’t have to worry about IllegalArgumentException or any other (smile). If you don’t want to add a parser, just call .add().

And if you want to combine the two, you can give a condition and a parser together. First give the condition, then the parser, and that’s all.

Alex Sükein

Alex Sükein

- Süxy, can you tell me about robust software development Nasa uses?
- Yes. If we have a final exam tomorrow.