Writing custom n1ql queries in spring - spring-data

I'm trying to query a spring-data couchbase repository using N1QL queries. I have two doubts:
I'm using #Query annotation to generate queries, my code looks like this:
#Query("#{#n1ql.selectEntity} WHERE $0 = $1 AND #{#n1ql.filter}")
public Page<GsJsonStore> matchJson(String term, String value, Pageable pageable);
//Query
Page<GsJsonStore> p = repo.matchJson("_object.details.status", "ready", pg);
This query doesn't return any results. However, when I run the same query (below) in cbq I get the desired result:
select * from default where _object.details.status = 'ready';
How can I view the query string generated by the Couchbase repository? I'm using spring-boot. Am I correct in using the #Query annotation for this use-case?
Also, how can I perform n1QL queries on CouchbaseOperations template? I know that there's a findByN1QL method, but I didn't find any good documentation about it. Could someone please explain how to use this?

The query looks ok. You did persist your GsJsonStore entities using the Spring Data Couchbase repository did you?
In order to log all the queries generated and executed by the framework (including inline queries like in your case), you can configure the logger like so in a logback.xml configuration:
<logger name="org.springframework.data.couchbase.repository.query" level="debug"/>
You'll see that the query that got executed and the query that you ran in cbq are not the same, since at least you didn't use a WHERE clause.
In CouchbaseOperations there are two methods relative to N1QL queries:
findByN1QL: this expects specific structure of the query, in order to make sure all data necessary to correct deserialization of a Spring Data annotated entity is selected (which is the purpose of the #n1ql.selectEntity and #n1ql.filter SpEL).
findByN1QLProjection is more free-form. If Jackson can deserialize the results of the provided query to the requested Class, then it will. As such, the SELECT clause is much less implicitly restricted in this method.
To use both, you must pass in a N1qlQuery object from the SDK. Such queries can be constructed using factory methods of the N1qlQuery class, for example:
//a version of the query that is constructed from positional parameters
N1qlQuery queryWithParameter = N1qlQuery.parameterized("SELECT name FROM `beer-sample` WHERE name LIKE $0", JsonArray.from("%dog%"));
//let Spring Data execute the query, projecting to the String class
List<String> beerNamesContainingDog = template.findByN1QLProjection(queryWithParameter, String.class);

Related

Entity Framework query based on string stored in configuration file

i would like to know if you have any idea how i can achieve this, considering a query stored as string in the configuration file.
I tried to use SqlQuery applied to the DBSet, but the problem is that SqlQuery requires me to select all properties of the required entities in my query. If i don't consider any column, it will complain because is not able to map the query to the entities.
I don't want to select all properties of the entities i want to query.
Thanks
If you are using EF then why not use Database.ExecuteSqlCommand()? It's in the System.Data.Entity namespace.
For example:
int result = db.Database.ExecuteSqlCommand("Non SELECT SQL etc...");
Well, I ended up implementing a mechanism using reflection that basically receives a group of fields to select, and constructs dynamic objects with those fields, so when applied the query with the joins between the entities, will only bring the fields I am looking for.
So, considering Entity1, Entity2, Entity3 with the following relationship
<b>Entity1</b>{
<br/> Entity1Name, <br/> List<*Entity2*> Entity2Items, <br/> etc..
<br/>}
and
<b>Entity2</b> { <br/> Entity2Name, <br/> List<*Entity3*> Entity3Items <br/>}
I can store e.g. the following query in the configuration file, and retrieve the information:
"Entity1.Entity1Name", <br/>
"Entity1.Entity2Items.Entity2Name", <br/>
"Entity1.Entity2Items.Entity3Items.Entity3Name"
Anyway, I was just trying to see if there would be any solution out-of-the-box that would require minimal code changes.
Thank you.

Couchbase + Spring Data: Get only field instead of whole entity using #Query

I'm still relatively new in Couchbase with N1QL and Spring Data. I try to retrieve a list of all distinct fields. I don't want to retrieve the whole entity since I expect the result to be rather large.
My documents look like
{
"fieldname": "something",
...
}
My repository class is
#Repository
#N1qlPrimaryIndexed
public interface MyRepository extends CouchbasePagingAndSortingRepository<MyEntity, String> {
#Query("select distinct fieldname from mybucket")
Object findAllDistinctFields();
}
But I always get an Exception 'Query returning a primitive type are expected to return exactly 1 result'
org.springframework.data.couchbase.core.CouchbaseQueryExecutionException: Query returning a primitive type are expected to return exactly 1 result, got 100014
at org.springframework.data.couchbase.repository.query.AbstractN1qlBasedQuery.executeSingleProjection(AbstractN1qlBasedQuery.java:193)
at org.springframework.data.couchbase.repository.query.AbstractN1qlBasedQuery.executeDependingOnType(AbstractN1qlBasedQuery.java:134)
at org.springframework.data.couchbase.repository.query.AbstractN1qlBasedQuery.execute(AbstractN1qlBasedQuery.java:93)
Is there a way to return only all the fields and not whole entities? If I try the query 'select distinct sku from backinstock;' in Couchbase's GUI it works fine!
BTW I'm working with spring-data-couchbase 2.1.5 and Couchbase 4.5.1-2844.
There is projections support which can fetch specific fields of an entity in Spring data Ingalls 2.2.0-RC1 release. The reference documentation is here http://docs.spring.io/spring-data/couchbase/docs/2.2.0.RC1/reference/html/#_dto_projections

Spring LDAP JPA - Limit number of result

I'm using spring-ldap 2.0.4 and spring-data-jpa 1.9.0.
I built a JPA repository like this :
public interface PersonRepo extends LdapRepository<Person> {
Person findByUid (String uid);
#Query("(&(attribute=*{0}*)(attribute2=X)(attribute3=Y))")
List<Person> findByAttributeContains(String attribute);
}
So far everything is fine. I could write methods that fill my needs thanks to query methods.
For some queries i had to use #Query annotation because they were many and operator.
But i would like to limit the number of result to return from my second query method.
I know there is there is the Top and First keywords to define query methods in spring JPA. But I didn't manage to get it work. Plus I want to use multiple and operator in the method.
Thanks
I managed to limit the number of result using XML LDAP configuration.
<ldap:ldap-template context-source-ref="contextSource" count-limit="100"/>

Is it possible to use SpringData MongoDB repository to execute an arbitrary query, with pagination?

We have a use case where a user can pass in arbitrary search criteria for a collection, and wants the output paged. Using Spring Data repositories, this is quite simple if we know ahead of time what attributes they may be searching on by simple extending MongoRepository, and declaring a:
Page<Thing> findByFooAndBarAndBaz(Type foo, Type bar, Type baz, Pageable page)
However, if we generate the query ourselves either using the fluent interface or constructing a mongo string and wrapping it in a BasicQuery class, I can not find a way to get that into a repository instance. There is no:
Page<Thing> findByQuery(Query q, Pageable page)
functionality that I have been able to see.
Nor can I see how to hook into the MongoTemplate querying functionality with the Page abstraction.
I'm hoping I don't have to roll my own paging (calculating skip and limit parameters, which I guess is not hard) and call into the template directly, but I guess I can if that's the best choice.
I don't think this can be done in the way I'd hoped, but I've come up with a workaround. As background, we put all our methods to do data access in a DAO, and some delegate to the repository, some to the template.
Wrote a DAO method which takes our arbitrary filter string (which I have a utility that converts it to standard mongo JSON query syntax.
Wrap that in a BasicQuery to get a "countQuery".
Use that countQuery to get a total count of records using MongoTemplate#count(Query, Class)
Append my paging criteria to create a "pageQuery" using Query#with(Pageable)
Run the pageQuery with MongoTemplate#find(Query, Pageable)
Get the List<T> result from that, the Pageable that was used for the query and the count returned from the countQuery run, and construct a new PageImp to return to the caller.
Basically, this (DocDbDomain is a test domain class for testing out document db stuff):
Query countQuery = new BasicQuery(toMongoQueryString(filterString));
Query pageQuery = countQuery.with(pageRequest);
long total = template.count(countQuery, DocDbDomain.class);
List<DocDbDomain> content = template.find(pageQuery, DocDbDomain.class);
return new PageImpl<DocDbDomain>(content, pageRequest, total);
You can use the #Query annotation to execute an arbitrary query through a repository method:
interface PersonRepository extends Repository<Person, Long> {
#Query("{ 'firstname' : ?0 }")
Page<Person> findByCustomQuery(String firstname, Pageable pageable);
}
Generally speaking, #Query can contain any JSON query you can execute via the shell but with the ?0 kind of syntax to replace parameter values. You can find more information on the basic mechanism in the reference documentation.
In case you can't express your query within the #Query-Annotation, you can use the Spring Repository PageableExecutionUtils for your custom queries.
For example like this:
#Override
public Page<XXX> findSophisticatedXXX(/* params, ... */ #NotNull Pageable pageable) {
Query query = query(
where("...")
// ... sophisticated query ...
).with(pageable);
List<XXX> list = mongoOperations.find(query, XXX.class);
return PageableExecutionUtils.getPage(list, pageable,
() -> mongoOperations.count((Query.of(query).limit(-1).skip(-1), XXX.class));
}
Like in the original Spring Data Repository, the PageableExecutionUtils will do a separated count request and wrap it into a nice Page for you.
Here you can see that spring is doing the same.

Adding OrderBy clause to a named query

Is it possible to add an OrderBy clause in to JPA Named query at runtime?
Named queries are processed by the persistence provider when the EntityManagerFactory is created. You can't change/modify/append anything about a named query dynamically at runtime.
If you are using JPA 2.0 and you need a way to do high-performance dynamic queries at runtime, you should look into the Criteria API.
From Java EE 5 Documentation : "Is used to specify a named query in the Java Persistence query language, which is a static query expressed in metadata. Query names are scoped to the persistence unit".
As it says, it is static & you can't change the query at runtime.
Rather use custom query or if ordering element is fixed then you can use annotation;
Field:
#OrderBy(value="nickname")
List<Person> friends;
Method:
#OrderBy("nickname ASC")
public List<Person> getFriends() {...};
Even if you can't add order-by clause to your named query, you can provide a parametric orderBy. The following is a perfectly valid query:
SELECT u FROM User u ORDER BY :orderBy
And you are going to change ordering with something like:
query.setParameter("orderBy", "id");