james-server-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From btell...@apache.org
Subject svn commit: r1688120 - in /james/mailbox/trunk/elasticsearch: ./ src/main/java/org/apache/james/mailbox/elasticsearch/query/ src/test/java/org/apache/james/mailbox/elasticsearch/query/
Date Mon, 29 Jun 2015 08:28:37 GMT
Author: btellier
Date: Mon Jun 29 08:28:36 2015
New Revision: 1688120

URL: http://svn.apache.org/r1688120
Log:
MAILBOX-235 Conversions between James queries and ElasticSearch queries - many thanks to Matthieu
Baechlor and Antoine Duprat for their reviews

Added:
    james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/CriterionConverter.java
    james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/FilteredQueryCollector.java
    james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/FilteredQueryRepresentation.java
    james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/QueryConverter.java
    james/mailbox/trunk/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/query/FilteredQueryCollectorTest.java
Modified:
    james/mailbox/trunk/elasticsearch/pom.xml

Modified: james/mailbox/trunk/elasticsearch/pom.xml
URL: http://svn.apache.org/viewvc/james/mailbox/trunk/elasticsearch/pom.xml?rev=1688120&r1=1688119&r2=1688120&view=diff
==============================================================================
--- james/mailbox/trunk/elasticsearch/pom.xml (original)
+++ james/mailbox/trunk/elasticsearch/pom.xml Mon Jun 29 08:28:36 2015
@@ -95,6 +95,7 @@
         <dependency>
             <groupId>org.assertj</groupId>
             <artifactId>assertj-core</artifactId>
+            <version>3.0.0</version>
             <scope>test</scope>
         </dependency>
         <dependency>

