How do I dynamically create "OR" predicates if I have a List<List<String>>
I am using query dsl and spring data.
QOrder order = QOrder.order;
JPQLQuery<Order> query = from(order);
query.where(order.status.eq("ready"));
List<List<String>> filterTypes;
This is what I am trying to do:
for(List<String> types : filterTypes) {
query.where(order.type.in(types));
}
So the result should be something like
select * from order o where o.status='ready' and (o.type in(t1,t2) or o.type in(t3,t4))
To directly answer your question: assuming you're using a relatively modern version of QueryDSL, you should be able to use a BooleanBuilder:
QOrder order = QOrder.order;
SQLQuery<Order> query = from(order);
query.where(order.status.eq("ready"));
// Example data
List<List<String>> filterTypes = ImmutableList.of(
ImmutableList.of("t1", "t2"),
ImmutableList.of("t3", "t4"));
BooleanBuilder builder = new BooleanBuilder();
for (List<String> types : filterTypes) {
builder.or(order.type.in(types));
}
query.where(builder);
Backing up, assuming your actual application data model is similar to the example you provided, this:
o.type in (t1,t2) or o.type in (t3,t4)
Is equivalent to:
o.type in (t1,t2,t3,t4)
You could translate your List<List<String>> into List<String> and do your type query update once:
QOrder order = QOrder.order;
SQLQuery<Order> query = from(order);
query.where(order.status.eq("ready"));
// Example data
List<List<String>> filterTypes = ImmutableList.of(
ImmutableList.of("t1", "t2"),
ImmutableList.of("t3", "t4"));
List<String> flatFilterTypes = filterTypes.stream().flatMap(List::stream).collect(Collectors.toList());
query.where(order.type.in(flatFilterTypes));
I suspect that your database's query optimizer would do the same thing for either query (you'd have to check a query execution plan to be sure), but it'd probably be more clear what's going on if you did simplified the query on the Java side rather than relying on the database query optimizer.
Related
Using Neo4j driver for java, i want to send a search query to the database such as:
MATCH(a:`Label`{Property:"NODE_PROPERTY"})
RETURN *
First i create a session and the i use the run methods of the driver to run a query:
Result run = session.run(query);
run variable contains a list of Records. My question is how can i consume the records so that i can convert them to java objects? I tried to get the values of the results but since they're not iterable, it's not possible to get them one by one.
Result implements Iterator<Record>, so there is a bunch of ways of consuming it, e.g.:
While loop (Java 6 style):
Result result = session.run(query);
List<MyPojo> myList = new ArrayList<>();
while(result.hasNext()) {
Record r = result.next();
myList.add(mapToMyPojo(r));
}
Stream (Java 8+ style):
Result result = session.run(query);
List<MyPojo> myList = result.stream()
.map(record -> mapToMyPojo(record))
.collect(Collectors.toList());
Using Result.list(Function<Record,T> mapFunction):
Result result = session.run(query);
List<MyPojo> myList = result.list(r -> mapToMyPojo(r));
Mapping to a Java object is pretty stright-forward:
public MyPojo mapToMyPojo(Record record) {
MyPojo pojo = new MyPojo();
pojo.setProperty(record.get("Property").asString());
// ...
return pojo;
}
Although instead of mapping results manually, you might want to use neo4j-ogm
I am trying to build a query like this:
List<Integer> ids = ...
String query = DSL.select(TABLE.SOMETHING).from(TABLE).where(TABLE.ID.in(ids)).
getSQL();
But I am not able to get the generated query with the values, just the placeholders.
I tried DSL.inline(ids) but it doesnt' work.
How can I do this?
I am using jOOQ 3.4.2.
Thanks for the help.
UPDATE:
Seems I can do this with:
Configuration configuration = new DefaultConfiguration();
configuration.set(SQLDialect.DERBY);
Settings settings = new Settings()
.withStatementType(StatementType.STATIC_STATEMENT);
configuration.set(settings);
DSLContext create = DSL.using(configuration);
String query = create.select(TABLE.SOMETHING).from(TABLE).where(TABLE.ID.in(ids)).getSQL();
If someone can confirm that is th right way, thanks.
You cannot inline a list with jOOQ's DSL.inline() because if you could, the semantics of such a value would be that of a list/array literal in the database, not of a list of individual values.
Correct way to use DSL.inline():
Here's one correct way to pass a list of inlined values to the Field.in(Field<?>...):
List<Integer> ids = ...
String query = DSL.using(configuration) // Use a Configuration or at least a SQLDialect!
.select(TABLE.SOMETHING)
.from(TABLE)
.where(TABLE.ID.in(ids.stream().map(DSL::inline).collect(toList())))
.getSQL();
Inline all bind values on a per-getSQL() basis:
Use Query.getSQL(ParamType)
List<Integer> ids = ...
String query = DSL.using(configuration)
.select(TABLE.SOMETHING)
.from(TABLE)
.where(TABLE.ID.in(ids))
.getSQL(ParamType.INLINED);
Inline all bind values on a per-Configuration basis:
The solution you've mentioned in your question edit is valid as well, of course:
List<Integer> ids = ...
Configuration configuration = new DefaultConfiguration();
configuration.set(new Settings().withStatementType(StatementType.STATIC_STATEMENT));
String query = DSL.using(configuration)
.select(TABLE.SOMETHING)
.from(TABLE)
.where(TABLE.ID.in(ids))
.getSQL();
I need to implement a Query operation on DynamoDB. Right now I'm doing it by giving the HashKey and then filtering out the results according to my conditions on non-key attributes.
This is what I'm doing :
MusicData hashKey = new MusicData();
hashKey.setID(singer);
DynamoDBQueryExpression<MusicData> queryExpression = new DynamoDBQueryExpression<MusicData>().withHashKeyValues(hashKey);
List<MusicData> queryResult = mapper.query(MusicData.class, queryExpression);
for (MusicData musicData : queryResult) {
if( my condtions ) {
do something;
}
}
What I'm trying to do is to be able to do something like this :
MusicData hashKey = new MusicData();
hashKey.setID(singer);
hashKey.setAlbum(sampleAlbum);
hashKey.setSinger(duration);
DynamoDBQueryExpression<MusicData> queryExpression = new DynamoDBQueryExpression<MusicData>().withHashKeyValues(hashKey);
List<MusicData> queryResult = mapper.query(MusicData.class, queryExpression);
for (MusicData musicData : queryResult) {
if( my condtions ) {
do something;
}
}
And get results already filtered out. Is there a way to do this in DynamoDB?
Yes, you can ask DynamoDB to perform filtering of queries before it returns results. However, you will still incur the 'cost' of reading these items even though they are not returned to your client. This is still a good practice as it will eliminate unnecessary transfer of items over the network.
To do this you will call additional methods on your DynamoDBQueryExpression object, specifically withFilterExpression and addExpressionAttributeNamesEntry / addExpressionAttributeValuesEntry to complete the expression.
Without the specific example of what type of conditions you want to apply it is hard to give an example, but depending on how simple your condition you want to apply is you could contain it in just the withFilterExpression method.
DynamoDBQueryExpression<MusicData> queryExpression = new DynamoDBQueryExpression<MusicData>().withHashKeyValues(hashKey).withFilterExpression("foo > 10");
I want to execute a query in java where path and _id are two fields of the mongo document.
I want to get results list where these two fields are equal in the document.
I have tried using the following query.But could not retrieve the results properly.Received empty list which is not the case.
List<Metadata> MetadataList= ops.find(new Query(Criteria.where("path").is("_id")), Metadata.class);
How to get results where two field values are equal in mongo.
What you are looking for is the $where operator in MongoDB. Standard query operations do not compare the values of one field against another. In order to do this, you need to employ the JavaScript evaluation server side which can actually compare the two field values:
BasicQuery query = new BasicQuery(
new BasicDBObject("$where", "return this._id == this.path")
);
<Metadata> MetadataList = ops.find(query, Metadata.class);
Or you can do the same thing with native operators through the $redact pipeline stage available to the aggregation framework.
Pretty sure there is no $redact support in spring mongo as yet, but you can wrap the aggregation operation with a class to do so:
public class CustomAggregationOperation implements AggregationOperation {
private DBObject operation;
public CustomAggregattionOperation (DBObject operation) {
this.operation = operation;
}
#Override
public DBObject toDBObject(AggregationOperationContext context) {
return context.getMappedObject(operation);
}
}
And use it like this:
Aggregation aggregation = newAggregation(
new CustomAggregationOperation(
new BasicDBObject(
"$redact",
new BasicDBObject("$cond",
new BasicDBObject()
.append("if", new BasicDBObject(
"$eq", Arrays.asList("$_id", "$path")
))
.append("then", "$$KEEP")
.append("else", "$$PRUNE")
)
)
)
);
AggregationResults<Metadata> results = ops.aggregate(
(TypedAggregation<Metadata>) aggregation, Metadata.class);
So basic MongoDB query operations do not compare field values against each other. To do this you need to follow one of the methods here.
You can use BasicDBObject to add condition.
Try something
BasicDBObject query = new BasicDBObject("path", new BasicDBObject("$eq", "_id");
collection.find(query);
Please refer the below link for more information
http://mongodb.github.io/mongo-java-driver/2.13/getting-started/quick-tour/
I am running map reduce in mongoDB with morphia, this is my java code
String map = "function() { emit(this.id,this.cal.charge)}";
String reduce = "function(k, v) {var i, sum = 0;for (i in v) {sum += v[i];}return sum;}";
MapreduceResults<Results> mrRes = ds.mapReduce(MapreduceType.MERGE,ds.createQuery(MyTable.class).field("id").equal(5),map,reduce,null,null,Re.class);
This work fine and put results to 'Re' collection, but how can i get result as objects or list without inserting to a new Collection ?
Thanks
I couldn't commented this because it violates the length limit.
If it is not going to be too much of a fuss, you can do it by using java driver directly without using the morphia interface. just get the mongo object from morphia datastore and use java driver's map reduce command; it is something like this:
DBObject queryObject = new BasicDBObject("id", 5);
DBCollection collection = ds.getCollection(MyTable.class);
MapReduceCommand mrc = new MapReduceCommand(collection, // collection to do map-reduce on
map, // map function
reduce, // reduce function
null, // output collection for the result
MapReduceCommand.OutputType.INLINE, // output result type
queryObject); // query to use in map reduce function
btw morphia has a newer version in github https://github.com/jmkgreen/morphia maybe you wanna check that out too. I saw that the newer version also don't support inline operation on map-reduce.