View Javadoc

1   package com.atlassian.user.impl.hibernate.search.query;
2   
3   import com.atlassian.user.EntityException;
4   import com.atlassian.user.impl.RepositoryException;
5   import com.atlassian.user.impl.hibernate.DefaultHibernateExternalEntity;
6   import com.atlassian.user.impl.hibernate.DefaultHibernateGroup;
7   import com.atlassian.user.impl.hibernate.DefaultHibernateUser;
8   import com.atlassian.user.impl.hibernate.repository.HibernateRepository;
9   import com.atlassian.user.repository.RepositoryIdentifier;
10  import com.atlassian.user.search.DefaultSearchResult;
11  import com.atlassian.user.search.SearchResult;
12  import com.atlassian.user.search.page.DefaultPager;
13  import com.atlassian.user.search.query.*;
14  import net.sf.hibernate.Criteria;
15  import net.sf.hibernate.HibernateException;
16  import net.sf.hibernate.Session;
17  import net.sf.hibernate.expression.*;
18  import org.springframework.orm.hibernate.SessionFactoryUtils;
19  
20  import java.util.Iterator;
21  import java.util.List;
22  
23  /**
24   * Handles {@link Query} objects on a {@link HibernateRepository}.
25   * <p/>
26   * For now - boolean queries can only be performed on the same
27   */
28  public class HibernateEntityQueryParser extends AbstractEntityQueryParser implements EntityQueryParser
29  {
30      private final RepositoryIdentifier identifier;
31      private final HibernateRepository repository;
32  
33      public HibernateEntityQueryParser(RepositoryIdentifier identifier, HibernateRepository repository)
34      {
35          this.identifier = identifier;
36          this.repository = repository;
37      }
38  
39      public SearchResult findUsers(Query query) throws EntityException
40      {
41          validateQuery(query);
42          return parseQuery(query);
43      }
44  
45      public SearchResult findGroups(Query query) throws EntityException
46      {
47          validateQuery(query);
48          return parseQuery(query);
49      }
50  
51      public SearchResult findUsers(Query query, QueryContext context) throws EntityException
52      {
53          validateQuery(query);
54  
55          if (context instanceof AllRepositoriesQueryContext ||
56              context.getRepositoryKeys().contains(identifier.getKey()) ||
57              context.getRepositoryKeys().contains(QueryContext.ALL_REPOSITORIES))
58              return parseQuery(query);
59  
60          return null;
61      }
62  
63      public SearchResult findGroups(Query query, QueryContext context) throws EntityException
64      {
65          validateQuery(query);
66  
67          if (context instanceof AllRepositoriesQueryContext ||
68              context.getRepositoryKeys().contains(identifier.getKey()) ||
69              context.getRepositoryKeys().contains(QueryContext.ALL_REPOSITORIES))
70  
71              return parseQuery(query);
72  
73          return null;
74      }
75  
76      private MatchMode getMatchMode(String matchingRule)
77      {
78          if (matchingRule.equals(TermQuery.SUBSTRING_CONTAINS))
79          {
80              return MatchMode.ANYWHERE;
81          }
82          if (matchingRule.equals(TermQuery.SUBSTRING_ENDS_WITH))
83          {
84              return MatchMode.END;
85          }
86          if (matchingRule.equals(TermQuery.SUBSTRING_STARTS_WITH))
87          {
88              return MatchMode.START;
89          }
90          return MatchMode.EXACT;
91      }
92  
93      private String identifyProperty(TermQuery q)
94      {
95          String entityProperty = null;
96  
97          if (q instanceof UserNameTermQuery)
98              entityProperty = "name";
99          else if (q instanceof EmailTermQuery)
100             entityProperty = "email";
101         else if (q instanceof FullNameTermQuery)
102             entityProperty = "fullName";
103         else if (q instanceof GroupNameTermQuery)
104             entityProperty = "name";
105         else if (q instanceof GroupsOfUserTwoTermQuery)
106             entityProperty = "entity";
107 
108         return entityProperty;
109     }
110 
111     /**
112      * Converts a {@link BooleanQuery} object into an appropriate Hibernate {@link Criteria}, performs the search, and
113      * returns a {@link SearchResult}.
114      */
115     private SearchResult parseQuery(Query q) throws EntityException
116     {
117         //1. establish the Object being queried.
118         Session session = SessionFactoryUtils.getSession(repository.getSessionFactory(), true);
119         Query definingQuery = q;
120 
121         if (q instanceof BooleanQuery)
122             definingQuery = identifyDefiningQuery((BooleanQuery) q);
123 
124         List result;
125         try
126         {
127             Criteria baseCriteria = getBaseCriteria(definingQuery, session);
128             baseCriteria = identifyAndAddSearchCriteria(q, definingQuery, baseCriteria);
129             baseCriteria.addOrder(Order.asc("name")); // sort results explicitly by username rather than leaving this up to the mood of the database
130 
131             result = baseCriteria.list();
132         }
133         catch (HibernateException e)
134         {
135             throw new RepositoryException(e);
136         }
137 
138         return new DefaultSearchResult(new DefaultPager(result), identifier.getName());
139     }
140 
141     private Criteria identifyAndAddSearchCriteria(Query q, Query definingQuery, Criteria baseCriteria)
142         throws EntityQueryException, HibernateException
143     {
144         if (q instanceof BooleanQuery)
145             return addSearchCriteria((BooleanQuery) q, definingQuery, baseCriteria);
146         else
147         {
148             return addSearchCriteria((TermQuery) q, baseCriteria);
149         }
150     }
151 
152     private Criteria addSearchCriteria(BooleanQuery q, Query definingQuery, Criteria baseCriteria)
153         throws EntityQueryException, HibernateException
154     {
155         /**
156          * Membership queries are performed on associations - i.e. subCriteria, and need to be handled on a case by case basis.
157          */
158         if (definingQuery instanceof MembershipQuery)
159         {
160             return addMembershipSearchCriteria(q, baseCriteria);
161         }
162 
163         Junction junction = identifyAndOrJunction(q);
164         baseCriteria.add(junction);
165         Iterator iter = q.getQueries().iterator();
166 
167         while (iter.hasNext())
168         {
169             Query query = (Query) iter.next();
170 
171             if (query instanceof BooleanQuery)
172             {
173                 addSearchCriteria((BooleanQuery) query, definingQuery, baseCriteria);
174             }
175             else if (query instanceof TermQuery)
176             {
177                 junction.add(getQueryExpression((TermQuery) query));
178             }
179             else
180                 throw new EntityQueryException("Unknown query type: [" + query.getClass().getName() + "]");
181         }
182 
183         return baseCriteria;
184     }
185 
186     private Junction identifyAndOrJunction(BooleanQuery q)
187     {
188         Junction junction;
189         if (q.isAND())
190             junction = Expression.conjunction();
191         else
192             junction = Expression.disjunction();
193 
194         return junction;
195     }
196 
197     private Criteria addMembershipSearchCriteria(BooleanQuery q, Criteria baseCriteria) throws HibernateException
198     {
199         if (q instanceof GroupsOfUserTwoTermQuery)
200         {
201             addGroupsOfUserSearchCriteria(q, baseCriteria);
202         }
203         else if (q instanceof GroupsOfExternalEntityTwoTermQuery)
204         {
205             addGroupsOfExternalEntitySearchCriteria(q, baseCriteria);
206         }
207 
208         return baseCriteria;
209     }
210 
211     private void addGroupsOfUserSearchCriteria(BooleanQuery q, Criteria baseCriteria) throws HibernateException
212     {
213         UserNameTermQuery userNameQuery = ((GroupsOfUserTwoTermQuery) q).getUserNameTermQuery();
214         GroupNameTermQuery groupNameQuery = ((GroupsOfUserTwoTermQuery) q).getGroupNameTermQuery();
215 
216         if (groupNameQuery.getTerm().equals(TermQuery.WILDCARD))
217         {
218             //do nothing, there is no specific group query and we defer to the username term below
219         }
220         else if (groupNameQuery.isMatchingSubstring())
221         {
222             baseCriteria.add(getLikeExpression("name", groupNameQuery, false));
223         }
224         else
225         {
226             baseCriteria.add(new EqExpression("name", groupNameQuery.getTerm(), false));
227         }
228 
229         if (userNameQuery.isMatchingSubstring())
230         {
231             baseCriteria.createCriteria("localMembers").add(getLikeExpression("name", userNameQuery, false));
232         }
233         else
234         {
235             baseCriteria.createCriteria("localMembers").add(new EqExpression("name", userNameQuery.getTerm(), false));
236         }
237     }
238 
239     private Criterion getLikeExpression(String entityAttribute, TermQuery termQuery, boolean caseInsensitive)
240     {
241         if (caseInsensitive)
242             return Expression.ilike(entityAttribute, termQuery.getTerm(), getMatchMode(termQuery.getMatchingRule()));
243         else
244             return Expression.like(entityAttribute, termQuery.getTerm(), getMatchMode(termQuery.getMatchingRule()));
245     }
246 
247     private void addGroupsOfExternalEntitySearchCriteria(BooleanQuery q, Criteria baseCriteria)
248         throws HibernateException
249     {
250         ExternalEntityNameTermQuery nameQuery =
251             ((GroupsOfExternalEntityTwoTermQuery) q).getExternalEntityNameTermQuery();
252         GroupNameTermQuery groupQuery = ((GroupsOfExternalEntityTwoTermQuery) q).getGroupNameTermQuery();
253 
254         if (groupQuery.getTerm().equals(TermQuery.WILDCARD))
255         {
256             //do nothing, there is no specific group query and we defer to the username term below
257         }
258         else if (groupQuery.isMatchingSubstring())
259         {
260             baseCriteria.add(getLikeExpression("name", groupQuery, false));
261         }
262         else
263         {
264             baseCriteria.add(new EqExpression("name", groupQuery.getTerm(), false));
265         }
266 
267         if (nameQuery.isMatchingSubstring())
268         {
269             baseCriteria.createCriteria("externalMembers").add(getLikeExpression("name", nameQuery, false));
270         }
271         else
272         {
273             baseCriteria.createCriteria("externalMembers").add(new EqExpression("name", nameQuery.getTerm(), false));
274         }
275     }
276 
277     private Criteria addSearchCriteria(TermQuery q, Criteria baseCriteria)
278     {
279         Criterion expression = getQueryExpression(q);
280         baseCriteria.add(expression);
281         return baseCriteria;
282     }
283 
284     private Criterion getQueryExpression(TermQuery termQuery)
285     {
286         String hqlField = identifyProperty(termQuery);
287 
288         if (termQuery.isMatchingSubstring())
289         {
290             return getLikeExpression(hqlField, termQuery, true);
291         }
292         else
293         {
294             return new EqExpression(hqlField, termQuery.getTerm(), true);
295         }
296     }
297 
298     private Criteria getBaseCriteria(Query query, Session session)
299     {
300         Criteria baseCriteria = null;
301 
302         if (query instanceof UserQuery)
303         {
304             baseCriteria = session.createCriteria(DefaultHibernateUser.class);
305         }
306         else if (query instanceof GroupQuery)
307         {
308             baseCriteria = session.createCriteria(DefaultHibernateGroup.class);
309         }
310         else if (query instanceof UsersInGroupTwoTermQuery)
311         {
312             baseCriteria = session.createCriteria(DefaultHibernateUser.class);
313         }
314         else if (query instanceof GroupsOfUserTwoTermQuery || query instanceof GroupsOfExternalEntityTwoTermQuery)
315         {
316             baseCriteria = session.createCriteria(DefaultHibernateGroup.class);
317         }
318         else if (query instanceof ExternalEntitiesInGroupTwoTermQuery)
319         {
320             baseCriteria = session.createCriteria(DefaultHibernateExternalEntity.class);
321         }
322 
323         return baseCriteria;
324     }
325 }