Spring Mongodb aggregation doesn't work with DBRef - spring

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.

Related

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

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

Elastic search with Spring Data for Reactive Repository - Deleting based on nested attributes

I am using Spring data for Elastic Search and am using the ReactiveCrudRepository for stuff like finding and deleting. I noticed that with attributes that are in root and are simple objects, the deletion works (deleteByAttributeName). However if I have nested objects then it does not work.
Here's my entities
Book
#Data
#TypeAlias("book")
#Document(indexName = "book")
public class EsBook{
#Field(type = FieldType.Long)
private Long id;
#Field(type = FieldType.Nested)
private EsStats stats;
#Field(type = FieldType.Date, format = DateFormat.date)
private LocalDate publishDate;
}
Stats
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
public class EsStats{
#Field(type = FieldType.Double)
private Double averageRating;
#Field(type = FieldType.Integer)
private Double totalRatings;
#Field(type = FieldType.Keyword)
private String category; //this can be null
}
Here is what I have tried and is working and not working
I used ReactiveCrudRepository to delete documents in index. For all the regular fields on Book Level like id or with id and publishDate deletion works perfectly. As soon as I use embedded object like Stats, it stops working. I see the documents and the stats that I am sending match atleast visually but never finds or deletes them.
I tried to use EqualsAndHashcode in the Stats assuming maybe iternally somehow does not consider equal for some reason. I also tried changing double data type to int, because on looking at the elastic search document, I see that average review if whole number like 3 is save as 3 but when we send it from Java, i see in the debug 3 being shown as 3.0, so I was doubting if that is the case, but does not seem so. Even changing the datatype to int deletion does not work.
public interface ReactiveBookRepository extends ReactiveCrudRepository<EsBook, String> {
Mono<Void> deleteById(long id); //working
Mono<Void> deleteByIdAndPublishDate(long id, LocalDate publishDate); //Nor working
Mono<Void> deleteByIdAndStats(long id, LocalDate startDate);
}
Any help will be appreciated
Have you verified that your Elasticsearch index mapping matches your Spring Data annotations?
Verify that the index mapping defines the stats field as a nested field type.
If not, then try changing your Spring annotation to:
#Field(type = FieldType.Object)
private EsStats stats;

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.

Fetch child entities when finding by a normal field in Spring Data JPA

I am using Spring Data JpaRepository to find List of entities matching a particular field. Consider the following code snippet:
Entity:
#Entity
#Table(name = "master")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Master implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
#Column(name = "id", nullable = false)
private Long Id;
#NotNull
#Column(name = "user_id", nullable = false)
private String userId;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="id", referencedColumnName="id", insertable=false, updatable=false)
private Details Details;
Spring Data Custom JpaRepository:
public interface MasterRepository extends JpaRepository<Master,Long> {
List<Master> findMasterByUserId(String userId);
}
When i am using findBookingMasterByUserId repository method to find all records with specific user id, I am getting the List of Master entity but I am not getting the Details entity that has id as foreign key in it.
However, I get all the dependent entities when I use out of the box findAll method of JpaRepository but with custom findMasterByUserId repository method, child entities are not being fetched eagerly.
Any type of help would be highly appreciated. Thanks!
You can use #EntityGraph in your repo to eagerly get associated data:
#EntityGraph(attributePaths = {"details"})
List<Master> findBookingMasterByUserId(String userId);
P.S. Don't forget to change 'Details' field to details;
Your entity name is "Master" not "booking_master".
Change your method to:
List<Master> findByUserId(String userId);
Refer to below spring docs for more information on query creation mechanism for JPA.
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/
Alternatively,
#Query("SELECT m FROM Master m WHERE m.userId = :userId")
List<Master> findByUserId(#Param("userId") String userId);
The query generation from the method name is a query generation strategy where the invoked query is derived from the name of the query method.
We can create query methods that use this strategy by following these rules:
The name of our query method must start with one of the following
prefixes: find…By, read…By, query…By, count…By, and get…By.
If we want to limit the number of returned query results, we can add
the First or the Top keyword before the first By word. If we want to
get more than one result, we have to append the optional numeric
value to the First and the Top keywords. For example, findTopBy,
findTop1By, findFirstBy, and findFirst1By all return the first entity
that matches with the specified search criteria.
If we want to select unique results, we have to add the Distinct
keyword before the first By word. For example, findTitleDistinctBy or
findDistinctTitleBy means that we want to select all unique titles
that are found from the database.
We must add the search criteria of our query method after the first
By word. We can specify the search criteria by combining property
expressions with the supported keywords.
If our query method specifies x search conditions, we must add x
method parameters to it. In other words, the number of method
parameters must be equal than the number of search conditions. Also,
the method parameters must be given in the same order than the search
conditions.

NamedEntityGraph Returns All Columns and Objects

I am trying to utilize a NamedEntityGraph to limit the return data for specific queries. Mainly I do not want to return full object details when listing the object. A very simple class example is below.
#Entity
#Table(name="playerreport",schema="dbo")
#NamedEntityGraphs({
#NamedEntityGraph(name = "report.simple",
attributeNodes =
{#NamedAttributeNode(value="intId")
}
)
})
public class PlayerReportEntity {
#Id
#Column(name="intid",columnDefinition="uniqueidentifier")
private String intId;
#Column(name="plyid",columnDefinition="uniqueidentifier")
#Basic(fetch=FetchType.LAZY)
private String plyId;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "plyid", insertable=false,updatable=false)
private PlayerEntity player;
No matter what I do to plyId and player are always returned. Is there any way to only return the requested columns (intId) ?
As for the collection Hibernate does not do the join for the player object but it still returns player as null. So that part is working to an extent.
I am using a JPARepository below to generate Crud Statements for me
public interface PlayerReportRepository extends JpaRepository<PlayerReportEntity, String> {
#EntityGraph(value="report.simple")
List<PlayerIntelEntity> findByPlyId(#Param(value = "playerId") String playerId);
#Override
#EntityGraph(value="report.simple")
public PlayerIntelEntity findOne(String id);
}
A chunk of text from here - "Hence it seems that the #NamedEntityGraph only affects fields that are Collections, but fields that are not a Collection are always loaded." from JIRA
Please use the Example 47 on this page and use repositories accordingly.
In essence, hibernate is right now loading all the feilds in the class and for collections it will work if you follow the example stated above.
Thanks.

Resources