Spring data repository: findBySomething***In*** and result order - spring-data

Is there any way to make those functions getting the results ordered like in the order of the list (or collection) given to the function for the in lookup?
Simple example of a spring data repository interface function:
public void List<Entity> findByColorIn(List<String> colors)
Now im creating a string list with the order "green", "blue", "cyan" and calling this function.
Assuming there is a unique match of all these three colors:
how can i make the function returning the results by the given order of property in the given list? In this example:
Entity[id=32, color="green"]
Entity[id=11, color="blue"]
Entity[id=22, color="cyan"]
Not sure whats the default order in this case but im assuming the id...

spring data has two variants with api how you can use standard order based on property.
1 variant :
public void List<Entity> findByColorInOrderByColorDesc(List<String> colors)
public void List<Entity> findByColorInOrderByColorAsc(List<String> colors)
2 variant
Sort sort= new Sort(Sort.Direction.ASC/DESC,"color");
public void List<Entity> findByColorIn(List<String> colors , Sort sort)
if you want to use custom order for case , sort order :
color="green" , color="blue", color="cyan"
you need use variant 2 with custom Sort implementation based on your sort logic.
Also you can get not sorted result and sort it in server side if you have small result set . spring data might return stream , so you can do something like :
findByColorIn(colors).stream().sorted(comparator....).collect(Collectors.toList());

In my case I just sorted it by hand since I wasn't dealing with a very long array of items.
First you retrieve the elements from database in whatever order they come, then compare with your color list and sort it accordingly.

If you want to retrieve Objects and keep order of ids in the same time, you can use this:
#Query("select p from Colour p where p.id in :colors order by FIELD(p.id,:colors)")
List<Color> getColorsByIdsOrdered(List<String> colors);
P.S.: In case IntelliJ or other platform marks ,,FIELD'' with RED, just ignore it. Application will compile and request will work fine.

Related

RDF4J not filtering TreeModel in expected way

I have a TTL with something like
ex:isDataProperty rdf:type owl:DatatypeProperty .
ex:Article a owl:Class ;
owl:hasKey ( ex:isDataProperty ) .
And when I load the model with RDF4J (as a TreeModel) then try to filter to extract the properties annotated with haskey fails (just returns empty list result)
Some samples that return data:
val dataProperties = model.filter(null, RDF.TYPE, OWL.DATATYPEPROPERTY).subjects().asScala
val classes = model.filter(null, RDF.TYPE, OWL.CLASS).subjects().asScala
The sample I want, that doesn't return data:
val propertiesWithKeys = model.filter(null, RDF.PROPERTY, OWL.HASKEY).subjects().asScala
I have tried a few variations of the previous one using RDF.TYPE or RDF.Value. (instead of RDF.PROPERTY)
The thing you're after is any subject that has a owl:hasKey property, regardless of value. So both the subject and the object are wildcards, you just want to filter by property name. The way to do that is like this:
model.filter(null, OWL.HASKEY, null)
Now, furthermore you say that you want to know the properties that have been used as annotation using this owl:hasKey property. In your example, that would be ex:isDataProperty. Note that in your model, this is not the subject of the owl:hasKey relation - it's in the object values:
model.filter(null, OWL.HASKEY, null).objects()
To further complicate matters, the object values in your example are not simply single values. Instead, each class is annotated using a list of properties, so the object value is a list object (a.k.a. an RDF Collection). To process this list, there are some utility methods provided by the Models and RDFCollections classes.
For each of the objects you can do this to get the actual list of values:
RDFCollections.asValues(model, objectNode, new ArrayList<Value>())
(where objectNode is one of the values that .objects() returned)
Edit since objects() returns objects of type Value and RDFCollections expects a Resource, you'll either have to do a cast, or if you want to do all of this in a fluent way, you can use Models.objectResources instead. The whole thing then becomes:
Models.objectResources(model.filter(null, OWL.HASKEY, null))
.asScala.map(o => RDFCollections.asValues(model, o, new ArrayList[Value]()));
(I may have the Scala-specific bits of this wrong, but you get the gist hopefully)
For more information on how to work with the rdf4j Model API and with RDF Collections, see the rdf4j documentation.

Scala: filter Seq[] (make a diff)

I have two Seq[_]es in my Play application.
Now I want to make a diff of those and get as a result an Seq with all items which are not in the other one.
I tried to use .filter() but I don't know if thats a good way
How can I achieve this?
thanks in advance
Update ... PseudoCode Example
I have two Seq[]
1.) Seq[CarsInStock]
Attributes[ID, Brand, Color]
[{1,Porsche,Red},{3,Mercedes,Blue}]
2.) Seq[CarsAfterSale]
Attributes[ID, Brand, Color,Doors,Windows]
[{1,Porsche,Red,4,10}]
Now I wan't to make a diff between the two seq[]. As result I want to get the Object {3,Mercedes,Blue}] because it is in stock, but after sales I have to know which ones I have to remove from stock.
I want to recognize the difference by the ID of the elements
You can simply filter out all cars whose id exist in the other Seq.
stock.filterNot(c => afterSale.exists(_.id == c.id))
Unless you expect the second Seq to be short, you can probably optimize it by creating a Set of ids:
val afterSaleIds = afterSale.iterator.map(_.id).toSet
stock.filterNot(c => afterSaleIds.contains(c.id))

