Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Commit

Permalink
use hibernate search in GetTransUnitListHandler
Browse files Browse the repository at this point in the history
  • Loading branch information
Patrick Huang committed Jun 28, 2012
1 parent d6f80e0 commit 722b5d8
Show file tree
Hide file tree
Showing 10 changed files with 392 additions and 260 deletions.
61 changes: 55 additions & 6 deletions zanata-war/src/main/java/org/zanata/dao/TextFlowDAO.java
Expand Up @@ -21,6 +21,7 @@
package org.zanata.dao;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
Expand Down Expand Up @@ -54,9 +55,13 @@
import org.zanata.model.HDocument;
import org.zanata.model.HLocale;
import org.zanata.model.HTextFlow;
import org.zanata.util.HTextFlowPosComparator;
import org.zanata.webtrans.shared.model.DocumentId;
import org.zanata.webtrans.shared.model.TransMemoryQuery;
import org.zanata.webtrans.shared.rpc.HasSearchType.SearchType;

import com.google.common.collect.Lists;

import lombok.extern.slf4j.Slf4j;

@Name("textFlowDAO")
Expand All @@ -65,6 +70,7 @@
@Slf4j
public class TextFlowDAO extends AbstractDAOImpl<HTextFlow, Long>
{
//TODO replace all getSession() code to use entityManager
private static final Version LUCENE_VERSION = Version.LUCENE_29;

@In
Expand Down Expand Up @@ -237,7 +243,7 @@ public int getCountByDocument(Long documentId)
}

@SuppressWarnings("unchecked")
public List<HTextFlow> getTransUnitList(Long documentId)
public List<HTextFlow> getTextFlows(Long documentId)
{
Query q = getSession().createQuery("from HTextFlow tf where tf.obsolete=0 and tf.document.id = :id order by tf.pos");
q.setParameter("id", documentId);
Expand Down Expand Up @@ -288,20 +294,18 @@ public int compare(Long arg0, Long arg1)
* @param hLocale locale
* @return a list of HTextFlow that has no translation for given locale.
*/
public List<HTextFlow> getAllUntranslatedTextFlowByDocumentId(Long documentId, HLocale hLocale)
public List<HTextFlow> getAllUntranslatedTextFlowByDocumentId(DocumentId documentId, HLocale hLocale)
{
// @formatter:off
String query = "select distinct tf from HTextFlow tf left join tf.targets " +
"where tf.obsolete = 0 and tf.document.id = :docId and " +
"(:locale not in indices(tf.targets) or exists " + //text flow does not have a target for given locale
" (select tft.id from HTextFlowTarget tft where tft.textFlow.id = tf.id and tft.locale = :locale and " +
" (size(tft.contents) = 0 or tft.state = :contentState)" +
" )" + //text flow has target but target has either empty contents or content state is NEW
" (select tft.id from HTextFlowTarget tft where tft.textFlow.id = tf.id and tft.locale = :locale and tft.state = :contentState)" + //text flow has target but target has either empty contents or content state is NEW
") order by tf.pos";
// @formatter:on

Query textFlowQuery = getSession().createQuery(query);
textFlowQuery.setParameter("docId", documentId);
textFlowQuery.setParameter("docId", documentId.getId());
textFlowQuery.setParameter("locale", hLocale);
textFlowQuery.setParameter("contentState", ContentState.New);
textFlowQuery.setCacheable(true).setComment("TextFlowDAO.getAllUntranslatedTextFlowByDocId");
Expand All @@ -312,4 +316,49 @@ public List<HTextFlow> getAllUntranslatedTextFlowByDocumentId(Long documentId, H
new Object [] { documentId, result.size(), hLocale.getLocaleId()});
return result;
}

public List<HTextFlow> getTextFlowsByStatus(DocumentId documentId, HLocale hLocale, boolean filterTranslated, boolean filterNeedReview, boolean filterUntranslated)
{
List<HTextFlow> result = Lists.newArrayList();
List<HTextFlow> untranslated = Lists.newArrayList();
List<HTextFlow> translated = Lists.newArrayList();

if (filterUntranslated)
{
//hard part. leave it alone.
untranslated = getAllUntranslatedTextFlowByDocumentId(documentId, hLocale);
result.addAll(untranslated);
}
if (filterNeedReview || filterTranslated)
{
// @formatter:off
String queryString = "select distinct tf from HTextFlow tf inner join tf.targets as tft " +
"where tf.document.id = :docId and tft.locale = :locale and tft.state in (:contentStates) " +
"order by tf.pos";
// @formatter:on
List<ContentState> contentStates = Lists.newArrayList();
if (filterNeedReview)
{
contentStates.add(ContentState.NeedReview);
}
if (filterTranslated)
{
contentStates.add(ContentState.Approved);
}
Query query = getSession().createQuery(queryString);
query.setParameter("docId", documentId.getId());
query.setParameter("locale", hLocale);
query.setParameterList("contentStates", contentStates);
query.setCacheable(true).setComment("TextFlowDAO.getTextFlowsByStatus");

translated = query.list();
result.addAll(translated);
}

if (!untranslated.isEmpty() && !translated.isEmpty())
{
Collections.sort(result, HTextFlowPosComparator.INSTANCE);
}
return result;
}
}
Expand Up @@ -21,6 +21,7 @@
package org.zanata.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.lucene.analysis.Analyzer;
Expand All @@ -39,17 +40,16 @@
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.AutoCreate;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.log.Log;
import org.zanata.common.ContentState;
import org.zanata.common.LocaleId;
import org.zanata.dao.DocumentDAO;
import org.zanata.dao.TextFlowDAO;
import org.zanata.exception.ZanataServiceException;
import org.zanata.hibernate.search.ConfigurableNgramAnalyzer;
import org.zanata.hibernate.search.IndexFieldLabels;
import org.zanata.model.HDocument;
import org.zanata.model.HLocale;
import org.zanata.model.HTextFlow;
import org.zanata.model.HTextFlowTarget;
Expand All @@ -58,7 +58,8 @@
import org.zanata.service.TextFlowSearchService;
import org.zanata.webtrans.shared.model.DocumentId;
import org.zanata.webtrans.shared.model.WorkspaceId;
import org.zanata.webtrans.shared.util.TextFlowFilter;
import com.google.common.base.Function;
import com.google.common.collect.Lists;

