Select distinct values from MongoDB array using Java - java

I want to get a list of unique values from a List field called categories.
I am using Java and MongoDB. Most of the examples and docs I can see seem to suggest I need to do something like what I have below:
public static List<String> listCategories(String input) {
Datastore ds = Dao.instance().getDatabase();
BasicDBObject dbObject=new BasicDBObject("categories", input);
DBCollection dBCollection = ds.getCollection(Product.class);
List<String> categories = dBCollection.distinct("categories",dbObject);
return categories;
}
However when I test it using this code:
#Test
public void testListCategories(){
List<String> categories = Product.listCategories("S");
Assert.assertTrue(categories.size() > 0);
}
The test fails even though I know there are categories that start with S (have also tried a few others just to be sure).
Is it even possible to do this, if so do you have any pointers?
Thanks

BasicDBObject dbObject=new BasicDBObject();
dbObject.append(categories", input);
Now it will be like a where condition,
DBCollection dBCollection = ds.getCollection(Product.class);
List<String> categories = dBCollection.distinct("categories",dbObject);

The test fails even though I know there are categories that start with S (have also tried a few others just to be sure).
You need to perform a regex match using the $regex operator on the categories field to find distinct categories that start with a particular input.
Your updated code should look like:
BasicDBObject like = new BasicDBObject("$regex",
java.util.regex.Pattern.compile("^"+input,
Pattern.CASE_INSENSITIVE));
BasicDBObject query = new BasicDBObject("categories", like);
List<String> categories = dBCollection.distinct("categories",query);

Related

Query dsl - Looping through a list to create OR predicates

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.

jOOQ - query builder with IN predicate

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();

Finding distinct from collections in mongodb

Our previous implementation for finding distinct elements from a collection used to be :
List<String> names = mongoClient.getDB(dbName).getCollection(collectionName).distinct(NAME_KEY);
Trying to upgrade this into the current implementation with mongo 3.3.0+ as tried is :
List<String> names = mongoClient.getDatabase(dbName)
.getCollection(collectionName, TDocType.class)
.distinct(NAME_KEY, String.class); // compile error - required Class<TResult>
Have also given a try to
.distinct(NAME_KEY, TDocType.class) // doesn't work
What shall be the target type of the iterable in this case?
Edit - The question is not a duplicate of Get distinct records values since the implementation has changed over the upgrade of mongodb-java-driver.
You can try something like this.
DistinctIterable<String> iterable = mongoClient.getDatabase(dbName).
.getCollection(collectionName, TDocType.class).distinct(NAME_KEY, String.class);
MongoCursor<String> cursor = iterable.iterator();
List<String> list = new ArrayList<>();
while (cursor.hasNext()) {
list.add(cursor.next());
}

Get the single value from mongodb Collection using java code

I want to get only the single value from Mongodb Collection.
Currently i am getting the Document which is FindIterable.
CustomObject obj = db
.getCollection("Collection",CustomObject.class)
.find(and(eq("field1", new BigDecimal(10409)),eq("field2", new BigDecimal(1))));
But , i dont want any result in to the Object or List.like in Oracle we use Query to get single Object :
SELECT NAME FROM EMPLOYEE_TABLE WHERE ID=10 AND DEPT_ID=23;
This query gives us the Single name of the employee on the basis of filter conditions, and we get the output in String type Object.
Same i want to get from the mongodb , i don't want to use any bean to populate the data. i want only single String object as result.
You may use the find method on Collection, passing a query and fields to retrieve:
BasicDBObject fields = new BasicDBObject();
fields.put("name", true);
BasicDBObject query = new BasicDBObject();
query.put("id", 13);
query.put("dept_id", 23);
DBCursor find = mongoTemplate.getCollection("example").find(query, select);
List<DBObject> list = find.toArray();
List<String> names = list.stream().map(o -> String.valueOf(o.get("name"))).collect(Collectors.toList());
If you are seeking for a function that returns just one document, then you can use findOne.

Mongo spring query where two fields are equal

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/

Categories

Resources