Java tutorial
/* * Weblounge: Web Content Management System * Copyright (c) 2003 - 2011 The Weblounge Team * http://entwinemedia.com/weblounge * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package ch.entwine.weblounge.contentrepository.impl.index.elasticsearch; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.FULLTEXT_FUZZY; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.TEXT_FUZZY; import static ch.entwine.weblounge.common.content.SearchQuery.Quantifier.All; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.ALTERNATE_VERSION; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.CONTENT_EXTERNAL_REPRESENTATION; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.CONTENT_FILENAME; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.CONTENT_MIMETYPE; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.CONTENT_SOURCE; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.CREATED; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.CREATED_BY; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.FULLTEXT; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.LOCKED_BY; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.MODIFIED; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.MODIFIED_BY; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.PAGELET_PROPERTIES; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.PAGELET_TYPE; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.PAGELET_TYPE_COMPOSER; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.PAGELET_TYPE_COMPOSER_POSITION; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.PAGELET_TYPE_POSITION; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.PATH; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.PATH_PREFIX; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.PUBLISHED_BY; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.PUBLISHED_FROM; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.SERIES; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.STATIONARY; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.SUBJECT; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.TEMPLATE; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.TEXT; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.TYPE; import static ch.entwine.weblounge.contentrepository.impl.index.IndexSchema.VERSION; import ch.entwine.weblounge.common.content.SearchQuery; import ch.entwine.weblounge.common.content.SearchQuery.Quantifier; import ch.entwine.weblounge.common.content.SearchTerms; import ch.entwine.weblounge.common.content.page.Pagelet; import ch.entwine.weblounge.common.content.page.PageletURI; import ch.entwine.weblounge.common.impl.content.SearchQueryImpl; import ch.entwine.weblounge.common.security.User; import ch.entwine.weblounge.contentrepository.impl.index.IndexSchema; import ch.entwine.weblounge.contentrepository.impl.index.IndexUtils; import org.apache.commons.lang.StringUtils; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.FilterBuilder; import org.elasticsearch.index.query.FilterBuilders; import org.elasticsearch.index.query.FuzzyLikeThisQueryBuilder; import org.elasticsearch.index.query.MatchAllQueryBuilder; import org.elasticsearch.index.query.MatchQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilderException; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.RangeQueryBuilder; import org.elasticsearch.index.query.TermsQueryBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * Weblounge implementation of the elastic search query builder. */ public class ElasticSearchSearchQuery implements QueryBuilder { /** The logging facility */ private static final Logger logger = LoggerFactory.getLogger(ElasticSearchSearchQuery.class); /** Term queries on fields */ private Map<String, Set<Object>> searchTerms = null; /** Negative term queries on fields */ private Map<String, Set<Object>> negativeSearchTerms = null; /** Fields that must be empty */ private Set<String> emptyFields = null; /** Fields that need to match all values */ private List<ValueGroup> groups = null; /** Fields that must not be empty */ private Set<String> nonEmptyFields = null; /** Fields that query a date range */ private Set<DateRange> dateRanges = null; /** Filter expression */ private String filter = null; /** Text query */ private String text = null; /** Fuzzy text query */ private String fuzzyText = null; /** Fulltext query */ private String fulltext = null; /** Fuzzy fulltext query */ private String fuzzyFulltext = null; /** True if more recent documents should be boosted */ private boolean recencyBoost = false; /** The boolean query */ private QueryBuilder queryBuilder = null; /** * Creates a new elastic search query based on the Weblounge query. * * @param query * the weblounge query */ public ElasticSearchSearchQuery(SearchQuery query) { init(query); createQuery(); } /** * {@inheritDoc} * * @see org.elasticsearch.index.query.BaseQueryBuilder#doXContent(org.elasticsearch.common.xcontent.XContentBuilder, * org.elasticsearch.common.xcontent.ToXContent.Params) */ protected void init(SearchQuery query) { // Resource id if (query.getIdentifier().length > 0) { and(IndexSchema.RESOURCE_ID, query.getIdentifier(), true); } // Version / Preferred version if (query.getPreferredVersion() >= 0) { andNot(ALTERNATE_VERSION, Long.toString(query.getPreferredVersion()), false); } else if (query.getVersion() >= 0) { and(VERSION, Long.toString(query.getVersion()), false); } // Path if (query.getPath() != null) { and(PATH, query.getPath(), true); } if (query.getPathPrefix() != null) { and(PATH_PREFIX, query.getPathPrefix(), true); } // Type if (query.getTypes().length > 0) { and(TYPE, query.getTypes(), true); } // Without type if (query.getWithoutTypes().length > 0) { andNot(TYPE, query.getWithoutTypes(), true); } // Subjects if (query.getSubjects() != null) { for (SearchTerms<String> terms : query.getSubjects()) { for (String subject : terms.getTerms()) { and(SUBJECT, subject, true); } if (Quantifier.All.equals(terms.getQuantifier())) { if (groups == null) groups = new ArrayList<ValueGroup>(); groups.add( new ValueGroup(SUBJECT, (Object[]) terms.getTerms().toArray(new String[terms.size()]))); } } } // Series if (query.getSeries().length > 0) { and(SERIES, query.getSeries(), true); } // Template if (query.getTemplate() != null) { and(TEMPLATE, query.getTemplate(), true); } // Stationary if (query.isStationary()) { and(STATIONARY, Boolean.TRUE.toString(), false); } // Creator if (query.getCreator() != null) { and(CREATED_BY, IndexUtils.serializeUserId(query.getCreator()), true); } // Creation date if (query.getCreationDate() != null) { if (query.getCreationDateEnd() != null) and(CREATED, query.getCreationDate(), query.getCreationDateEnd()); else and(CREATED, IndexUtils.beginningOfDay(query.getCreationDate()), IndexUtils.endOfDay(query.getCreationDate())); } // Modifier if (query.getModifier() != null) { and(MODIFIED_BY, IndexUtils.serializeUserId(query.getModifier()), true); } // Modification date if (query.getModificationDate() != null) { if (query.getCreationDateEnd() != null) and(MODIFIED, query.getModificationDate(), query.getModificationDateEnd()); else { and(MODIFIED, IndexUtils.beginningOfDay(query.getModificationDate()), IndexUtils.endOfDay(query.getModificationDate())); } } // Without Modification if (query.getWithoutModification()) { andEmpty(MODIFIED); } // Publisher if (query.getPublisher() != null) { and(PUBLISHED_BY, IndexUtils.serializeUserId(query.getPublisher()), true); } // Publication date if (query.getPublishingDate() != null) { if (query.getCreationDateEnd() != null) and(PUBLISHED_FROM, query.getPublishingDate(), query.getPublishingDateEnd()); else and(PUBLISHED_FROM, IndexUtils.beginningOfDay(query.getPublishingDate()), IndexUtils.endOfDay(query.getPublishingDate())); } // Without Publication if (query.getWithoutPublication()) { andEmpty(PUBLISHED_FROM); } // Lock owner if (query.getLockOwner() != null) { User user = query.getLockOwner(); if (SearchQueryImpl.ANY_USER.equals(user.getLogin())) andNotEmpty(LOCKED_BY); else and(LOCKED_BY, IndexUtils.serializeUserId(user), true); } // Pagelet elements // for (Map.Entry<String, String> entry : query.getElements().entrySet()) { // TODO: Language? // solrQuery.append(" ").append(PAGELET_CONTENTS).append(":"); // solrQuery.append("\"").append(entry.getKey()).append("=\""); // for (String contentValue : StringUtils.split(entry.getValue())) { // solrQuery.append(" \"").append(IndexUtils.clean(contentValue)).append("\""); // } // } // Pagelet properties for (Map.Entry<String, String> entry : query.getProperties().entrySet()) { StringBuffer searchTerm = new StringBuffer(); searchTerm.append(entry.getKey()); searchTerm.append("=").append(entry.getValue()); and(PAGELET_PROPERTIES, searchTerm.toString(), true); } // Pagelet types if (query.getPagelets() != null) { for (SearchTerms<Pagelet> terms : query.getPagelets()) { String field = null; for (Pagelet pagelet : terms.getTerms()) { String oldField = field; StringBuffer searchTerm = new StringBuffer(); searchTerm.append(pagelet.getModule()).append("/").append(pagelet.getIdentifier()); // Are we looking for the pagelet in a certain composer or position? PageletURI uri = pagelet.getURI(); if (uri != null) { if (StringUtils.isNotBlank(uri.getComposer()) && uri.getPosition() >= 0) { field = MessageFormat.format(PAGELET_TYPE_COMPOSER_POSITION, uri.getComposer(), uri.getPosition()); and(field, searchTerm.toString(), true); } else if (StringUtils.isNotBlank(uri.getComposer())) { field = MessageFormat.format(PAGELET_TYPE_COMPOSER, uri.getComposer()); and(field, searchTerm.toString(), true); } else if (uri.getPosition() >= 0) { field = MessageFormat.format(PAGELET_TYPE_POSITION, uri.getPosition()); and(field, searchTerm.toString(), true); } else { field = PAGELET_TYPE; and(field, searchTerm.toString(), true); } } else { field = PAGELET_TYPE; and(field, searchTerm.toString(), true); } if (All.equals(terms.getQuantifier()) && oldField != null && !oldField.equals(field)) { logger.warn( "Queries based on pagelets and the 'all' quantifier need to use the same field"); } } // Add filters to support AND queries if (All.equals(terms.getQuantifier())) { if (groups == null) groups = new ArrayList<ValueGroup>(); List<String> pagelets = new ArrayList<String>(terms.size()); for (Pagelet p : terms.getTerms()) pagelets.add(p.toString()); groups.add(new ValueGroup(field, (Object[]) pagelets.toArray(new String[pagelets.size()]))); } } } // Content filenames if (query.getFilename() != null) { and(CONTENT_FILENAME, query.getFilename(), true); } // Content source if (query.getSource() != null) { and(CONTENT_SOURCE, query.getSource(), true); } // Content external location if (query.getExternalLocation() != null) { and(CONTENT_EXTERNAL_REPRESENTATION, query.getExternalLocation().toExternalForm(), true); } // Content mime types if (query.getMimetype() != null) { and(CONTENT_MIMETYPE, query.getMimetype(), true); } // Fulltext if (query.getFulltext() != null) { for (SearchTerms<String> terms : query.getFulltext()) { StringBuffer queryText = new StringBuffer(); for (String term : terms.getTerms()) { if (queryText.length() > 0) queryText.append(" "); queryText.append(term); } if (query.isFuzzySearch()) fuzzyFulltext = queryText.toString(); else fulltext = queryText.toString(); if (All.equals(terms.getQuantifier())) { if (groups == null) groups = new ArrayList<ValueGroup>(); if (query.isFuzzySearch()) { logger.warn("All quantifier not supported in conjunction with wildcard fulltext"); } groups.add(new ValueGroup(FULLTEXT, (Object[]) terms.getTerms().toArray(new String[terms.size()]))); } } } // Text if (query.getTerms() != null) { for (SearchTerms<String> terms : query.getTerms()) { StringBuffer queryText = new StringBuffer(); for (String term : terms.getTerms()) { if (queryText.length() > 0) queryText.append(" "); queryText.append(term); } if (query.isFuzzySearch()) fuzzyText = queryText.toString(); else this.text = queryText.toString(); if (All.equals(terms.getQuantifier())) { if (groups == null) groups = new ArrayList<ValueGroup>(); if (query.isFuzzySearch()) { logger.warn("All quantifier not supported in conjunction with wildcard text"); } groups.add(new ValueGroup(TEXT, (Object[]) terms.getTerms().toArray(new String[terms.size()]))); } } } // Filter query if (query.getFilter() != null) { this.filter = query.getFilter(); } // Recency boost this.recencyBoost = query.getRecencyPriority(); } /** * Create the actual query. We start with a query that matches everything, * then move to the boolean conditions, finally add filter queries. */ private void createQuery() { queryBuilder = new MatchAllQueryBuilder(); // The boolean query builder BoolQueryBuilder booleanQuery = new BoolQueryBuilder(); // Terms if (searchTerms != null) { for (Map.Entry<String, Set<Object>> entry : searchTerms.entrySet()) { Set<Object> values = entry.getValue(); if (values.size() == 1) booleanQuery.must(new TermsQueryBuilder(entry.getKey(), values.iterator().next())); else booleanQuery .must(new TermsQueryBuilder(entry.getKey(), values.toArray(new String[values.size()]))); } this.queryBuilder = booleanQuery; } // Negative terms if (negativeSearchTerms != null) { for (Map.Entry<String, Set<Object>> entry : negativeSearchTerms.entrySet()) { Set<Object> values = entry.getValue(); if (values.size() == 1) booleanQuery.mustNot(new TermsQueryBuilder(entry.getKey(), values.iterator().next())); else booleanQuery.mustNot( new TermsQueryBuilder(entry.getKey(), values.toArray(new String[values.size()]))); } this.queryBuilder = booleanQuery; } // Date ranges if (dateRanges != null) { for (DateRange dr : dateRanges) { booleanQuery.must(dr.getQueryBuilder()); } this.queryBuilder = booleanQuery; } // Text if (text != null) { MatchQueryBuilder textQueryBuilder = QueryBuilders.matchQuery(TEXT, text); booleanQuery.must(textQueryBuilder); this.queryBuilder = booleanQuery; } // Fuzzy text if (fuzzyText != null) { FuzzyLikeThisQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyLikeThisQuery(TEXT_FUZZY) .likeText(fuzzyText); booleanQuery.must(fuzzyQueryBuilder); this.queryBuilder = booleanQuery; } // Fulltext if (fulltext != null) { MatchQueryBuilder textQueryBuilder = QueryBuilders.matchQuery(FULLTEXT, fulltext); booleanQuery.must(textQueryBuilder); this.queryBuilder = booleanQuery; } // Fuzzy fulltext if (fuzzyFulltext != null) { FuzzyLikeThisQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyLikeThisQuery(FULLTEXT_FUZZY) .likeText(fuzzyFulltext); booleanQuery.must(fuzzyQueryBuilder); this.queryBuilder = booleanQuery; } // Recency boost. We differentiate between various (random) levels of // recency if (recencyBoost) { Calendar date = Calendar.getInstance(); // Last week RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(MODIFIED); date.add(Calendar.WEEK_OF_YEAR, -1); rangeQueryBuilder.gt(date.getTimeInMillis()).boost(10); booleanQuery.should(rangeQueryBuilder); // Last month rangeQueryBuilder = QueryBuilders.rangeQuery(MODIFIED); date.add(Calendar.WEEK_OF_YEAR, -3); rangeQueryBuilder.gt(date.getTimeInMillis()).boost(5); booleanQuery.should(rangeQueryBuilder); // Last year rangeQueryBuilder = QueryBuilders.rangeQuery(MODIFIED); date.add(Calendar.MONTH, -5); rangeQueryBuilder.gt(date.getTimeInMillis()).boost(2); booleanQuery.should(rangeQueryBuilder); } QueryBuilder unfilteredQuery = queryBuilder; List<FilterBuilder> filters = new ArrayList<FilterBuilder>(); // Add filtering for AND terms if (groups != null) { for (ValueGroup group : groups) { filters.addAll(group.getFilterBuilders()); } } // Non-Empty fields if (nonEmptyFields != null) { for (String field : nonEmptyFields) { filters.add(FilterBuilders.existsFilter(field)); } } // Empty fields if (emptyFields != null) { for (String field : emptyFields) { filters.add(FilterBuilders.missingFilter(field)); } } // Filter expressions if (filter != null) { filters.add(FilterBuilders.termFilter(FULLTEXT, filter)); } // Apply the filters if (filters.size() == 1) { this.queryBuilder = QueryBuilders.filteredQuery(unfilteredQuery, filters.get(0)); } else if (filters.size() > 1) { FilterBuilder andFilter = FilterBuilders.andFilter(filters.toArray(new FilterBuilder[filters.size()])); this.queryBuilder = QueryBuilders.filteredQuery(unfilteredQuery, andFilter); } } /** * Stores <code>fieldValue</code> as a search term on the * <code>fieldName</code> field. * * @param fieldName * the field name * @param fieldValue * the field value * @param clean * <code>true</code> to escape solr special characters in the field * value */ protected void and(String fieldName, Object fieldValue, boolean clean) { // Fix the field name, just in case fieldName = StringUtils.trim(fieldName); // Make sure the data structures are set up accordingly if (searchTerms == null) searchTerms = new HashMap<String, Set<Object>>(); Set<Object> termValues = searchTerms.get(fieldName); if (termValues == null) { termValues = new HashSet<Object>(); searchTerms.put(fieldName, termValues); } // Add the term termValues.add(fieldValue); } /** * Stores <code>fieldValues</code> as search terms on the * <code>fieldName</code> field. * * @param fieldName * the field name * @param fieldValues * the field value * @param clean * <code>true</code> to escape solr special characters in the field * value */ protected void and(String fieldName, Object[] fieldValues, boolean clean) { for (Object v : fieldValues) { and(fieldName, v, clean); } } /** * Stores <code>fieldValue</code> as a search term on the * <code>fieldName</code> field. * * @param fieldName * the field name * @param startDate * the start date * @param endDate * the end date */ protected void and(String fieldName, Date startDate, Date endDate) { // Fix the field name, just in case fieldName = StringUtils.trim(fieldName); // Make sure the data structures are set up accordingly if (dateRanges == null) dateRanges = new HashSet<DateRange>(); // Add the term DateRange dateRange = new DateRange(fieldName, startDate, endDate); dateRanges.add(dateRange); } /** * Stores <code>fieldValue</code> as a negative search term on the * <code>fieldName</code> field. * * @param fieldName * the field name * @param fieldValue * the field value * @param clean * <code>true</code> to escape solr special characters in the field * value */ protected void andNot(String fieldName, Object fieldValue, boolean clean) { // Fix the field name, just in case fieldName = StringUtils.trim(fieldName); // Make sure the data structures are set up accordingly if (negativeSearchTerms == null) negativeSearchTerms = new HashMap<String, Set<Object>>(); Set<Object> termValues = negativeSearchTerms.get(fieldName); if (termValues == null) { termValues = new HashSet<Object>(); negativeSearchTerms.put(fieldName, termValues); } // Add the term termValues.add(fieldValue); } /** * Stores <code>fieldValues</code> as negative search terms on the * <code>fieldName</code> field. * * @param fieldName * the field name * @param fieldValues * the field value * @param clean * <code>true</code> to escape solr special characters in the field * value */ protected void andNot(String fieldName, Object[] fieldValues, boolean clean) { for (Object v : fieldValues) { andNot(fieldName, v, clean); } } /** * Encodes the field name as part of the AND clause of a solr query: * <tt>AND -fieldName : [* TO *]</tt>. * * @param fieldName * the field name */ protected void andEmpty(String fieldName) { if (emptyFields == null) emptyFields = new HashSet<String>(); emptyFields.add(StringUtils.trim(fieldName)); } /** * Encodes the field name as part of the AND clause of a solr query: * <tt>AND fieldName : ["" TO *]</tt>. * * @param fieldName * the field name */ protected void andNotEmpty(String fieldName) { if (nonEmptyFields == null) nonEmptyFields = new HashSet<String>(); nonEmptyFields.add(StringUtils.trim(fieldName)); } /** * {@inheritDoc} * * @see org.elasticsearch.common.xcontent.ToXContent#toXContent(org.elasticsearch.common.xcontent.XContentBuilder, * org.elasticsearch.common.xcontent.ToXContent.Params) */ @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { return queryBuilder.toXContent(builder, params); } /** * {@inheritDoc} * * @see org.elasticsearch.index.query.QueryBuilder#buildAsBytes() */ @Override public BytesReference buildAsBytes() throws QueryBuilderException { return queryBuilder.buildAsBytes(); } /** * {@inheritDoc} * * @see org.elasticsearch.index.query.QueryBuilder#buildAsBytes(org.elasticsearch.common.xcontent.XContentType) */ @Override public BytesReference buildAsBytes(XContentType contentType) { return queryBuilder.buildAsBytes(contentType); } /** * Utility class to hold date range specifications and turn them into elastic * search queries. */ private static final class DateRange { /** The field name */ private String field = null; /** The start date */ private Date startDate = null; /** The end date */ private Date endDate = null; /** * Creates a new date range specification with the given field name, start * and end dates. <code>null</code> may be passed in for start or end dates * that should remain unspecified. * * @param field * the field name * @param start * the start date * @param end * the end date */ DateRange(String field, Date start, Date end) { this.field = field; this.startDate = start; this.endDate = end; } /** * Returns the range query that is represented by this date range. * * @return the range query builder */ QueryBuilder getQueryBuilder() { RangeQueryBuilder rqb = new RangeQueryBuilder(field); if (startDate != null) rqb.from(startDate.getTime()); if (endDate != null) rqb.to(endDate.getTime()); return rqb; } /** * {@inheritDoc} * * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (obj instanceof DateRange) { return ((DateRange) obj).field.equals(field); } return false; } /** * {@inheritDoc} * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return field.hashCode(); } } /** * Stores a group of values which will later be added to the query using AND. */ private static final class ValueGroup { /** The field name */ private String field = null; /** The values to store */ private Object[] values = null; /** * Creates a new value group for the given field and values. * * @param field * the field name * @param values * the values */ ValueGroup(String field, Object... values) { this.field = field; this.values = values; } /** * Returns the filter that will make sure only documents are returned that * match all of the values at once. * * @return the filter builder */ List<FilterBuilder> getFilterBuilders() { List<FilterBuilder> filters = new ArrayList<FilterBuilder>(values.length); for (Object v : values) { filters.add(FilterBuilders.termFilter(field, v.toString())); } return filters; } /** * {@inheritDoc} * * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (obj instanceof DateRange) { return ((DateRange) obj).field.equals(field); } return false; } /** * {@inheritDoc} * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return field.hashCode(); } } }