import lombok.extern.slf4j.Slf4j;

Expand All @@ -81,9 +82,6 @@ public class TextFlowSearchServiceImpl implements TextFlowSearchService
@In
DocumentDAO documentDAO;

@In
TextFlowFilter textFlowFilterImpl;

@In
private FullTextEntityManager entityManager;

Expand Down Expand Up @@ -129,14 +127,14 @@ private List<HTextFlowTarget> findTextFlowTargetsByDocumentPaths(WorkspaceId wor
if (!constraints.isSearchInSource() && !constraints.isSearchInTarget())
{
//searching nowhere
return new ArrayList<HTextFlowTarget>();
return Collections.emptyList();
}

// FIXME remove .trim() and zero-length check when ngram analyzer is updated to respect leading and trailing whitespace
int searchLength = Math.min(3, constraints.getSearchString().trim().length());
if (searchLength == 0)
{
return new ArrayList<HTextFlowTarget>();
return Collections.emptyList();
}
Analyzer ngramAnalyzer = new ConfigurableNgramAnalyzer(searchLength, !constraints.isCaseSensitive());

Expand Down Expand Up @@ -231,7 +229,126 @@ private List<HTextFlowTarget> findTextFlowTargetsByDocumentPaths(WorkspaceId wor
@Override
public List<HTextFlow> findTextFlows(WorkspaceId workspace, DocumentId doc, FilterConstraints constraints)
{
// TODO Implement findTextFlows within document
return null;
//TODO this method has high percentage of duplication from the above method. Refactor.
LocaleId localeId = workspace.getLocaleId();
String projectSlug = workspace.getProjectIterationId().getProjectSlug();
String iterationSlug = workspace.getProjectIterationId().getIterationSlug();

localeServiceImpl.validateLocaleByProjectIteration(localeId, projectSlug, iterationSlug);

if (!constraints.isSearchInSource() && !constraints.isSearchInTarget())
{
//searching nowhere
return Collections.emptyList();
}

// FIXME remove .trim() and zero-length check when ngram analyzer is updated to respect leading and trailing whitespace
int searchLength = Math.min(3, constraints.getSearchString().trim().length());
if (searchLength == 0)
{
return Collections.emptyList();
}
Analyzer ngramAnalyzer = new ConfigurableNgramAnalyzer(searchLength, !constraints.isCaseSensitive());

String[] searchFields = (constraints.isCaseSensitive() ? IndexFieldLabels.CONTENT_FIELDS_CASE_PRESERVED : IndexFieldLabels.CONTENT_FIELDS_CASE_FOLDED);

Query searchPhraseQuery;
QueryParser parser = new MultiFieldQueryParser(Version.LUCENE_29, searchFields, ngramAnalyzer);
try
{
searchPhraseQuery = parser.parse("\"" + QueryParser.escape(constraints.getSearchString()) + "\"");
}
catch (ParseException e)
{
throw new ZanataServiceException("Failed to parse query", e);
}

TermQuery projectQuery = new TermQuery(new Term(IndexFieldLabels.PROJECT_FIELD, projectSlug));
TermQuery iterationQuery = new TermQuery(new Term(IndexFieldLabels.ITERATION_FIELD, iterationSlug));
TermQuery localeQuery = new TermQuery(new Term(IndexFieldLabels.LOCALE_ID_FIELD, localeId.getId()));
HDocument hDocument = documentDAO.getById(doc.getId());
TermQuery documentQuery = new TermQuery(new Term(IndexFieldLabels.DOCUMENT_ID_FIELD, hDocument.getDocId()));

BooleanQuery sourceQuery = new BooleanQuery();
sourceQuery.add(projectQuery, Occur.MUST);
sourceQuery.add(iterationQuery, Occur.MUST);
sourceQuery.add(searchPhraseQuery, Occur.MUST);
sourceQuery.add(documentQuery, Occur.MUST);

List<HTextFlow> resultList = Lists.newArrayList();
if (constraints.isSearchInTarget())
{
BooleanQuery targetQuery = (BooleanQuery) sourceQuery.clone();
targetQuery.add(localeQuery, Occur.MUST);
if (!constraints.isIncludeApproved())
{
TermQuery approvedStateQuery = new TermQuery(new Term(IndexFieldLabels.CONTENT_STATE_FIELD, ContentState.Approved.toString()));
targetQuery.add(approvedStateQuery, Occur.MUST_NOT);
}

if (!constraints.isIncludeFuzzy())
{
TermQuery approvedStateQuery = new TermQuery(new Term(IndexFieldLabels.CONTENT_STATE_FIELD, ContentState.NeedReview.toString()));
targetQuery.add(approvedStateQuery, Occur.MUST_NOT);
}

if (!constraints.isIncludeNew())
{
TermQuery approvedStateQuery = new TermQuery(new Term(IndexFieldLabels.CONTENT_STATE_FIELD, ContentState.New.toString()));
targetQuery.add(approvedStateQuery, Occur.MUST_NOT);
}

FullTextQuery ftQuery = entityManager.createFullTextQuery(targetQuery, HTextFlowTarget.class);
@SuppressWarnings("unchecked")
List<HTextFlowTarget> matchedTargets = (List<HTextFlowTarget>) ftQuery.getResultList();
log.info("got {} HTextFLowTarget results", matchedTargets.size());
List<HTextFlow> hTextFlows = Lists.transform(matchedTargets, new Function<HTextFlowTarget, HTextFlow>()
{
@Override
public HTextFlow apply(HTextFlowTarget target)
{
return target.getTextFlow();
}
});
resultList.addAll(hTextFlows);
}

if (constraints.isSearchInSource())
{
FullTextQuery ftQuery = entityManager.createFullTextQuery(sourceQuery, HTextFlow.class);
@SuppressWarnings("unchecked")
List<HTextFlow> matchedSources = (List<HTextFlow>) ftQuery.getResultList();
log.info("got {} HTextFLow results", matchedSources.size());
HLocale hLocale = localeServiceImpl.getByLocaleId(localeId);
for (HTextFlow hTextFlow : matchedSources)
{
if (!resultList.contains(hTextFlow))
{
HTextFlowTarget hTextFlowTarget = hTextFlow.getTargets().get(hLocale.getId());
if (isContentStateValid(hTextFlowTarget, constraints))
{
resultList.add(hTextFlow);
}
}
}
}

return resultList;
}

private static boolean isContentStateValid(HTextFlowTarget hTextFlowTarget, FilterConstraints constraints)
{
if (hTextFlowTarget == null)
{
return constraints.isIncludeNew();
}
else
{
ContentState state = hTextFlowTarget.getState();
return (constraints.isIncludeApproved() && state == ContentState.Approved) ||
(constraints.isIncludeFuzzy() && state == ContentState.NeedReview) ||
(constraints.isIncludeNew() && state == ContentState.New);
}
}

}
@@ -0,0 +1,19 @@
package org.zanata.util;

import java.util.Comparator;

import org.zanata.model.HTextFlow;

/**
* @author Patrick Huang <a href="mailto:pahuang@redhat.com">pahuang@redhat.com</a>
*/
public enum HTextFlowPosComparator implements Comparator<HTextFlow>
{
INSTANCE;

@Override
public int compare(HTextFlow one, HTextFlow other)
{
return one.getPos().compareTo(other.getPos());
}
}
Expand Up @@ -28,6 +28,7 @@
import org.zanata.webtrans.client.history.History;
import org.zanata.webtrans.client.history.HistoryToken;

import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.ui.HasValue;
Expand All @@ -50,7 +51,7 @@ public TransFilterPresenter(final Display display, final EventBus eventBus, fina
{
super(display, eventBus);
this.history = history;
currentState = new HistoryToken();;
currentState = new HistoryToken();
}

@Override
Expand All @@ -62,7 +63,7 @@ protected void onBind()
@Override
public void onValueChange(ValueChangeEvent<String> event)
{
if (event.getValue() != currentState.getSearchText())
if (!event.getValue().equals(currentState.getSearchText()))
{
HistoryToken newToken = history.getHistoryToken();
newToken.setSearchText(event.getValue());
Expand All @@ -78,10 +79,10 @@ public void onValueChange(ValueChangeEvent<String> event)
public void onValueChange(ValueChangeEvent<String> event)
{
HistoryToken token = history.getHistoryToken();
if (token.getSearchText() != currentState.getSearchText())
if (!token.getSearchText().equals(currentState.getSearchText()))
{
eventBus.fireEvent(new FindMessageEvent(token.getSearchText()));
display.getFilterText().setValue(token.getSearchText(), false);
eventBus.fireEvent(new FindMessageEvent(token.getSearchText()));
}
currentState = token;
}
Expand Down

0 comments on commit 722b5d8

Please sign in to comment.