Added: james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/CriterionConverter.java
URL: http://svn.apache.org/viewvc/james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/CriterionConverter.java?rev=1688120&view=auto
==============================================================================
--- james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/CriterionConverter.java
(added)
+++ james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/CriterionConverter.java
Mon Jun 29 08:28:36 2015
@@ -0,0 +1,246 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.elasticsearch.query;
+
+import org.apache.james.mailbox.elasticsearch.json.HeaderCollection;
+import org.apache.james.mailbox.elasticsearch.json.JsonMessageConstants;
+import org.apache.james.mailbox.model.SearchQuery;
+import org.apache.james.mailbox.model.SearchQuery.Criterion;
+import org.apache.james.mailbox.model.SearchQuery.HeaderOperator;
+
+import javax.mail.Flags;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+import static org.elasticsearch.index.query.FilterBuilders.existsFilter;
+import static org.elasticsearch.index.query.FilterBuilders.rangeFilter;
+import static org.elasticsearch.index.query.FilterBuilders.termFilter;
+import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
+import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
+import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
+import static org.elasticsearch.index.query.QueryBuilders.nestedQuery;
+import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;
+
+public class CriterionConverter {
+
+    private Map<Class<?>, Function<SearchQuery.Criterion, FilteredQueryRepresentation>>
criterionConverterMap;
+    private Map<Class<?>, BiFunction<String, SearchQuery.HeaderOperator, FilteredQueryRepresentation>>
headerOperatorConverterMap;
+
+    public CriterionConverter() {
+        criterionConverterMap = new HashMap<>();
+        headerOperatorConverterMap = new HashMap<>();
+        
+        registerCriterionConverters();
+        registerHeaderOperatorConverters();
+    }
+
+    private void registerCriterionConverters() {
+        registerCriterionConverter(SearchQuery.FlagCriterion.class, this::convertFlag);
+        registerCriterionConverter(SearchQuery.UidCriterion.class, this::convertUid);
+        registerCriterionConverter(SearchQuery.ConjunctionCriterion.class, this::convertConjunction);
+        registerCriterionConverter(SearchQuery.InternalDateCriterion.class, this::convertInternalDate);
+        registerCriterionConverter(SearchQuery.HeaderCriterion.class, this::convertHeader);
+        
+        registerCriterionConverter(
+            SearchQuery.AllCriterion.class, 
+            criterion -> FilteredQueryRepresentation.fromQuery(matchAllQuery()));
+        
+        registerCriterionConverter(
+            SearchQuery.TextCriterion.class,
+            criterion -> FilteredQueryRepresentation.fromQuery(
+                    matchQuery(JsonMessageConstants.TEXT_BODY, criterion.getOperator().getValue())));
+        
+        registerCriterionConverter(
+            SearchQuery.ModSeqCriterion.class,
+            criterion -> createNumericFilter(JsonMessageConstants.MODSEQ, criterion.getOperator()));
+        
+        registerCriterionConverter(
+            SearchQuery.SizeCriterion.class,
+            criterion -> createNumericFilter(JsonMessageConstants.SIZE, criterion.getOperator()));
+        
+        registerCriterionConverter(
+            SearchQuery.CustomFlagCriterion.class,
+            criterion -> FilteredQueryRepresentation.fromFilter(
+                    termFilter(JsonMessageConstants.USER_FLAGS, criterion.getFlag())));
+    }
+    
+    @SuppressWarnings("unchecked")
+    private <T extends Criterion> void registerCriterionConverter(Class<T> type,
Function<T, FilteredQueryRepresentation> f) {
+        criterionConverterMap.put(type, (Function<Criterion, FilteredQueryRepresentation>)
f);
+    }
+    
+    private void registerHeaderOperatorConverters() {
+
+        registerHeaderOperatorConverter(
+            SearchQuery.ExistsOperator.class,
+            (headerName, operator) -> FilteredQueryRepresentation.fromFilter(
+                existsFilter(JsonMessageConstants.HEADERS + "." + headerName))
+        );
+        
+        registerHeaderOperatorConverter(
+            SearchQuery.AddressOperator.class,
+            (headerName, operator) -> manageAddressFields(headerName, operator.getAddress()));
+        
+        registerHeaderOperatorConverter(
+            SearchQuery.DateOperator.class,
+            (headerName, operator) -> dateRangeFilter(JsonMessageConstants.SENT_DATE,
operator));
+        
+        registerHeaderOperatorConverter(
+            SearchQuery.ContainsOperator.class,
+            (headerName, operator) -> FilteredQueryRepresentation.fromQuery(
+                matchQuery(JsonMessageConstants.HEADERS + "." + headerName,
+                    operator.getValue())));
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T extends HeaderOperator> void registerHeaderOperatorConverter(Class<T>
type, BiFunction<String, T, FilteredQueryRepresentation> f) {
+        headerOperatorConverterMap.put(type, (BiFunction<String, HeaderOperator, FilteredQueryRepresentation>)
f);
+    }
+
+    public FilteredQueryRepresentation convertCriterion(SearchQuery.Criterion criterion)
{
+        return criterionConverterMap.get(criterion.getClass()).apply(criterion);
+    }
+
+    private FilteredQueryRepresentation convertInternalDate(SearchQuery.InternalDateCriterion
dateCriterion) {
+        SearchQuery.DateOperator dateOperator = dateCriterion.getOperator();
+        return dateRangeFilter(JsonMessageConstants.DATE, dateOperator);
+    }
+
+    private FilteredQueryRepresentation dateRangeFilter(String field, SearchQuery.DateOperator
dateOperator) {
+        SearchQuery.DateResolution dateResolution = dateOperator.getDateResultion();
+        String lowDateString = ISO_OFFSET_DATE_TIME.format(DateResolutionFormater.computeLowerDate(DateResolutionFormater.convertDateToZonedDateTime(dateOperator.getDate()),
dateResolution));
+        String upDateString = ISO_OFFSET_DATE_TIME.format(
+            DateResolutionFormater.computeUpperDate(
+                DateResolutionFormater.convertDateToZonedDateTime(dateOperator.getDate()),
+                dateResolution));
+        return convertDateOperatorToFiteredQuery(field, dateOperator, lowDateString, upDateString);
+    }
+
+    private FilteredQueryRepresentation convertConjunction(SearchQuery.ConjunctionCriterion
criterion) {
+        return criterion.getCriteria().stream()
+            .map(this::convertCriterion)
+            .collect(new FilteredQueryCollector(criterion.getType()));
+    }
+
+    private FilteredQueryRepresentation convertFlag(SearchQuery.FlagCriterion flagCriterion)
{
+        SearchQuery.BooleanOperator operator = flagCriterion.getOperator();
+        Flags.Flag flag = flagCriterion.getFlag();
+        if (flag.equals(Flags.Flag.DELETED) ) {
+            return FilteredQueryRepresentation.fromFilter(termFilter(JsonMessageConstants.IS_DELETED,
operator.isSet()));
+        }
+        if (flag.equals(Flags.Flag.ANSWERED) ) {
+            return FilteredQueryRepresentation.fromFilter(
+                termFilter(JsonMessageConstants.IS_ANSWERED, operator.isSet()));
+        }
+        if (flag.equals(Flags.Flag.DRAFT) ) {
+            return FilteredQueryRepresentation.fromFilter(
+                termFilter(JsonMessageConstants.IS_DRAFT, operator.isSet()));
+        }
+        if (flag.equals(Flags.Flag.SEEN) ) {
+            return FilteredQueryRepresentation.fromFilter(
+                termFilter(JsonMessageConstants.IS_UNREAD, !operator.isSet()));
+        }
+        if (flag.equals(Flags.Flag.RECENT) ) {
+            return FilteredQueryRepresentation.fromFilter(
+                termFilter(JsonMessageConstants.IS_RECENT, operator.isSet()));
+        }
+        if (flag.equals(Flags.Flag.FLAGGED) ) {
+            return FilteredQueryRepresentation.fromFilter(
+                termFilter(JsonMessageConstants.IS_FLAGGED, operator.isSet()));
+        }
+        throw new RuntimeException("Unknown flag used in Flag search criterion");
+    }
+
+    private FilteredQueryRepresentation createNumericFilter(String fieldName, SearchQuery.NumericOperator
operator) {
+        switch (operator.getType()) {
+        case EQUALS:
+            return FilteredQueryRepresentation.fromFilter(
+                rangeFilter(fieldName).gte(operator.getValue()).lte(operator.getValue()));
+        case GREATER_THAN:
+            return FilteredQueryRepresentation.fromFilter(rangeFilter(fieldName).gte(operator.getValue()));
+        case LESS_THAN:
+            return FilteredQueryRepresentation.fromFilter(rangeFilter(fieldName).lte(operator.getValue()));
+        default:
+            throw new RuntimeException("A non existing numeric operator was triggered");
+        }
+    }
+
+    private FilteredQueryRepresentation convertUid(SearchQuery.UidCriterion uidCriterion)
{
+        if (uidCriterion.getOperator().getRange().length == 0) {
+            return FilteredQueryRepresentation.empty();
+        }
+        return Arrays.stream(uidCriterion.getOperator().getRange())
+            .map(this::uidRangeFilter)
+            .collect(new FilteredQueryCollector(SearchQuery.Conjunction.OR));
+    }
+
+    private FilteredQueryRepresentation uidRangeFilter(SearchQuery.NumericRange numericRange)
{
+        return FilteredQueryRepresentation.fromFilter(
+            rangeFilter(JsonMessageConstants.ID)
+                .lte(numericRange.getHighValue())
+                .gte(numericRange.getLowValue()));
+    }
+
+    private FilteredQueryRepresentation convertHeader(SearchQuery.HeaderCriterion headerCriterion)
{
+        return headerOperatorConverterMap.get(headerCriterion.getOperator().getClass())
+            .apply(
+                headerCriterion.getHeaderName().toLowerCase(),
+                headerCriterion.getOperator());
+    }
+
+    private FilteredQueryRepresentation manageAddressFields(String headerName, String value)
{
+        return FilteredQueryRepresentation.fromQuery(
+            nestedQuery(getFieldNameFromHeaderName(headerName), boolQuery().should(matchQuery(getFieldNameFromHeaderName(headerName)
+ "." + JsonMessageConstants.EMailer.NAME, value)).should(matchQuery(getFieldNameFromHeaderName(headerName)
+ "." + JsonMessageConstants.EMailer.ADDRESS, value))));
+    }
+
+    private String getFieldNameFromHeaderName(String headerName) {
+        switch (headerName.toLowerCase()) {
+        case HeaderCollection.TO:
+            return JsonMessageConstants.TO;
+        case HeaderCollection.CC:
+            return JsonMessageConstants.CC;
+        case HeaderCollection.BCC:
+            return JsonMessageConstants.BCC;
+        case HeaderCollection.FROM:
+            return JsonMessageConstants.FROM;
+        }
+        throw new RuntimeException("Header not recognized as Addess Header : " + headerName);
+    }
+
+    private FilteredQueryRepresentation convertDateOperatorToFiteredQuery(String field, SearchQuery.DateOperator
dateOperator, String lowDateString, String upDateString) {
+        switch (dateOperator.getType()) {
+        case BEFORE:
+            return FilteredQueryRepresentation.fromFilter(
+                rangeFilter(field).lte(upDateString));
+        case AFTER:
+            return FilteredQueryRepresentation.fromFilter(
+                rangeFilter(field).gte(lowDateString));
+        case ON:
+            return FilteredQueryRepresentation.fromFilter(
+                rangeFilter(field).lte(upDateString).gte(lowDateString));
+        }
+        throw new RuntimeException("Unknown date operator");
+    }
+
+}

Added: james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/FilteredQueryCollector.java
URL: http://svn.apache.org/viewvc/james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/FilteredQueryCollector.java?rev=1688120&view=auto
==============================================================================
--- james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/FilteredQueryCollector.java
(added)
+++ james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/FilteredQueryCollector.java
Mon Jun 29 08:28:36 2015
@@ -0,0 +1,123 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.elasticsearch.query;
+
+import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
+import static org.elasticsearch.index.query.FilterBuilders.boolFilter;
+
+import org.apache.james.mailbox.model.SearchQuery;
+import org.elasticsearch.index.query.BoolFilterBuilder;
+import org.elasticsearch.index.query.BoolQueryBuilder;
+
+import java.util.EnumSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.BinaryOperator;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collector;
+
+public class FilteredQueryCollector implements Collector<FilteredQueryRepresentation,
FilteredQueryRepresentation, FilteredQueryRepresentation> {
+
+    private final SearchQuery.Conjunction type;
+
+    public FilteredQueryCollector(SearchQuery.Conjunction type) {
+        this.type = type;
+    }
+
+    @Override
+    public Supplier<FilteredQueryRepresentation> supplier() {
+        return FilteredQueryRepresentation::empty;
+    }
+
+    @Override
+    public BiConsumer<FilteredQueryRepresentation, FilteredQueryRepresentation> accumulator()
{
+        return this::apply;
+    }
+
+    @Override
+    public BinaryOperator<FilteredQueryRepresentation> combiner() {
+        return this::apply;
+    }
+
+    @Override
+    public Function<FilteredQueryRepresentation, FilteredQueryRepresentation> finisher()
{
+        return (accumulator)->accumulator;
+    }
+
+    @Override
+    public Set<Characteristics> characteristics() {
+        return EnumSet.of(Characteristics.UNORDERED);
+    }
+
+    private FilteredQueryRepresentation apply(FilteredQueryRepresentation accumulator, FilteredQueryRepresentation
collected) {
+        initializeAccumulatorWhenNeeded(accumulator, collected);
+        switch (type) {
+        case OR:
+            return applyOr(accumulator, collected);
+        case AND:
+            return applyAnd(accumulator, collected);
+        case NOR:
+            return applyNor(accumulator, collected);
+        }
+        return accumulator;
+    }
+
+    private void initializeAccumulatorWhenNeeded(FilteredQueryRepresentation accumulator,
FilteredQueryRepresentation collected) {
+        // This method is compulsory because elasticSearch refuses to build empty BoolQuery
and empty BoolFilter
+        if (!accumulator.getQuery().isPresent() && collected.getQuery().isPresent())
{
+            accumulator.setQuery(Optional.of(boolQuery()));
+        }
+        if (!accumulator.getFilter().isPresent() && collected.getFilter().isPresent())
{
+            accumulator.setFilter(Optional.of(boolFilter()));
+        }
+    }
+
+    private FilteredQueryRepresentation applyNor(FilteredQueryRepresentation accumulator,
FilteredQueryRepresentation collected) {
+        if (collected.getQuery().isPresent()) {
+            ((BoolQueryBuilder) accumulator.getQuery().get()).mustNot(collected.getQuery().get());
+        }
+        if (collected.getFilter().isPresent()) {
+            ((BoolFilterBuilder) accumulator.getFilter().get()).mustNot(collected.getFilter().get());
+        }
+        return accumulator;
+    }
+
+    private FilteredQueryRepresentation applyAnd(FilteredQueryRepresentation accumulator,
FilteredQueryRepresentation collected) {
+        if (collected.getQuery().isPresent()) {
+            ((BoolQueryBuilder) accumulator.getQuery().get()).must(collected.getQuery().get());
+        }
+        if (collected.getFilter().isPresent()) {
+            ((BoolFilterBuilder) accumulator.getFilter().get()).must(collected.getFilter().get());
+        }
+        return accumulator;
+    }
+
+    private FilteredQueryRepresentation applyOr(FilteredQueryRepresentation accumulator,
FilteredQueryRepresentation collected) {
+        if (collected.getQuery().isPresent()) {
+            ((BoolQueryBuilder) accumulator.getQuery().get()).should(collected.getQuery().get());
+        }
+        if (collected.getFilter().isPresent()) {
+            ((BoolFilterBuilder) accumulator.getFilter().get()).should(collected.getFilter().get());
+        }
+        return accumulator;
+    }
+}

Added: james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/FilteredQueryRepresentation.java
URL: http://svn.apache.org/viewvc/james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/FilteredQueryRepresentation.java?rev=1688120&view=auto
==============================================================================
--- james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/FilteredQueryRepresentation.java
(added)
+++ james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/FilteredQueryRepresentation.java
Mon Jun 29 08:28:36 2015
@@ -0,0 +1,64 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.elasticsearch.query;
+
+import org.elasticsearch.index.query.FilterBuilder;
+import org.elasticsearch.index.query.QueryBuilder;
+
+import java.util.Optional;
+
+public  class FilteredQueryRepresentation {
+
+    public static FilteredQueryRepresentation fromQuery(QueryBuilder query) {
+        return new FilteredQueryRepresentation(Optional.of(query), Optional.empty());
+    }
+
+    public static FilteredQueryRepresentation fromFilter(FilterBuilder filter) {
+        return new FilteredQueryRepresentation(Optional.empty(), Optional.of(filter));
+    }
+
+    public static FilteredQueryRepresentation empty() {
+        return new FilteredQueryRepresentation(Optional.empty(), Optional.empty());
+    }
+
+    private Optional<FilterBuilder> filter;
+    private Optional<QueryBuilder> query;
+
+    private FilteredQueryRepresentation(Optional<QueryBuilder> query, Optional<FilterBuilder>
filter) {
+        this.query = query;
+        this.filter = filter;
+    }
+
+    public Optional<FilterBuilder> getFilter() {
+        return filter;
+    }
+
+    public Optional<QueryBuilder> getQuery() {
+        return query;
+    }
+
+    public void setFilter(Optional<FilterBuilder> filter) {
+        this.filter = filter;
+    }
+
+    public void setQuery(Optional<QueryBuilder> query) {
+        this.query = query;
+    }
+}

Added: james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/QueryConverter.java
URL: http://svn.apache.org/viewvc/james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/QueryConverter.java?rev=1688120&view=auto
==============================================================================
--- james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/QueryConverter.java
(added)
+++ james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/QueryConverter.java
Mon Jun 29 08:28:36 2015
@@ -0,0 +1,81 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.elasticsearch.query;
+
+import org.apache.james.mailbox.elasticsearch.json.JsonMessageConstants;
+import org.apache.james.mailbox.model.SearchQuery;
+import org.elasticsearch.common.lang3.tuple.Pair;
+import org.elasticsearch.index.query.QueryBuilder;
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import static org.elasticsearch.index.query.FilterBuilders.termFilter;
+import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
+import static org.elasticsearch.index.query.QueryBuilders.filteredQuery;
+
+public class QueryConverter implements Function<Pair<SearchQuery, String>, QueryBuilder>
{
+
+
+    private final CriterionConverter criterionConverter;
+
+    public QueryConverter(CriterionConverter criterionConverter) {
+        this.criterionConverter = criterionConverter;
+    }
+
+    @Override
+    public QueryBuilder apply(Pair<SearchQuery, String> pair) {
+        return from(pair.getLeft(), pair.getRight());
+    }
+
+    public QueryBuilder from(SearchQuery searchQuery, String mailboxUUID) {
+        return Stream.of(generateQueryBuilder(searchQuery))
+            .map((rep) -> addMailboxFilters(rep, mailboxUUID))
+            .map(this::getFinalQuery)
+            .findAny()
+            .get();
+    }
+
+    private FilteredQueryRepresentation generateQueryBuilder(SearchQuery searchQuery) {
+        List<SearchQuery.Criterion> criteria = searchQuery.getCriterias();
+        if (criteria.isEmpty()) {
+            return criterionConverter.convertCriterion(SearchQuery.all());
+        } else if (criteria.size() == 1) {
+            return criterionConverter.convertCriterion(criteria.get(0));
+        } else {
+            return criterionConverter.convertCriterion(new SearchQuery.ConjunctionCriterion(SearchQuery.Conjunction.AND,
criteria));
+        }
+    }
+
+    private FilteredQueryRepresentation addMailboxFilters(FilteredQueryRepresentation elasticsearchQueryRepresentation,
String mailboxUUID) {
+        return Stream.of(elasticsearchQueryRepresentation,
+            FilteredQueryRepresentation.fromFilter(termFilter(JsonMessageConstants.MAILBOX_ID,
mailboxUUID)))
+            .collect(new FilteredQueryCollector(SearchQuery.Conjunction.AND));
+    }
+
+    private QueryBuilder getFinalQuery(FilteredQueryRepresentation filteredQueryRepresentation)
{
+        QueryBuilder query = filteredQueryRepresentation.getQuery().orElse(matchAllQuery());
+        if (!filteredQueryRepresentation.getFilter().isPresent()) {
+            return query;
+        }
+        return filteredQuery(query, filteredQueryRepresentation.getFilter().get());
+    }
+
+}

Added: james/mailbox/trunk/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/query/FilteredQueryCollectorTest.java
URL: http://svn.apache.org/viewvc/james/mailbox/trunk/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/query/FilteredQueryCollectorTest.java?rev=1688120&view=auto
==============================================================================
--- james/mailbox/trunk/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/query/FilteredQueryCollectorTest.java
(added)
+++ james/mailbox/trunk/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/query/FilteredQueryCollectorTest.java
Mon Jun 29 08:28:36 2015
@@ -0,0 +1,106 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.elasticsearch.query;
+
+import static net.javacrumbs.jsonunit.fluent.JsonFluentAssert.assertThatJson;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
+import static org.elasticsearch.index.query.FilterBuilders.termFilter;
+import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
+
+import com.google.common.collect.Lists;
+import org.apache.james.mailbox.model.SearchQuery;
+import org.elasticsearch.index.query.QueryBuilder;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.stream.Stream;
+
+public class FilteredQueryCollectorTest {
+
+    @Test
+    public void emptyStreamShouldBeCollectedAsEmptyFilteredQueryRepresentation() throws Exception
{
+        List<FilteredQueryRepresentation> emptyFilteredQueryRepresentationList = Lists.newArrayList();
+        FilteredQueryRepresentation collectionResult = emptyFilteredQueryRepresentationList
+            .stream()
+            .collect(new FilteredQueryCollector(SearchQuery.Conjunction.AND));
+        assertThat(collectionResult.getFilter()).isEmpty();
+        assertThat(collectionResult.getQuery()).isEmpty();
+    }
+
+    @Test
+    public void queryAloneShouldBeWellCollected() throws Exception {
+        FilteredQueryRepresentation collectionResult = Stream.of(FilteredQueryRepresentation.fromQuery(matchAllQuery()))
+            .collect(new FilteredQueryCollector(SearchQuery.Conjunction.AND));
+        assertThat(collectionResult.getFilter()).isEmpty();
+        assertThat(collectionResult.getQuery()).isPresent();
+        assertThatJson(collectionResult.getQuery().get().toXContent(jsonBuilder(), QueryBuilder.EMPTY_PARAMS).string())
+            .isEqualTo("{\"bool\":{\"must\":{\"match_all\":{}}}}");
+    }
+
+    @Test
+    public void filterAloneShouldBeWellCollected() throws Exception {
+        FilteredQueryRepresentation collectionResult = Stream.of(FilteredQueryRepresentation.fromFilter(termFilter("field",
"value")))
+            .collect(new FilteredQueryCollector(SearchQuery.Conjunction.AND));
+        assertThat(collectionResult.getFilter()).isPresent();
+        assertThat(collectionResult.getQuery()).isEmpty();
+        assertThatJson(collectionResult.getFilter().get().toXContent(jsonBuilder(), QueryBuilder.EMPTY_PARAMS).string())
+            .isEqualTo("{\"bool\":{\"must\":{\"term\":{\"field\":\"value\"}}}}");
+    }
+
+    @Test
+    public void aggregationBetweenQueryAndFilterShouldWork() throws Exception {
+        FilteredQueryRepresentation collectionResult = Stream.of(
+            FilteredQueryRepresentation.fromFilter(termFilter("field", "value")),
+            FilteredQueryRepresentation.fromQuery(matchAllQuery()))
+            .collect(new FilteredQueryCollector(SearchQuery.Conjunction.AND));
+        assertThat(collectionResult.getFilter()).isPresent();
+        assertThat(collectionResult.getQuery()).isPresent();
+        assertThatJson(collectionResult.getQuery().get().toXContent(jsonBuilder(), QueryBuilder.EMPTY_PARAMS).string())
+            .isEqualTo("{\"bool\":{\"must\":{\"match_all\":{}}}}");
+        assertThatJson(collectionResult.getFilter().get().toXContent(jsonBuilder(), QueryBuilder.EMPTY_PARAMS).string())
+            .isEqualTo("{\"bool\":{\"must\":{\"term\":{\"field\":\"value\"}}}}");
+    }
+
+    @Test
+    public void queryAggregationShouldWork() throws Exception {
+        FilteredQueryRepresentation collectionResult = Stream.of(
+            FilteredQueryRepresentation.fromQuery(matchAllQuery()),
+            FilteredQueryRepresentation.fromQuery(matchAllQuery()))
+            .collect(new FilteredQueryCollector(SearchQuery.Conjunction.AND));
+        assertThat(collectionResult.getFilter()).isEmpty();
+        assertThat(collectionResult.getQuery()).isPresent();
+        assertThatJson(collectionResult.getQuery().get().toXContent(jsonBuilder(), QueryBuilder.EMPTY_PARAMS).string())
+            .isEqualTo("{\"bool\":{\"must\":[{\"match_all\":{}},{\"match_all\":{}}]}}");
+    }
+
+    @Test
+    public void filterAggregationShouldWork() throws Exception {
+        FilteredQueryRepresentation collectionResult = Stream.of(
+            FilteredQueryRepresentation.fromFilter(termFilter("field", "value")),
+            FilteredQueryRepresentation.fromFilter(termFilter("field", "value")))
+            .collect(new FilteredQueryCollector(SearchQuery.Conjunction.AND));
+        assertThat(collectionResult.getFilter()).isPresent();
+        assertThat(collectionResult.getQuery()).isEmpty();
+        assertThatJson(collectionResult.getFilter().get().toXContent(jsonBuilder(), QueryBuilder.EMPTY_PARAMS).string())
+            .isEqualTo("{\"bool\":{\"must\":[{\"term\":{\"field\":\"value\"}},{\"term\":{\"field\":\"value\"}}]}}");
+    }
+
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


Mime
View raw message