spring reactive: check if user exists or not - spring

I am using spring reactive and need to check weather user with particular
data exists or not and currently I am not able to solve that problem.
Considering the scenerio
In my document I need to check if username or email already exists or not
In RDBS I can do it as
select count(id)>0 where username='abc' or email='abc#idx.com'
while using spring reactive which returns either mono or flux the simple most query becomes
{$or:[{"username":"abc"},{"email":"abc#idx.com"}]} which will return flux but I need boolean to verify from db
On solution is that I can get Flux<User> and the iterate it using form loop but then using ' result.block()' whick will block some other threads and therefore not a clean solution.
Is there any clean solution or any Idea how to solve this.
Thanks
Edit One possible solution can be creating unique indexing in monogdb, that I am using right now. But if there is any other solution please let me know

Using Spring Data MongoDB, you can use something like below:
public interface ReactiveUserRepository extends ReactiveSortingRepository<User, Long> {
Mono<User> findByUsernameAndEmail(String username, String email);
}
Find a single entity for the given criteria. It completes with IncorrectResultSizeDataAccessException on non-unique results.
You can refer to the complete syntax here:
https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/

Related

Quarkus PanacheEntityBase query with IN

I have a customer entity of type PanacheEntityBase. I would like get all customers by ids
I can get it with customer.list("customerId", id) in a loop. I dont think its a good solution. I prefer using IN. Please suggest how to use IN with PanacheEntityBase
I solved the problem with customerEntity.list("customerId IN (?1)", customerIds);
Note: customersids must be a java list (Array did not work)

How to check if exists when using CrudRepository#getOne()

I have a Document entity. To edit a document you must acquire a DocumentLock.
Request API to edit a document looks like below. edit=true allows to fetch the record from database with 'FOR UPDATE`.
GET /api/document/123?edit=true
In Back end side, we do like this (of course over simplified),
Document document = documentRepository.getOne(documentId) //<--- (1)
if(document == null){ //<--- (2) This is always false
//Throw exception
}
DocumentLock dl = DocumentLock.builder()
.lockedBy(user)
.lockedAt(NOW)
.document(document)
.build()
documentLockRepository.save(dl);
We are using getOne() because we do not need to fetch whole document from database, we are just creating a relation with DocumentLock object.
Question is, how to ensure the document actually exists? Because getOne() will always return the proxy and I do not see any obvious way to check if the record exists in databse.
Versions:
spring-data-jpa: 2.2.6.RELEASE
hibernate: 5.4.12
Update: removed additional questions.
Use findById or existsById. Yes they do access the database. That is the point of it, isn't it?
getOne is explicitly for use cases where you already know the entity exists and only need the id wrapped in something that looks like the entity.
If your repository returning value or its transactional management is confusing, you should check your repository codes and entity class design even session configurations that used for querying or even your Datastore design because everything looks fine in this code fragment

Spring reactive: Chaining repository results

Repository repo
Repository otherRepo
foreach entity : repo.FindAll() {
entityFind = otherRepo.FindById(entity.Prop)
if (entityFind != null) {
return entityFind
}
}
How could I do this using the spring reactive?
I could use blockFirst() to search in otherRepo but it would break the reaction chain
I also have tried use a handle() to control the flow but I don't get to break the flow when I find an item
Any idea?
Thanks
If you have repos like this, for each record of repo1, if you need to find a record from repo2, you could probably join the tables using spring data JPQL & use your custom method instead as your current approach could have performance impact.
As you seem to be interested only in the first record, Just to give you an idea, We can achieve something like this.
return Flux.fromIterable(repo.findAll()) //assuming it returns a list
.map(entity -> otherRepo.findById(entity.property)) // for each entity we query the other repo
.filter(Objects::nonNull) // replace it with Optional::isPresent if it is optional
.next(); //converts the flux to mono with the first record
The answer from vins is assuming non-reactive repository, so here it is in a fully reactive style:
return repo.findAll() //assuming reactive repository, which returns Flux<Entity>
.flatMap(entity -> otherRepo.findById(entity.property)) //findById returns an empty Mono if id not found, which basically gets ignored by flatMap
.next(); //first record is turned into a Mono, and the Flux is cancelled
Note that as you've stated, this can lead to unnecessary requests being made to Cassandra (and then cancelled by the next()). This is due to flatMap allowing several concurrent requests (256 by default). You can either reduce the parallelism of flatMap (by providing a second parameter, an int) or use concatMap to perform findById queries serially.

sdn4.0 set resultDataContents graph

I want to use SDN4.0 to visualize by D3 in web application.For example,I want to use the following cypher query to get data:
#Query("MATCH (n:app)-[r:r1]->(m:app) RETURN n.alias,r,m.alias")
Iterable<Map<String, Object>> getAllRelations();
But the httpServer not response the exact data I want.
[{n.alias=A, r=(227)-[r1]->(235), m.alias=B}, ....]
And I want to response r1's properties ,tried r1.* but failed.
From the http://neo4j.com/developer/guide-data-visualization/ there is possible to set resultDataContents to graph(origin as rest)
So Is it any possiblity to set this parameter in SDN4.0 or have other solutions?
Thanks if having any ideas.
SDN is for creating domain rich applications. As such it's not the best tool to use if all you need is a list of properties to represent a graph.
You have a couple of paths as I can see it:
Model your application properly with SDN. Use #NodeEntity on a class called App and create a #Relationship to another App. You can then leverage SDN's repositories to return you a rich domain model which you can then transform (e.g. using DTO's) to the front end if need be.
Use the java Neo4j client, OGM HTTP driver (Undocumented), or if you are happy to completely use Javascript (either from the browser or with meteor or using a NodeJS server) you can just use the Javascript driver and call the database directly.
Either way if you are using D3 I highly recommend you use JSOG to represent your model in the front end.
How do I query for relationship data in spring data neo4j 4?
Above Answer solved my question.By using Neo4jOperations.queryForObjects() under Spring data Neo4j and return path in cypher query.

