How to search Mongo for element in a list of string using spring query - spring

So, here's my mongo entity :
public class OfferEntity {
#Id
private String id;
public List<String> categories;
private Type type;
private String title;
}
I use spring org.springframework.data.mongodb.core.query.Criteria and org.springframework.data.mongodb.core.query.Query.
The goal is to find Offers by categories.
Something like :
Query query = new Query();
ArrayList<String> strings = new ArrayList<>();
strings.add("new");
strings.add("old");
strings.add("blue");
strings.add("stolen");
query.addCriteria(Criteria.where("categories").contains(strings);
or
query.addCriteria(Criteria.where("categories").contains("new","old","blue");
I know there's no such thing as "contains" method here, but you get the idea.
I've found some things about "elemMatch" but it does not seems to match my need, as it's meant to find a element in an array with a matching key/value pair.
Any thoughts ? I'm sure it's possible but I'm a bit lost here.

You need to use Criteria.in
Reference
query.addCriteria(Criteria.where("categories").in(Arrays.asList("new","old","blue"));

Related

Spring Mongodb aggregation doesn't work with DBRef

I have an aggregation which doesn't work in mongodb and spring boot. I would be grateful if anyone could help me.
Here is my ExplainDoc class:
#Document(collection = "ExplainDoc")
public class ExplainDoc{
#Id
private String id;
#TextIndexed(weight=3)
private String product_in_brief;
private Product product;
#TextScore
private Float textScore; }
And here is my other class:
#Document(collection = "product")
public class Product{
#Id
private String id;
private String category;
}
What I want to do is to make a text search and find all ExplainDocs which have the given text in their product_in_brief PROVIDED THAT their product has a specific category.
In my search repository, I have an aggregation like the following:
public List<MyAggrResults> searchBriefExplanations(String text, String category){
MatchOperation matchRegion = Aggregation.match(Criteria.where("product.category").is(category));
TextCriteria criteria = TextCriteria.forDefaultLanguage().matchingAny(text);
MatchOperation match = Aggregation.match(criteria);
GroupOperation group = Aggregation.group("product.category").push("$$ROOT").as("myresults").sum("textScore").as("score");
ProjectionOperation project = Aggregation.project("product_in_brief", "product").andExpression("{$meta: \"textScore\"}").as("textScore");
}
The code works now. However, I see it is so expensive to have the product object always as a nested document. How should I change the code if I want to use the product object as #DBRef? When I add the #DBRef, the code doesn't work anymore. I think the reason is that the product.category is not recognized anymore.
I hope somebody can help me.
There is nothing special about DBRef that magically makes it efficient. It is simply a label for the combination of collection name and an id. You still need to use aggregation pipeline to query data that uses DBRef in the same way you'd query if you didn't use DBRef.

How to merge documents from two different indexes using Spring elasticsearch

I am new to elastic-search and i am trying to use spring data elastic search in the application. I have a requirement where in there are two separate indexes and i want to fetch documents from both indexes in one query based on some condition.
I would try to explain it with sample example with the same scenario.
There are two Different classes for individual indexes.
#Document(indexName = "Book", type = "Book")
public class Book {
#Id
private String id;
#Field(type = FieldType.String)
private String bookName;
#Field(type = FieldType.Integer)
private int price;
#Field(type = FieldType.String)
private String authorName;
//Getters and Setters
}
There is one more class Author
#Document(indexName = "Author", type = "Author")
public Class Author{
#Id
private String id;
#Field(type = FieldType.String)
private String authorName;
//Getters and setters
}
So there are two indexes one Book and Other Author.
I want to fetch all the documents where authorName in Book index is equal to authorName in Author index.
Can i get the details from both the index as a single document like merged result.
It would be very helpful if anyone can suggest solution for this usecase.
Thanks a lot for your answer
Your question is not clear. the document in the Author index is not the same document as the document in the Book index. they are two different documents. the closest thing I can think of is querying multiple indices with the same query -
just add multiple indices to the indexCoordinates.of method to search for the same filed\value in both indices.
for example:
elasticsearchTemplae.search(query, returnedObjectClass, indexCoordinates.of("Author", "Book")).
it will return all the search hits with both indices mentioned, which have to fulfill the query conditions, whichever they are.

Spring Data JPA Repository Method Query Cannot Parse Property Name Following OrderBy

My Repository Method Query cannot parse a property name that comes after the OrderBy, but it can if it follows the findBy or findAllBy. The attribute in my entity that is giving me issues is zIndex
Entity Class
#Entity
public class DisplayLayer
{
#Id
#Column(name="DISPLAY_LAYER_ID")
private Long id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="DISPLAY_CONTAINER_ID")
private DisplayContainer displayContainer;
#Column(name="NAME")
private String name;
#Column(name="Z_INDEX")
private Long zIndex;
#Column(name="DESCRIPTION")
private String description;
// Getters & Setters
}
Repository Class
public interface DisplayLayerRepository extends BaseRepository<DisplayLayer, Long>
{
// This one method query works just fine
public List<DisplayLayer> findByZIndex(Long pZIndex);
// These two throw the same error:
// Unable to locate Attribute with the the given name [ZIndex]
//
// public List<DisplayLayer> findAllByOrderByZIndexAsc();
// public List<DisplayLayer> findByDisplayContainerIdOrderByZIndexAsc(Long pDisplayLayerId);
}
So in my first method query, when OrderBy is not used, it correctly parses it as [zIndex], but when it follows the OrderBy for some reason it capitalizes the z: [ZIndex].
Is this a known issue, or am I doing something wrong?
EDIT
What makes me think there might be a bug with the parser is that if I update zIndex to IndexZ in my entity and then change my query methods from OrderByZIndex to OrderByIndexZ everything works.
This solution is not ideal though as the database table is Z_INDEX and that has a specific meaning when talking about layering of graphics.
Yes there is a bug but there is a simple workaround, please try :
displayLayerRepository.findAll(Sort.by(Sort.Order.desc("zIndex").ignoreCase()));
I suppose DisplayLayerRepository extends JpaRepository.
It's similar when you use parameters in your findBy method. Define in your repository empty method :
public List<DisplayLayer> findByDisplayContainerId(Long pDisplayLayerId, Sort sort);
Then you can call it by :
displayLayerRepository.findByDisplayContainerId(1L, Sort.by(Sort.Order.desc("zIndex").ignoreCase()));
You can use JpaSort in query. As in your case, it looks something like this:
Sort sort = JpaSort.unsafe(Sort.Direction.ASC, "zIndex");
List<DisplayLayer> zIndexes = displayLayerRepository.findByZIndex(pZIndex, sort);
and your query in Repository will look like this:
public List<DisplayLayer> findByZIndex(Long pZIndex, Sort sort);

Spring Data JPA Projection with select distinct

I have a database table which holds Metadata for documents. My task now is to get a list with documenttypes. The documenttypes are not unique in the database table but of course I want them to be in my list. The sql is very simple:
SELECT DISTINCT groupname, group_displayorder
FROM t_doc_metadata
ORDER BY group_displayorder;
I have learned that I can use projections to get a subset of fields from my entity DocMetadata. I solved this as follows. My Entity:
#Entity
#Table(name="T_DOC_METADATA")
#Data
public class DocMetadata {
..............
#Column(nullable=false)
private String displayname;
#Column(nullable=false)
private Integer displayorder;
#Column(nullable=false)
private String groupname;
#Column(name="GROUP_DISPLAYORDER",
nullable=false)
private Integer groupDisplayorder;
#Column(name="METADATA_CHANGED_TS",
nullable=false,
columnDefinition="char")
private String metadataChangedTimestamp;
..........
}
My inteface for projection:
public interface GroupnameAndOrder {
String getGroupname();
Integer getGroupDisplayorder();
void setGroupname(String name);
void setGroupDisplayorder(int order);
}
Now I thought I'd be extraordinary clever by adding these lines to my repository:
#Query("select distinct d.groupname, d.groupDisplayorder from DocMetadata d order by d.groupDisplayorder")
public List<GroupnameAndOrder> findSortedGroupnames();
Sadly, when iterating over the result list and calling getGroupname() the result is null.
So I changed the lines in my repository according to the documentation:
public List<GroupnameAndOrder> findBy();
Now I get the groupnames but of course they are not unique now. So it doesn't solve my problem.
Is there any way to receive a ordered list with unique groupnames?
You are trying to be too clever. Instead just write the proper find method and return the GroupnameAndOrder. Spring Data JPA will then only retrieve what is needed for the projection.
Something like this should do the trick.
List<GroupnameAndOrder> findDistinctByOrderByGroupDisplayorder();

