Hi yrodiere,
1- The generic query method is like the below code; it uses the entity fields and projection fields.
Fields are used to full text search “vehicleId”, “category.categoryId”, “category.name”, “modelDescription”, “longModelDescription”, “brand”, “equipmentType”, “modelType”, “body”, “startYear”, “endYear”
Projection Fields; “vehicleId”, “category.categoryId”, “brand”, “modelDescription”, “longModelDescription”, “startYear”, “endYear”, “body”, “hp”
protected Analyzer analyzer = new WhitespaceAnalyzer();
@Override
public List<T> searchProjection(String searchText, String[] fields, String[] projectionFields, BasicTransformerAdapter resultTransformer, Sort sort, int firstResult, int maxResult, Class<T> entityClass) {
if (Strings.isNullOrEmpty(searchText)) {
return new ArrayList<>();
}
List<String> keywords = this.tokenizeString(this.analyzer, searchText);
try {
FullTextSession fullTextSession = this.getFullTextSession();
QueryBuilder qb = this.getQueryBuilder(fullTextSession, entityClass);
BooleanJunction<BooleanJunction> booleanJunction = qb.bool();
booleanJunction.must(qb.keyword().onField("deleted").matching(false).createQuery());
for (String keyword : keywords) {
booleanJunction.must(qb.keyword().wildcard().onFields(fields).matching(keyword + "*").createQuery());
}
FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(booleanJunction.createQuery(), entityClass);
if (projectionFields != null && projectionFields.length > 0) {
fullTextQuery.setProjection(projectionFields);
fullTextQuery.setResultTransformer(resultTransformer);
}
fullTextQuery.setFirstResult(firstResult); //start from the firstResult element
fullTextQuery.setMaxResults(maxResult); //return max elements
if (sort != null) {
fullTextQuery.setSort(sort);
}
return fullTextQuery.getResultList();
} catch (EmptyQueryException ex) {
logger.error("Something went wrong while searching: {}, exception:{}", searchText, ex.getLocalizedMessage());
return new ArrayList<>();
} catch (Exception ex) {
logger.error("Something went wrong while searching: {}, exception:{}", searchText, ex.getLocalizedMessage());
}
return null;
}
/**
* Validate input against the tokenizer and return a list of terms.
*
* @param analyzer
* @param string
* @return
*/
public List<String> tokenizeString(Analyzer analyzer, String string) {
List<String> result = new ArrayList<>();
try {
TokenStream stream = analyzer.tokenStream(null, new StringReader(string));
stream.reset();
while (stream.incrementToken()) {
result.add(stream.getAttribute(CharTermAttribute.class).toString().toLowerCase());
}
stream.close();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (Exception ex) {
ex.printStackTrace();
}
return result;
}
2- Entity model is like below;
@Data
@Entity
@Indexed
@Table(name = "vehicle")
@NoArgsConstructor
@EqualsAndHashCode(of = {"vehicleId"}, callSuper=false)
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class VehicleDbo extends MappedDomainObjectBase {
@Id
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Column(name = "vehicle_id", nullable = false, unique = true)
private String vehicleId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "category_id", referencedColumnName = "category_id")
@IndexedEmbedded(includeEmbeddedObjectId = true)
private CategoryDbo category;
@Column(name = "model")
private String model;
@Column(name = "brand")
@Field(index = org.hibernate.search.annotations.Index.YES, analyze = Analyze.YES, store = Store.YES, analyzer = @Analyzer(definition = "searchTextAnalyzer"))
private String brand;
@Column(name = "model_description")
@Field(index = org.hibernate.search.annotations.Index.YES, analyze = Analyze.YES, store = Store.YES, analyzer = @Analyzer(definition = "searchTextAnalyzer"))
private String modelDescription;
@Column(name = "width")
private int width;
@Column(name = "height")
private int height;
@Column(name = "body")
@Field(index = org.hibernate.search.annotations.Index.YES, analyze = Analyze.YES, store = Store.YES, analyzer = @Analyzer(definition = "searchTextAnalyzer"))
private String body;
@Column(name = "end_year")
@Field(index = org.hibernate.search.annotations.Index.YES, analyze = Analyze.YES, store = Store.YES, analyzer = @Analyzer(definition = "searchTextAnalyzer"))
@FieldBridge(impl = LongBridge.class)
private int endYear;
@Column(name = "fuel_type")
private String fuelType;
@Column(name = "fuel_type_enum")
@Enumerated(EnumType.ORDINAL)
private FuelTypeEnum fuelTypeEnum;
@Column(name = "equipment_type")
@Field(index = org.hibernate.search.annotations.Index.YES, analyze = Analyze.YES, store = Store.YES, analyzer = @Analyzer(definition = "searchTextAnalyzer"))
private String equipmentType;
@Column(name = "long_model_description")
@Field(index = org.hibernate.search.annotations.Index.YES, analyze = Analyze.YES, store = Store.YES, analyzer = @Analyzer(definition = "searchTextAnalyzer"))
private String longModelDescription;
@Column(name = "doors")
private int doors;
@Column(name = "acceleration")
private double acceleration;
@Column(name = "hp")
@Field(index = org.hibernate.search.annotations.Index.YES, analyze = Analyze.YES, store = Store.YES, analyzer = @Analyzer(definition = "searchTextAnalyzer"))
@FieldBridge(impl = LongBridge.class)
private int hp;
@Column(name = "udc")
private double udc;
@Column(name = "eudc")
private double eudc;
@Column(name = "vehicle_type")
private int vehicleType;
@Column(name = "auto_class")
private String autoClass;
@Column(name = "start_year")
@Field(index = org.hibernate.search.annotations.Index.YES, analyze = Analyze.YES, store = Store.YES, analyzer = @Analyzer(definition = "searchTextAnalyzer"))
@FieldBridge(impl = LongBridge.class)
private int startYear;
@Column(name = "seats")
private int seats;
@Column(name = "cylinders")
private int cylinders;
@Column(name = "ccm")
private int ccm;
}
3- I use elastic search integration.
spring.jpa.properties.hibernate.search.default.indexmanager=elasticsearch
spring.jpa.properties.hibernate.search.default.elasticsearch.aws.signing.enabled=true
spring.jpa.properties.hibernate.search.default.elasticsearch.host=${ELASTIC_SEARCH_URL}
spring.jpa.properties.hibernate.search.default.elasticsearch.aws.access_key=${ELASTIC_SEARCH_AWS_ACCESS_KEY}
spring.jpa.properties.hibernate.search.default.elasticsearch.aws.secret_key=${ELASTIC_SEARCH_AWS_SECRET_KEY}
spring.jpa.properties.hibernate.search.default.elasticsearch.aws.region=${ELASTIC_SEARCH_AWS_REGION}
spring.jpa.properties.hibernate.search.default.elasticsearch.index_schema_management_strategy=create
spring.jpa.properties.hibernate.search.default.elasticsearch.required_index_status=yellow
spring.jpa.properties.hibernate.search.default.elasticsearch.read_timeout=600000
spring.jpa.properties.hibernate.search.default.elasticsearch.index_management_wait_timeout=600000
4- For example when searching with only “206+” it returns empty. if a search with “206” it returns all match because of wildcard match.
This is the database records for 206+
SELECT long_model_description FROM car.vehicle where long_model_description like '%206+%';
206+ 1.4 Comfort
206+ 1.4 Urban Move
206+ 1.4 Sportium
206+ 1.4 HDI Sportium
206+ 1.4 Sportium
206+ 1.4 HDI Urban Move
206+ 1.4 HDI Comfort
206+ 1.4 HDI Envy
206+ 1.4 HDI Sportium
206+ 1.4 Envy