I have a Flux.fromIterable(list of ids) . I want to find out if any of the record is null .So I am trying to use Flux.any but I see that I does not even print anything inside any and directly goes to doOnEach as a result output is false. How can we fix this?
The solution should not be limited to null check.It can be any boolean condition.
Mono<Boolean> isAnyNull =Flux.fromIterable(request.getIds())
.switchIfEmpty(Mono.error(new SomeException("No elements")))
.flatMap(id->{
return FooRepo.find(id);
}).any(p->{
System.out.println("check is any null p?."+p);
return ((p==null)||(p.getId()==null));
}).doOnEach(System.out::println);
I tried below as a temp weird fix but i am not sure if this is correct.Also I think it will only work for null.
Mono<Boolean> isAnyNull = Flux.fromIterable(request.getds())
.switchIfEmpty(Mono.error(new SomeException("No elements")))
.flatMap(id -> {
return FooRepo.find(id);
})
.switchIfEmpty(Mono.error(new SomeException("INVALID_ID"))).hasElements()
.doOnEach(System.out::println);
Update : This also works and check null using stream -Comments appreciated to improve
Flux.fromIterable(request.getIds())
.switchIfEmpty(Mono.error(new SomeException("No elements")))
.flatMap(id -> {
return FooRepo.find(id);
})
.any(foo -> {
return (Objects.nonNull(foo)||foo.getId()!=null);
}).doOnEach(System.out::println);
Related
I'm using spring reactor. The code below is :
public Mono<ResponseEntity<SignUpResponse>> createSignUpForUser(SignUpRequest signUpRequest) {
return Mono.just(signUpRequest)
.map(sign -> {
Mono<UserDetailsEntity> userDetailsEntityMono = userDetailsRepository.findByPhoneNumber(sign.getMobileNumber());
userDetailsEntityMono.handle((user, sink) -> {
if (user != null) {
sink.error(new RuntimeException("Phone number already registered"));
}
});
return functionUserDetails.apply(sign);
})
.flatMap(userDetailsRepository::save)
.map(functionUser)
.flatMap(userRepository::save)
.map(usr -> ResponseEntity.ok(functionSignUpRes.apply(usr)))
.defaultIfEmpty(ResponseEntity.notFound().build())
.log();
}
Here the findByPhoneNumber(sign.getMobileNumber()) DB call is not working (the error is not throwing). The Rest of the operations are working and returning the response. Am i doing anything wrongly ? help me to fix this issue.
I think you need to rewrite your code a bit
public Mono<ResponseEntity<SignUpResponse>> createSignUpForUser(SignUpRequest signUpRequest) {
return userDetailsRepository.findByPhoneNumber(sign.getMobileNumber())
.flatMap(__ -> Mono.error(new RuntimeException("Phone number already registered")))
.switchIfEmpty(userDetailsRepository.save(functionUserDetails.apply(sign)))
.map(functionUser)
.flatMap(userRepository::save)
.map(usr -> ResponseEntity.ok(functionSignUpRes.apply(usr)))
.defaultIfEmpty(ResponseEntity.notFound().build())
.log()
;
}
Idea is that if you find that number then it should error, if result is empty then you would need to insert it so we need to use switch if empty
My code:
Mono.zip(
credentialService.getCredentials(connect.getACredentialsId()),
credentialService.getCredentials(connect.getBCredentialsId())
)
.flatMap(...
From the frontend we get connect object with 2 fields:
connect{
aCredentialsId : UUID //required
bCredentialsId : UUID //optional
}
So sometimes the second line credentialService.getCredentials(connect.getBCredentialsId())) can return Mono.empty
How to write code to be prepared for this empty Mono when my second field bCredentialsId is null?
What should I do? In case of empty values return Mono.just(new Object) and then check if obj.getValue != null??? I need to fetch data from DB for 2 different values
The strategy I prefer here is to declare an optional() utility method like so:
public class Utils {
public static <T> Mono<Optional<T>> optional(Mono<T> in) {
return in.map(Optional::of).switchIfEmpty(Mono.just(Optional.empty()));
}
}
...which then allows you to transform your second Mono to one that will always return an optional, and thus do something like:
Mono.zip(
credentialService.getCredentials(connect.getACredentialsId()),
credentialService.getCredentials(connect.getBCredentialsId()).transform(Utils::optional)
).map(e -> new Connect(e.getT1(), e.getT2()))
(...assuming you have a Connect object that takes an Optional as the second parameter of course.)
An easier way is using mono's defaultIfEmpty method.
Mono<String> m1 = credentialService.getCredentials(connect.getACredentialsId());
Mono<String> m2 = credentialService.getCredentials(connect.getBCredentialsId()).defaultIfEmpty("");
Mono.zip(m1, m2).map(t -> connectService.connect(t.getT1(), t.getT2()));
Explanation: if m2 is null then get empty string as a default value instead of null.
Instead of using .zip here, I would work with a nullable property of Connect and use .flatMap in combination with .switchIfEmpty for it.
Kotlin-Version:
val aCredentials = credentialService.getCredentials(connect.getACredentialsId())
credentialService.getCredentials(connect.getBCredentialsId())
.flatMap { bCredentials -> aCredentials
.map { Connect(it, bCredentials)}
.switchIfEmpty(Connect(null, bCredentials))
}
.switchIfEmpty { aCredentials.map { Connect(it, null) } }
I have the following code which I am trying to use for two purposes:
1) Call an API and get result as a POJO
2) Sanitize this object (POJO) before I display it in the UI
private fun getWinbackDataItems(rewardPurpose: String) /*Single<WinbackBaseItem>*/ {
val x = repository.getRewardsList(rewardPurpose)
.filter {
it.result?.rewards != null
}.map { winback ->
winback.result?.rewards?.asSequence()?.filter { rewardsItem ->
rewardsItem?.id != null && rewardsItem.title != null
}?.toList()?.take(3)?.map {
WinbackListItem(it?.id, it?.title!!, false)
}?.toList()
}
}
The point of contention for me is the line below:
itemListSanitized.add(WinbackListItem(it.id, it.title, false))
At this point I assume the filter has removed all nulls from the original list but to my amazement I find that I have to null check on it and all its content while adding them to the new list.
What do I miss here, pardon my naivety as I have just begun reactive
I take it that you are working not against executing code but against your IDE's warning messages or just the ability for this code to compile. What you're probably running up against is that earlier checks for null won't necessarily allow the compiler to assume non-null values later on, because in the meantime, other code in a different thread could have run and changed the values.
So when you create a WinbackListItem, you can safely assume that certain items are not null, and yet the compiler can't be sure of this, because it can't know what else is going on in your process space. So the compiler requires that you tell it not to worry about null values (!!) or that you check the values again. This is just the way Kotlin works. It's often a PITA, but it's just how it is.
I played with the posted code just to be sure I knew what I was talking about. Here is code that I was able to run:
private fun getWinbackDataItems(rewardPurpose: String) /*Single<WinbackBaseItem>*/ {
val x = repository.getRewardsList(rewardPurpose)
.filter {
it.result?.rewards != null
}.map { winback ->
winback.result?.rewards?.asSequence().filter { rewardsItem ->
rewardsItem.id != null && rewardsItem.title != null
}.toList().take(3).map {
println(it.id)
println(it.title)
WinbackListItem(it.id!!, it.title!!, false)
}.toList()
}.count()
}
I created some very simple classes and objects to satisfy this code and let it run. Note that I took out some unnecessary '?' null checks. I played with input values until I was convinced that it.id and it.title can never be null when the WinbackListItem constructor is called. And yet, the two !! on its parameters, or something else making sure they are not null, are required given this definition of WinbackListItem that won't accept null parameter values:
class WinbackListItem(val id: Int, val title: String, val huh: Boolean)
I'm looking for a function in Kotlin which stops an iteration once a predicate is fulfilled:
val services = listOf(service1, service2)
...
var res: Result = null
services.stopIfPredicateFulFilled { service ->
res = service.doSomething()
res != null
}
While this example is not really nice since res is overwritten in each iteration I hope the intention is clear.
forEach doesn't do the job the way I expect it to be done. So, I was wondering if there isn't anything else.
You can use the functions find { ... } and firstOrNull { ... } (they are equivalent, just named differently). They find the first element satisfying the predicate and return that element, ignoring all the remaining elements.
services.find { service ->
res = service.doSomething()
res != null
}
How do you preset fields so that (unless a specific value is entered from the form itself) they STAY null? For another project, I will later have to pull information from this table depending on what options people choose, so if I could do a cfif against nulls I think it'd be a lot easier than the blanks that are currently generated if I don't insert any new values.
Does anyone know where/how to do this? I'm using Microsoft's SQL Server Management Studio to edit what the individual columns, and all I can find are the command codes using INSERT, SELECT, etc., rather than having a list that I edit. Or is that the only way to make my default setting be "null"?
Thanks for the help
If a field is set to be a nullable field (that is, it allows NULL), when adding a row, it will be NULL unless otherwise specified.
You don't need to do anything special for this.
If your INSERT and UPDATE statements simply omit the field, it will not be updated, though you can specifically specify NULL for such a field if wanted.
You could use a helper function to handle parameter setup. This is a simplified version of a function I use...
private static object ProcessParameter(object input)
{
if (input == null)
return input;
switch (input.GetType().ToString().ToLower())
{
case "system.string":
if (input == null || input.ToString() == "") { return DBNull.Value; }
return input;
case "system.int32":
case "system.double":
if (input.ToString() == "0" && IsNullable(input)) { return DBNull.Value; }
return input;
case "system.datetime":
if (System.Convert.ToDateTime(input) == DateTime.MinValue || System.Convert.ToDateTime(input) == default(DateTime)) { return DBNull.Value; }
return input;
default:
return input;
}
}