Spring data MongoDB adding arrays to an existing document

Say I have the following Collections
public #Data class Customer {
#Id
private String id;
private String firstName;
private String lastName;
#DBRef
private List<Address> addressList= new ArrayList<Address>();
}
and
public #Data class Address {
#Id
private String id;
private String address;
private String type;
private String customerID;
}
And each Customer has multiple addresses, and I have implemented MongoRepository. Saving customer for the First time is working pretty well customerRepo.save(customerObject) and before calling the save I am persisting multiple Address Objects and then setting those to the addressList.
Next time when I am updating the same document and want to add a New set of Address to the existing list it is overwriting the whole addressList array. So basically what I have to do now to set new address like thisexistingCustomerObject.getAddressList().addAll(my new List of address) if there are thousand(or more than thousand) of elements or I am slicing the addressList array the following procedure won't be a good idea. My question is what is the best way to achieve this scenario? say if I don't want to use MongoTemplate. Is it possible Just using the MongoRepository
I don't think you can do it in that way. Previously i had the same situation, and I tried the following
1.org.springframework.core.convert.converter.Converter even I have managed to manipulate the DBObject but functions like $push or $set(wrapping them under key) does not work over there.
2.AbstractMongoEventListener by overriding onBeforeSave but Object manipulation was not taking place during save.
However you can try altering the mentioned
you can try override MongoRepository save method, It would better if someone point to the right direction.
Otherwise for my scenario I had to create Custom repository(To update and delete document) which is working parallel with MongoRepository (for Insert and retrieve data/document), but I believe thats an ugly workaround. There has to be a cleaner way to do it.

Resources