Rozdělané fulltextové vyhledávání
parent
b1818546c0
commit
02311ae5dd
@ -0,0 +1,71 @@
|
||||
package info.bukova.isspst.data;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.Type;
|
||||
import org.hibernate.search.annotations.Analyze;
|
||||
import org.hibernate.search.annotations.Field;
|
||||
import org.hibernate.search.annotations.Index;
|
||||
import org.hibernate.search.annotations.Indexed;
|
||||
|
||||
@Entity
|
||||
@Table(name = "FILE_METAINFO")
|
||||
@Indexed
|
||||
public class FileMetainfo extends BaseData {
|
||||
|
||||
@Column(name = "FILE_NAME")
|
||||
private String fileName;
|
||||
@Column(name = "PATH_IN_FILESYSTEM")
|
||||
private String pathInFilesystem;
|
||||
@Column(name = "MODULE_ID")
|
||||
private String moduleId;
|
||||
@Column(name = "RECORD_ID")
|
||||
private int recordId;
|
||||
@Column(name = "CONTENT")
|
||||
@Type(type = "text")
|
||||
@Field(index = Index.YES, analyze = Analyze.YES)
|
||||
private String content;
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public String getPathInFilesystem() {
|
||||
return pathInFilesystem;
|
||||
}
|
||||
|
||||
public void setPathInFilesystem(String pathInFilesystem) {
|
||||
this.pathInFilesystem = pathInFilesystem;
|
||||
}
|
||||
|
||||
public String getModuleId() {
|
||||
return moduleId;
|
||||
}
|
||||
|
||||
public void setModuleId(String moduleId) {
|
||||
this.moduleId = moduleId;
|
||||
}
|
||||
|
||||
public int getRecordId() {
|
||||
return recordId;
|
||||
}
|
||||
|
||||
public void setRecordId(int recordId) {
|
||||
this.recordId = recordId;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package info.bukova.isspst.services;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface FullTextService {
|
||||
|
||||
public void reindex();
|
||||
public List<?> search(Class<?> entityClass, String[] fields, String word);
|
||||
public List<?> globalSearch(String word);
|
||||
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
package info.bukova.isspst.services;
|
||||
|
||||
import info.bukova.isspst.dao.QueryDao;
|
||||
import info.bukova.isspst.data.BaseData;
|
||||
import info.bukova.isspst.data.User;
|
||||
import info.bukova.isspst.sort.ReflectionTools;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.search.FullTextSession;
|
||||
import org.hibernate.search.Search;
|
||||
import org.hibernate.search.annotations.IndexedEmbedded;
|
||||
import org.hibernate.search.query.dsl.QueryBuilder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
public class FullTextServiceImpl implements FullTextService {
|
||||
|
||||
@Autowired
|
||||
private QueryDao queryDao;
|
||||
private List<Class<?>> classesForSearch;
|
||||
private Map<Class<?>, String[]> fields;
|
||||
private List<Class<?>> nestedClasses;
|
||||
|
||||
public FullTextServiceImpl() {
|
||||
nestedClasses = new ArrayList<Class<?>>();
|
||||
nestedClasses.add(User.class);
|
||||
nestedClasses.add(BaseData.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void reindex() {
|
||||
Logger logger = LoggerFactory.getLogger(FullTextServiceImpl.class);
|
||||
logger.info("Indexing database for fulltext search");
|
||||
FullTextSession ftSession = Search.getFullTextSession(queryDao.getSession());
|
||||
try {
|
||||
ftSession.createIndexer().startAndWait();
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("Cannot index database");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public List<?> search(Class<?> entityClass, String[] fields, String word) {
|
||||
FullTextSession session = Search.getFullTextSession(queryDao.getSession());
|
||||
QueryBuilder qb = session.getSearchFactory().buildQueryBuilder().forEntity(entityClass).get();
|
||||
|
||||
Query luceneQuery = qb.keyword().onFields(fields).matching(word).createQuery();
|
||||
org.hibernate.Query hiberQuery = session.createFullTextQuery(luceneQuery, entityClass);
|
||||
|
||||
return hiberQuery.list();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public List<?> globalSearch(String word) {
|
||||
List<Object> result = new ArrayList<Object>();
|
||||
|
||||
for (Class<?> clazz : classesForSearch()) {
|
||||
result.addAll(search(clazz, fields.get(clazz), word));
|
||||
}
|
||||
|
||||
for (Object o : result) {
|
||||
BaseData data = (BaseData) o;
|
||||
Hibernate.initialize(data.getModifiedBy());
|
||||
Hibernate.initialize(data.getOwnedBy());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Class<?>> classesForSearch() {
|
||||
if (classesForSearch != null) {
|
||||
return classesForSearch;
|
||||
}
|
||||
|
||||
classesForSearch = new ArrayList<Class<?>>();
|
||||
fields = new HashMap<Class<?>, String[]>();
|
||||
FullTextSession session = Search.getFullTextSession(queryDao.getSession());
|
||||
|
||||
for (Class<?> clazz : session.getSearchFactory().getIndexedTypes()) {
|
||||
if (nestedClasses.contains(clazz)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
classesForSearch.add(clazz);
|
||||
fields.put(clazz, fields(clazz, ""));
|
||||
}
|
||||
|
||||
return classesForSearch;
|
||||
}
|
||||
|
||||
private String[] fields(Class<?> clazz, String prefix) {
|
||||
List<String> res = new ArrayList<String>();
|
||||
|
||||
for (Field field : ReflectionTools.getFields(clazz)) {
|
||||
for (Annotation a : field.getDeclaredAnnotations()) {
|
||||
if (a instanceof org.hibernate.search.annotations.Field) {
|
||||
res.add(prefix + field.getName());
|
||||
}
|
||||
|
||||
if (a instanceof IndexedEmbedded) {
|
||||
Class<?> fieldClass;
|
||||
|
||||
fieldClass = field.getType();
|
||||
if (fieldClass.isAssignableFrom(List.class)) {
|
||||
ParameterizedType type = (ParameterizedType) field.getGenericType();
|
||||
Type[] types = type.getActualTypeArguments();
|
||||
if (types.length == 1) {
|
||||
fieldClass = (Class<?>)types[0];
|
||||
}
|
||||
}
|
||||
|
||||
classesForSearch.remove(fieldClass);
|
||||
fields.remove(fieldClass);
|
||||
nestedClasses.add(fieldClass);
|
||||
|
||||
String nestedPrefix = prefix + field.getName() + ".";
|
||||
res.addAll(Arrays.asList(fields(fieldClass, nestedPrefix)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String resArray[] = new String[res.size()];
|
||||
resArray = res.toArray(resArray);
|
||||
return resArray;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package info.bukova.isspst.ui.search;
|
||||
|
||||
import info.bukova.isspst.EntityUrlResolver;
|
||||
import info.bukova.isspst.data.BaseData;
|
||||
|
||||
public abstract class AbstractResultTransformer implements ResultTransformer {
|
||||
|
||||
protected EntityUrlResolver urlResolver;
|
||||
|
||||
@Override
|
||||
public SearchResult transformObject(Object object) {
|
||||
BaseData data = (BaseData)object;
|
||||
SearchResult result = new SearchResult();
|
||||
|
||||
result.setCreated(data.getCreated());
|
||||
result.setModified(data.getModified());
|
||||
result.setOwnedBy(data.getOwnedBy());
|
||||
result.setModifiedBy(data.getModifiedBy());
|
||||
|
||||
if (urlResolver != null) {
|
||||
result.setUrl(urlResolver.entityUrl(data));
|
||||
}
|
||||
|
||||
|
||||
transform(object, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract void transform(Object object, SearchResult result);
|
||||
|
||||
@Override
|
||||
public void setUrlResolver(EntityUrlResolver resolver) {
|
||||
this.urlResolver = resolver;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package info.bukova.isspst.ui.search;
|
||||
|
||||
|
||||
public class CommonResultTransformer extends AbstractResultTransformer implements ResultTransformer {
|
||||
|
||||
@Override
|
||||
protected void transform(Object object, SearchResult result) {
|
||||
result.setRecordName("Neznámá entita");
|
||||
result.setDescription(object.toString());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package info.bukova.isspst.ui.search;
|
||||
|
||||
import info.bukova.isspst.data.Order;
|
||||
|
||||
public class OrderResultTransformer extends AbstractResultTransformer implements ResultTransformer {
|
||||
|
||||
@Override
|
||||
protected void transform(Object object, SearchResult result) {
|
||||
Order order = (Order)object;
|
||||
result.setRecordName("Objednávka: " + order.getNumser());
|
||||
result.setDescription(order.getDescription() + ", dodavatel: " + order.getSuplier().getCompany());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package info.bukova.isspst.ui.search;
|
||||
|
||||
import info.bukova.isspst.data.Requirement;
|
||||
|
||||
public class RequirementResultTransformer extends AbstractResultTransformer implements ResultTransformer {
|
||||
|
||||
@Override
|
||||
protected void transform(Object object, SearchResult result) {
|
||||
Requirement req = (Requirement)object;
|
||||
result.setRecordName("Požadavek na nákup/službu: " + req.getNumser());
|
||||
result.setDescription(req.getDescription());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package info.bukova.isspst.ui.search;
|
||||
|
||||
import info.bukova.isspst.EntityUrlResolver;
|
||||
|
||||
public interface ResultTransformer {
|
||||
|
||||
public SearchResult transformObject(Object object);
|
||||
public void setUrlResolver(EntityUrlResolver resolver);
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package info.bukova.isspst.ui.search;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.zkoss.bind.annotation.Command;
|
||||
import org.zkoss.bind.annotation.NotifyChange;
|
||||
import org.zkoss.zk.ui.select.annotation.WireVariable;
|
||||
|
||||
import info.bukova.isspst.UrlResolverHolder;
|
||||
import info.bukova.isspst.data.BaseData;
|
||||
import info.bukova.isspst.data.Requirement;
|
||||
import info.bukova.isspst.data.TripRequirement;
|
||||
import info.bukova.isspst.services.FullTextService;
|
||||
|
||||
public class SearchForm {
|
||||
|
||||
@WireVariable
|
||||
private FullTextService fulltextService;
|
||||
@WireVariable
|
||||
private UrlResolverHolder urlResolverHolder;
|
||||
private List<SearchResult> results;
|
||||
|
||||
private String keyWord;
|
||||
|
||||
public void init() {
|
||||
|
||||
}
|
||||
|
||||
@Command
|
||||
@NotifyChange("results")
|
||||
public void doSearch() {
|
||||
if (keyWord == null || keyWord.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//String[] fields = {"items.textItem", "description"};
|
||||
List<?> result = fulltextService.globalSearch(keyWord);
|
||||
//List<?> result = fulltextService.search(Requirement.class, fields, keyWord);
|
||||
|
||||
SearchListTransformer srt = new SearchListTransformer(urlResolverHolder);
|
||||
results = srt.transform(result);
|
||||
|
||||
System.out.println(result.size());
|
||||
}
|
||||
|
||||
public String getKeyWord() {
|
||||
return keyWord;
|
||||
}
|
||||
|
||||
public void setKeyWord(String keyWord) {
|
||||
this.keyWord = keyWord;
|
||||
}
|
||||
|
||||
public List<SearchResult> getResults() {
|
||||
return results;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package info.bukova.isspst.ui.search;
|
||||
|
||||
import info.bukova.isspst.UrlResolverHolder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SearchListTransformer {
|
||||
|
||||
private UrlResolverHolder urlResolverHolder;
|
||||
|
||||
public SearchListTransformer() {
|
||||
urlResolverHolder = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public SearchListTransformer(UrlResolverHolder urlResolverHolder) {
|
||||
super();
|
||||
this.urlResolverHolder = urlResolverHolder;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public List<SearchResult> transform(List<?> source) {
|
||||
List<SearchResult> ret = new ArrayList<SearchResult>();
|
||||
|
||||
for (int i = 0; i < source.size(); i++) {
|
||||
ResultTransformer transformer = TransformerFactory.transformerFor(source.get(i));
|
||||
if (urlResolverHolder != null) {
|
||||
transformer.setUrlResolver(urlResolverHolder.resolverFor(source.get(i).getClass()));
|
||||
}
|
||||
ret.add(transformer.transformObject(source.get(i)));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package info.bukova.isspst.ui.search;
|
||||
|
||||
import info.bukova.isspst.data.User;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class SearchResult {
|
||||
|
||||
private String recordName;
|
||||
private String description;
|
||||
private String url;
|
||||
private Date created;
|
||||
private Date modified;
|
||||
private User ownedBy;
|
||||
private User modifiedBy;
|
||||
|
||||
public String getRecordName() {
|
||||
return recordName;
|
||||
}
|
||||
|
||||
public void setRecordName(String recordName) {
|
||||
this.recordName = recordName;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public Date getModified() {
|
||||
return modified;
|
||||
}
|
||||
|
||||
public void setModified(Date modified) {
|
||||
this.modified = modified;
|
||||
}
|
||||
|
||||
public User getOwnedBy() {
|
||||
return ownedBy;
|
||||
}
|
||||
|
||||
public void setOwnedBy(User ownedBy) {
|
||||
this.ownedBy = ownedBy;
|
||||
}
|
||||
|
||||
public User getModifiedBy() {
|
||||
return modifiedBy;
|
||||
}
|
||||
|
||||
public void setModifiedBy(User modifiedBy) {
|
||||
this.modifiedBy = modifiedBy;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package info.bukova.isspst.ui.search;
|
||||
|
||||
import info.bukova.isspst.data.Order;
|
||||
import info.bukova.isspst.data.Requirement;
|
||||
import info.bukova.isspst.data.TripBill;
|
||||
import info.bukova.isspst.data.TripRequirement;
|
||||
|
||||
public class TransformerFactory {
|
||||
|
||||
public static ResultTransformer transformerFor(Object object) {
|
||||
if (object instanceof TripBill) {
|
||||
return new TripBillTransformer();
|
||||
}
|
||||
if (object instanceof TripRequirement) {
|
||||
return new TripRequirementTransformer();
|
||||
}
|
||||
if (object instanceof Requirement) {
|
||||
return new RequirementResultTransformer();
|
||||
}
|
||||
if (object instanceof Order) {
|
||||
return new OrderResultTransformer();
|
||||
}
|
||||
|
||||
return new CommonResultTransformer();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package info.bukova.isspst.ui.search;
|
||||
|
||||
import info.bukova.isspst.data.TripBill;
|
||||
|
||||
public class TripBillTransformer extends AbstractResultTransformer implements ResultTransformer {
|
||||
|
||||
@Override
|
||||
protected void transform(Object object, SearchResult result) {
|
||||
TripBill tb = (TripBill)object;
|
||||
result.setRecordName("Vyúčtování služební cesty: " + tb.getRequirement().getNumser());
|
||||
result.setDescription("Z " + tb.getRequirement().getFrom() + " do " + tb.getRequirement().getTo() + " - " + tb.getRequirement().getDescription());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package info.bukova.isspst.ui.search;
|
||||
|
||||
import info.bukova.isspst.data.TripRequirement;
|
||||
|
||||
public class TripRequirementTransformer extends AbstractResultTransformer implements ResultTransformer {
|
||||
|
||||
@Override
|
||||
protected void transform(Object object, SearchResult result) {
|
||||
TripRequirement tr = (TripRequirement)object;
|
||||
result.setRecordName("Žádost o služební cestu: " + tr.getNumser());
|
||||
result.setDescription("Z " + tr.getFrom() + " do " + tr.getTo() + " - " + tr.getDescription());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
<?page title="Search" contentType="text/html;charset=UTF-8"?>
|
||||
<zk>
|
||||
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
|
||||
|
||||
<window title="Search" border="normal" closable="true"
|
||||
apply="org.zkoss.bind.BindComposer"
|
||||
viewModel="@id('vm') @init('info.bukova.isspst.ui.search.SearchForm')"
|
||||
vflex="1">
|
||||
|
||||
<vbox width="100%"
|
||||
vflex="1">
|
||||
<hbox>
|
||||
<textbox value="@bind(vm.keyWord)" />
|
||||
<button label="Hledat" onClick="@command('doSearch')" />
|
||||
</hbox>
|
||||
<panel hflex="1" vflex="1">
|
||||
<panelchildren style="overflow:auto;">
|
||||
<vbox children="@load(vm.results)" width="100%">
|
||||
<template name="children">
|
||||
<vbox width="100%">
|
||||
<separator bar="true" width="100%"/>
|
||||
<label value="@load(each.recordName)" style="font-weight: bold; font-size:14px;"/>
|
||||
<label value="@load(each.description)"/>
|
||||
<hbox>
|
||||
<label value="Vytvořeno: "/>
|
||||
<label value="@load(each.created)"/>
|
||||
</hbox>
|
||||
<hbox>
|
||||
<label value="Vytvořil: "/>
|
||||
<label value="@load(each.ownedBy)"/>
|
||||
</hbox>
|
||||
<hbox>
|
||||
<label value="Změněno: "/>
|
||||
<label value="@load(each.modified)"/>
|
||||
</hbox>
|
||||
<hbox>
|
||||
<label value="Změnil: "/>
|
||||
<label value="@load(each.modifiedBy)"/>
|
||||
</hbox>
|
||||
<a href="@load(each.url)" label="@load(each.url)"/>
|
||||
</vbox>
|
||||
</template>
|
||||
</vbox>
|
||||
</panelchildren>
|
||||
</panel>
|
||||
</vbox>
|
||||
|
||||
</window>
|
||||
</zk>
|
Loading…
Reference in New Issue