Spring Data Repository - Paging large data sets (EclipseLink)

I am using Spring Data with EclipseLink JPA to do server side pagination on a database result set. I have everything working and I get the expected paged results, but I noticed performance suffering on large data sets (several million rows). It is taking about 5 minutes to return a page of 20 results. Perhaps this is to be expected, but what concerned me was the query output.
My log output:
SELECT COUNT(filename) FROM document
SELECT filename, datecaptured, din, docdate, docid, doctype, drawer, foldernumber, format, pagenumber, tempfilename, userid FROM document ORDER BY din ASC
I would understand that in order to page, Spring would need to know the max row count, so the first query makes sense.
The second query is pulling the entire database when I specifically only asked for 20 results with a 0 offset (page).
Does Spring/EclipseLink/JPA in fact grab the entire data set and then only return the subset paged request?
If that is the case, how should I modify my repository class to be more efficient?
My test case:
#Test
public void getPagedDocumentsTest() throws IOException {
Page<Document> requestedPage = documentRepository.findAll(new PageRequest(0, 20, Sort.Direction.ASC, "din"));
Assert.assertNotNull("Page is null", requestedPage);
Assert.assertNotNull("Page is empty", requestedPage.getContent());
List<Document> documents = requestedPage.getContent();
LOG.info("{}", documents);
LOG.info("{}", documents.size());
}
My repository class:
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
import com.example.data.model.Document;
#Repository
public interface DocumentRepository extends PagingAndSortingRepository<Document, String> {
}
Edit - per #Chris's suggestion
Tried adding the platform to my properties, but it didn't make a difference:
eclipselink.weaving=static
eclipselink.allow-zero-id=true
eclipselink.target-database=SQLServer
eclipselink.logging.level=FINE
Also tried adding it to my configuration (I'm using Java Config):
#Bean
public LocalContainerEntityManagerFactoryBean entityManager() {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setPersistenceUnitName("ExampleUnit");
factory.setPackagesToScan("com.example.data.model");
EclipseLinkJpaVendorAdapter eclipseLinkVendorAdapter = new EclipseLinkJpaVendorAdapter();
eclipseLinkVendorAdapter.setDatabase(Database.SQL_SERVER);
eclipseLinkVendorAdapter.setDatabasePlatform("SQLServer");
factory.setJpaVendorAdapter(eclipseLinkVendorAdapter);
factory.setDataSource(dataSource());
factory.setJpaProperties(jpaProperties());
factory.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
return factory;
}
Looks like the platform is set correctly.
[EL Config]: connection: 2015-08-06 12:04:05.691--ServerSession(686533955)--Connection(1896042043)--Thread(Thread[main,5,main])--connecting(DatabaseLogin(
platform=>SQLServerPlatform
user name=> ""
connector=>JNDIConnector datasource name=>null
))
But neither helped. The SQL query output remained the same as well.
Edit
Found a related question with a similar answer from #Chris:
EclipseLink generated SQL doesn't include pagination
EclipseLink 2.5 source that I checked I believe has support for database level filtering built into the following database platform classes:
DB2Platform
DerbyPlatform
FirebirdPlatform
H2Platform
HANAPlatform
HSQLPlatform
MySQLPlatform
OraclePlatform
PostgreSQLPlatform
SymfowarePlatform
Each of these override the printSQLSelectStatement method to take advantage of their respective database features to allow filtering in the SQL itself. Other platforms will need to use JDBC filtering, which depend on the driver to restrict rows - they may be able to optimize queries, but it is driver specific and I believe it is why your query takes longer then you desire.
I don't know SQLServer well enough to say what equivalent functionality it has that can be used within the SQL, but if you find it, you would need to create a SQLServerPlatform subclass, override the printSQLSelectStatement method as is done in the above classes, and then specify that platform class be used instead. Please also file a bug/feature to have it included in EclipseLink.
Other options are described here:
http://wiki.eclipse.org/EclipseLink/Examples/JPA/Pagination
One thing you should consider is whether you actually need to know the number of pages / total number of elements. If you are returning a page from a result set that has milions of elements, chances are your users will not be interested in looking through all those pages either way :). Maybe your front end shows the data in a an infinite scroll that just needs to know, if there are any more pages, instead of number of pages.
If any of those cases apply to you, you should consider returning a Slice instead of a Page, as in:
public Slice<MyClass> findByMyField(..);
This way, instead of doing the expensive Count, Spring Data will just ask for one more element than you originally wanted. If that element is present, the Slice will return true from the hasNext method.
Where I work we recently used Slices for several large data sets and with the right indexes (and after clearing the database cache :) we have seen some really significant gains.

Resources