Gremlin - how do you merge vertices to combine their properties without listing the properties explicitly?

Background: I'm trying to implement a time-series versioned DB using this approach, using gremlin (tinkerpop v3).
I want to get the latest state node (in red) for a given identity node (in blue) (linked by a 'state' edge which contains a timestamp range), but I want to return a single aggregated object which contains the id (cid) from the identity node and all the properties from the state node, but I don't want to have to list them explicitly.
(8640000000000000 is my way of indicating no 'to' date - i.e. the edge is current - slightly different from the image shown).
I've got this far:
:> g.V().hasLabel('product').
as('cid').
outE('state').
has('to', 8640000000000000).
inV().
as('name').
as('price').
select('cid', 'name','price').
by('cid').
by('name').
by('price')
=>{cid=1, name="Cheese", price=2.50}
=>{cid=2, name="Ham", price=5.00}
but as you can see I have to list out the properties of the 'state' node - in the example above the name and price properties of a product. But this will apply to any domain object so I don't want to have to list the properties all the time. I could run a query before this to get the properties but I don't think I should need to run 2 queries, and have the overhead of 2 round trips. I've looked at 'aggregate', 'union', 'fold' etc but nothing seems to do this.
Any ideas?
===================
Edit:
Based on Daniel's answer (which doesn't quite do what I want ATM) I'm going to use his example graph. In the 'modernGraph' people-create->software. If I run:
> g.V().hasLabel('person').valueMap()
==>[name:[marko], age:[29]]
==>[name:[vadas], age:[27]]
==>[name:[josh], age:[32]]
==>[name:[peter], age:[35]]
then the results are a list of entities's with the properties. What I want is, on the assumption that a person can only create one piece of software ever (although hopefully we will see how this could be opened up later for lists of software created), to include the created software 'language' property into the returned entity to get:
> <run some query here>
==>[name:[marko], age:[29], lang:[java]]
==>[name:[vadas], age:[27], lang:[java]]
==>[name:[josh], age:[32], lang:[java]]
==>[name:[peter], age:[35], lang:[java]]
At the moment the best suggestion so far comes up with the following:
> g.V().hasLabel('person').union(identity(), out("created")).valueMap().unfold().group().by {it.getKey()}.by {it.getValue()}
==>[name:[marko, lop, lop, lop, vadas, josh, ripple, peter], lang:[java, java, java, java], age:[29, 27, 32, 35]]
I hope that's clearer. If not please let me know.
Since you didn't provide I sample graph, I'll use TinkerPop's toy graph to show how it's done.
Assume you want to merge marko and lop:
gremlin> g = TinkerFactory.createModern().traversal()
==>graphtraversalsource[tinkergraph[vertices:6 edges:6], standard]
gremlin> g.V(1).valueMap()
==>[name:[marko],age:[29]]
gremlin> g.V(1).out("created").valueMap()
==>[name:[lop],lang:[java]]
Note, that there are two name properties and in theory you won't be able to predict which name makes it into your merged result; however that doesn't seem to be an issue in your graph.
Get the properties for both vertices:
gremlin> g.V(1).union(identity(), out("created")).valueMap()
==>[name:[marko],age:[29]]
==>[name:[lop],lang:[java]]
Merge them:
gremlin> g.V(1).union(identity(), out("created")).valueMap().
unfold().group().by(select(keys)).by(select(values))
==>[name:[lop],lang:[java],age:[29]]
UPDATE
Thank you for the added sample output. That makes it a lot easier to come up with a solution (although I think your output contains errors; vadas didn't create anything).
gremlin> g.V().hasLabel("person").
filter(outE("created")).map(
union(valueMap(),
outE("created").limit(1).inV().valueMap("lang")).
unfold().group().by {it.getKey()}.by {it.getValue()})
==>[name:[marko], lang:[java], age:[29]]
==>[name:[josh], lang:[java], age:[32]]
==>[name:[peter], lang:[java], age:[35]]
Merging edge and vertex properties using gremlin java DSL:
g.V().has('User', 'id', userDbId).outE(Edges.TWEETS)
.union(__.identity().valueMap(), __.inV().valueMap())
.unfold().group().by(__.select(Column.keys)).by(__.select(Column.values))
.map(v -> converter.toTweet((Map) v.get())).toList();
Thanks for the answer by Daniel Kuppitz and youhans it has given me a basic idea on the solution of the issue. But later I found out that the solution is not working for multiple rows. It is required to have local step for handling multiple rows. The modified gremlin query will look like:
g.V()
.local(
__.union(__.valueMap(), __.outE().inV().valueMap())
.unfold().group().by(__.select(Column.keys)).by(__.select(Column.values))
)
This will limit the scope of union and group by to a single row.
If you can work with custom DSL ,create custom DSL with java like this one.
public default GraphTraversal<S, LinkedHashMap> unpackMaps(){
GraphTraversal<S, LinkedHashMap> it = map(x -> {
LinkedHashMap mapSource = (LinkedHashMap) x.get();
LinkedHashMap mapDest = new LinkedHashMap();
mapSource.keySet().stream().forEach(key->{
Object obj = mapSource.get(key);
if (obj instanceof LinkedHashMap) {
LinkedHashMap childMap = (LinkedHashMap) obj;
childMap.keySet().iterator().forEachRemaining( key_child ->
mapDest.put(key_child,childMap.get(key_child)
));
} else
mapDest.put(key,obj);
});
return mapDest;
});
return it;
}
and use it freely like
g.V().as("s")
.valueMap().as("value_map_0")
.select("s").outE("INFO1").inV().valueMap().as("value_map_1")
.select("s").outE("INFO2").inV().valueMap().as("value_map_2")
.select("s").outE("INFO3").inV().valueMap().as("value_map_3")
.select("s").local(__.outE("INFO1").count()).as("value_1")
.select("s").outE("INFO1").inV().value("name").as("value_2")
.project("val_map1","val_map2","val_map3","val1","val2")
.by(__.select("value_map_1"))
.by(__.select("value_map_2"))
.by(__.select("value_1"))
.by(__.select("value_2"))
.unpackMaps()
results to rows with
map1_val1, map1_val2,.... ,map2_va1, map2_val2....,value1, value2
This can handle mix of values and valueMaps in a natural gremlin way.

Spring Data JPQL query: lower case for List param

I use Spring Data REST in my project and I have a #Query tag, as such:
#Query("from Customer c where lower(c.customerId) = lower(:customerId) and lower(c.department.businessUnit.name) = lower(:businessUnit)")
List<Customer> findByCustomerIdAndBusinessUnit(#Param('customerId') String customerId, #Param('businessUnit') String businessUnit)
This works perfectly fine. But, I am not sure how to convert to lowercase when a List is passed in, such as:
#Query("SELECT c FROM Customer c WHERE LOWER(c.customerId) IN LOWER(:customerIds)")
Page<Customer> findByCustomerIdIn(#Param("customerIds") List<String> customerIds, Pageable pageable)
That gives me the following error:
Caused by: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: ( near line 1, column 94 [SELECT c FROM com.myapp.Customer c WHERE LOWER(c.customerId) IN LOWER(:customerIds)]
I understand this happening because Spring Data REST cannot cast a whole list of Strings to lower case (that is not possible in straight up TSQL / PLSQL either!). So, are my options limited to implementing my own repository and build a controller on top of it...? Any better ideas? :)
I believe, you have to transform the list.
Even if you plan to use Spring JPA findByQuery like
List<User> findByCustomerIdInIgnoreCase(List<String> customerIds);
You have to write some code to transform the list into a list of lowercase strings.
If you are looking for converting the list in place, it might not be possible as Strings in Java are immutable
However, if the intention is to do in a single line, with as much less code as possible.
You can do it in Java 8 as below.
List<String> customerIdsLC = customerIds.stream().map(String::toLowerCase).collect(Collectors.toList());
In case, you have a different transformation to do - just provide the right info to map() in the form, Class::method
For e.g.
map(String::toUpperCase) - to convert to Uppercase
map(Long::valueOf) - to convert to Long.

Entity Framework Code First & Search Criteria

So I have a model created in Entity Framework 4 using the CTP4 code first features. This is all working well together.
I am attempting to add an advanced search feature to my application. This "advanced search" feature simply allows the users to enter multiple criteria to search by. For example:
Advanced Product Search
Name
Start Date
End Date
This would allow the user to search by the product name and also limit the results by the dates that they were created.
The problem is that I do not know how many of these fields will be used in any single search. How then can my Entity Framework query be constructed?
I have an example describing how to create a dynamic query for Entity Framework, however this does not seem to work for the POCO classes I created for Code First persistence.
What is the best way for to construct a query when the number of constraints are unknown?
So after some hours of work on this problem (and some help from our friend Google) I have found a workable solution to my problem. I created the following Linq expression extension:
using System;
using System.Linq;
using System.Linq.Expressions;
namespace MyCompany.MyApplication
{
public static class LinqExtensions
{
public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool condition, Expression<Func<TSource, bool>> predicate)
{
if (condition)
return source.Where(predicate);
else
return source;
}
}
}
This extension allows for a Linq query to be created like this:
var products = context.Products.WhereIf(!String.IsNullOrEmpty(name), p => p.Name == name)
.WhereIf(startDate != null, p => p.CreatedDate >= startDate)
.WhereIf(endDate != null, p => p.CreatedDate <= endDate);
This allows each WhereIf statement to only affect the results if it meets the provided condition. The solution seems to work, but I'm always open to new ideas and/or constructive criticism.
John,
Your solution is absolutely awesome! But, just to share, I have been using this method above until I see your ideia.
var items = context.Items.Where(t => t.Title.Contains(keyword) && !String.IsNullOrEmpty(keyword));
So, it seems not to be the best solution for this, but for sure it is a way around.