Elasticsearch: search for two different document in a single query - java

I have two entities Merchant and Customer:
public class Merchant{
private UUID id;
private String name;
//... other fields and getters/setters
}
public class Customer{
private UUID id;
private String name;
//... other fields and getters/setters
}
These two entities are sightly different from each-other.
What I'am trying to to do is when I search with the term "John" I want to get both a merchant named "John Market" and a customer called "John Smith".
To achieve this I indexed these entities to a single index.
#Document(indexName = "merchant_customer_index", type = "merchantorcustomer")
public class MerchantOrCustomer {
#Id
private UUID id;
private String name;
private int type;
//...
My query can return both Merchant and Customer:
List<MerchantOrCustomer> result = elasticsearchTemplate.queryForList(nativeSearchQuery, MerchantOrCustomer.class);
I distinguish them programmatic(if(result.get(i).getType() == 0 we received Merchant else Customer)
Then use their id to extract actual object from relational db.
I searched a lot, but couldn't find anything that can help to estimate if it is a good practice. Is it a good practice?
Please, give me a hint if there is a better way.

There doesn't seem to be anything wrong with what you did unless there is some collusion as mentioned by #Ivan in comments.
Here is another possible way to do if you were using elasticTemplate- Spring Data Elasticsearch: Multiple Index with same Document or if you are using queryBuilder - https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/java-search.html

Related

Spring Data Neo4J reference related nodes without overwriting them on save

I'm struggling to write this, so I may have to give an example to help explain the problem I'm experiencing.
Say we have nodes of three types (these nodes may have more relationships of their own, e.g. Product Family, has product manager):
Product
Product Family
Battery
With these relationships
A product can be be in 0 or more families
A product can have 0 or more batteries.
When using spring-data-neo4j and saving a new Product, I wish to include these relatiopnships, such as the batteries they require and the product family they belong to. However if I only supply say an ID rather then a fully populated object, it overwrites this object along with properties and relations accordingly.
This isn't great as it means that I have to end up sending a fully populated object, with all it's relations everytime I wish to save something, and some of these relations may go quite deep.
My domain is as follows:
#Node
public class Product {
#Id
#GeneratedValue(generatorClass = SnowflakeGenerator.class)
private Long productId;
private String name;
#Relationship(type = "REQUIRES_BATTERY", direction = OUTGOING)
private List<Battery> batteryList;
#Relationship(type = "IN_FAMILY", direction = OUTGOING)
private List<ProductFamily> productFamilyList;
}
#Node
public class Battery {
#Id
#GeneratedValue(generatorClass = SnowflakeGenerator.class)
private Long batteryId;
private String name;
}
#Node
public class ProductFamily {
#Id
#GeneratedValue(generatorClass = SnowflakeGenerator.class)
private Long familyId;
private String name;
}
This could very well by from coming from a Relational Database mindset and is a 'limitation' of using Neo4J.
TLDR When persisting somethign in Neo4J using spring-data how can I save just a relationship, rather than a whole related Node.
You can make use of projections in Spring Data Neo4j. (https://docs.spring.io/spring-data/neo4j/docs/current/reference/html/#projections)
This gives you the option to put a "mask" on the object tree, you want to persist (and what should stay untouched).
For example in your case:
interface ProductProjection {
// without defining e.g. String getName() here, SDN would not ever touch this property.
List<BatteryProjection> getBatteryList();
List<ProductFamilyProjection> getProductFamilyList();
}
interface BatteryProjection {
String getName();
}
interface ProductFamilyProjection {
String getName();
}

GraphQL complexity to support update/create worth it?

I´m having a relative complex, hierarchical data model in my Spring application and I need to query it as well as update all entities by letting the client pass the changed entity to the server.
Using GraphQL for querying solves exactly my needs, but to enable update and create capabilities however, I´d need to duplicate all my entities as *.graphqls files won´t support using "type" elements as "input" elements.
For the newly created Input element, I´d need to define a Java Object matching it too.
For queries I´d end up with
Portfolio.java
#Entity
public class Portfolio {
#Id
private int id;
private String name;
....
}
and portfolio.graphqls
type Portfolio {
id: ID!
name: String
....
}
But for updates I´d end up with
Portfolio.java
#Entity
public class Portfolio {
#Id
private int id;
private String name;
....
}
PortfolioInput.java
#Entity
public class PortfolioInput {
#Id
private int id;
private String name;
....
}
portfolio.graphqls
type Portfolio {
id: ID!
name: String
....
}
input PortfolioInput {
id: ID!
name: String
....
}
type PortfolioMutation{
updatePortfolio(input: PortfolioInput):Portfolio
}
My problem with that is, I need to keep 4 entities in sync with their fields now, just because GraphQL does not allow using the type as input and I´d need to do that for many other entities as well.
Is there any other solution, or do people go the extra mile and accept the additional complexity, or do you simply switch to REST for POST/PUT operations and only use GraphQL for GET?

Querying 2 Entities With Objectify

I have 2 diferent Entities:
USER:
#Entity
public class UserMW {
#Id
private Long id;
private String name;
private Long score;
...
}
USER_CHALLENGE
#Entity
public class UserChallengeMW {
#Id
private Long id;
#Index
private Ref<UserMW> user;
#Index
#Load
private Ref<ChallengeMW> challenge;
}
I want to be able to get one ChallengeMW object and query all users which have done this ChallengeMW. So far it's pretty simple. I just need to query USER_CHALLENGE filtering by "Ref challenge". Something like this:
ofy().load().type(UserChallengeMW.class).filter("challenge", challengeRef).list();
The problem is, I want to order it according to the property "score" into USER entity. Any idea about what is the correct way to go?
Unfortunately, there are no joins in the datastore. Either denormalize the 'score' into the UserChallengeMW object or query for all the data and sort it in memory.

How to model lookup tables JPA

I'm trying to build a Spring Boot data layer on top of another project's DB. I'm want to get to a point where I can consume their data via Restful endpoints rather than directly from the DB. Maximum abstraction is the goal. Here's my problem. Consider the following JPA entity:
#Entity
#Table(name = "PERSON", schema = "public")
public class Person {
#Id private long id;
private String name;
private long favoriteFood;
private Address address;
//Getters, Setter etc.....
}
Notice that favoriteFood is a long, not a String. This is because the DB uses a lookup table. Let's say Joe's favorite food is pizza. The person table stores a 1 in the favorite_food column which is the fk to the "pizza" value stored in the food_ref table. This pattern is repeated hundreds or times in the DB. What is the best way to model this in JPA/Hibernate? Change the variable to String and have the getter and setter do the lookup? I've not found any examples which seems strange. This is a common DB structure. Any advice on best practices would be appreciated. Thanks!
The best way in this scenario is to use one to one relationship in the JPA entity with the FoodRef class
#Entity
#Table(name = "PERSON", schema = "public")
public class Person {
#Id private long id;
private String name;
#OneToOne(fetch=FetchType.LAZY)
#JoinColumn(name="food_ref_id")
private FoodRef favoriteFood;
private Address address;
//Getters, Setter etc.....
}

Removing Many to one relationship in JPA using merge()

I have the below unidirectional Many To One mapping
#Entity
public class Item implements Serializable {
private Integer id;
private Double amount;
private Country origin;
#ManyToOne(optional=true)
#JoinColumn
public Country getOrigin() {
return this.origin;
}
}
#Entity
public class Country implements Serializable{
private String code;
private String desc;
}
Let say the relationship is optional so I am trying to remove the relation by updating it to null using code below
Country country = null;
//item is detached
item.setOrigin(country);
em.merge(item);
But the result turns out to be relationship is not removed.
However, this code works fine if country is not null and the system can update the relationship in DB.
It just simply ignore the field if it's null.
Can someone points out what setting can be changed in order to achieve my desired result?
P.S. Please be reminded that I am not wanting to delete the entity Country, but just remove the relationship between them.
Thanks all it's a mistaken question. It actually works.
There's just some client side issue submitting wrong data to it.

Categories