JAMES-1807 implement notInMailbox correctly
diff --git a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/events/ElasticSearchListeningMessageSearchIndex.java b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/events/ElasticSearchListeningMessageSearchIndex.java
index 63f0fec..5d24222 100644
--- a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/events/ElasticSearchListeningMessageSearchIndex.java
+++ b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/events/ElasticSearchListeningMessageSearchIndex.java
@@ -83,8 +83,9 @@
public Iterator<Long> search(MailboxSession session, Mailbox mailbox, SearchQuery searchQuery) throws MailboxException {
Preconditions.checkArgument(session != null, "'session' is mandatory");
MailboxId mailboxId = mailbox.getMailboxId();
+ MultimailboxesSearchQuery query = MultimailboxesSearchQuery.from(searchQuery).inMailboxes(mailboxId).build();
return searcher
- .search(ImmutableList.of(session.getUser()), ImmutableList.of(mailboxId), searchQuery)
+ .search(ImmutableList.of(session.getUser()), query)
.get(mailboxId)
.iterator();
}
@@ -93,7 +94,7 @@
public Map<MailboxId, Collection<Long>> search(MailboxSession session, MultimailboxesSearchQuery searchQuery)
throws MailboxException {
Preconditions.checkArgument(session != null, "'session' is mandatory");
- return searcher.search(ImmutableList.of(session.getUser()), searchQuery.getInMailboxes(), searchQuery.getSearchQuery()).asMap();
+ return searcher.search(ImmutableList.of(session.getUser()), searchQuery).asMap();
}
@Override
diff --git a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/QueryConverter.java b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/QueryConverter.java
index 0757b34..fb25109 100644
--- a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/QueryConverter.java
+++ b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/QueryConverter.java
@@ -21,16 +21,20 @@
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;
+import static org.elasticsearch.index.query.QueryBuilders.notQuery;
import java.util.Collection;
import java.util.List;
+import java.util.Optional;
import javax.inject.Inject;
import org.apache.james.mailbox.MailboxSession.User;
import org.apache.james.mailbox.elasticsearch.json.JsonMessageConstants;
import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
import org.apache.james.mailbox.model.SearchQuery;
+import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import com.github.steveash.guavate.Guavate;
@@ -46,10 +50,13 @@
this.criterionConverter = criterionConverter;
}
- public QueryBuilder from(SearchQuery searchQuery, List<User> users, Collection<MailboxId> mailboxIds) {
- QueryBuilder queryBuilder = generateQueryBuilder(searchQuery);
- queryBuilder = addUsersFilter(queryBuilder, users);
- return addMailboxFilters(queryBuilder, mailboxIds);
+ public QueryBuilder from(List<User> users, MultimailboxesSearchQuery query) {
+ BoolQueryBuilder boolQueryBuilder = boolQuery()
+ .must(generateQueryBuilder(query.getSearchQuery()))
+ .filter(usersQuery(users));
+ mailboxesQuery(query.getInMailboxes()).map(boolQueryBuilder::filter);
+ mailboxesQuery(query.getNotInMailboxes()).map(boolQueryBuilder::mustNot);
+ return boolQueryBuilder;
}
private QueryBuilder generateQueryBuilder(SearchQuery searchQuery) {
@@ -63,23 +70,21 @@
}
}
- private QueryBuilder addUsersFilter(QueryBuilder queryBuilder, List<User> users) {
+ private QueryBuilder usersQuery(List<User> users) {
ImmutableList<String> usernames = users.stream()
.map(User::getUserName)
.collect(Guavate.toImmutableList());
- return boolQuery().must(queryBuilder)
- .filter(termsQuery(JsonMessageConstants.USERS, usernames));
+ return termsQuery(JsonMessageConstants.USERS, usernames);
}
- private QueryBuilder addMailboxFilters(QueryBuilder queryBuilder, Collection<MailboxId> mailboxIds) {
+ private Optional<QueryBuilder> mailboxesQuery(Collection<MailboxId> mailboxIds) {
if (mailboxIds.isEmpty()) {
- return queryBuilder;
+ return Optional.empty();
}
ImmutableList<String> ids = mailboxIds.stream()
.map(MailboxId::serialize)
.collect(Guavate.toImmutableList());
- return boolQuery().must(queryBuilder)
- .filter(termsQuery(JsonMessageConstants.MAILBOX_ID, ids));
+ return Optional.of(termsQuery(JsonMessageConstants.MAILBOX_ID, ids));
}
}
diff --git a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/search/ElasticSearchSearcher.java b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/search/ElasticSearchSearcher.java
index 9edaa41..2eb4cf1 100644
--- a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/search/ElasticSearchSearcher.java
+++ b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/search/ElasticSearchSearcher.java
@@ -19,7 +19,6 @@
package org.apache.james.mailbox.elasticsearch.search;
-import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
@@ -36,7 +35,7 @@
import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.model.MailboxId;
import org.apache.james.mailbox.model.MailboxId.Factory;
-import org.apache.james.mailbox.model.SearchQuery;
+import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Client;
@@ -72,21 +71,21 @@
this.mailboxIdFactory = mailboxIdFactory;
}
- public Multimap<MailboxId, Long> search(List<User> users, Collection<MailboxId> mailboxIds, SearchQuery searchQuery) throws MailboxException {
- return new ScrollIterable(client, getSearchRequestBuilder(client, users, mailboxIds, searchQuery)).stream()
+ public Multimap<MailboxId, Long> search(List<User> users, MultimailboxesSearchQuery query) throws MailboxException {
+ return new ScrollIterable(client, getSearchRequestBuilder(client, users, query)).stream()
.flatMap(this::transformResponseToUidStream)
.collect(Guavate.toImmutableListMultimap(Pair::getLeft, Pair::getRight));
}
- private SearchRequestBuilder getSearchRequestBuilder(Client client, List<User> users, Collection<MailboxId> mailboxIds, SearchQuery searchQuery) {
- return searchQuery.getSorts()
+ private SearchRequestBuilder getSearchRequestBuilder(Client client, List<User> users, MultimailboxesSearchQuery query) {
+ return query.getSearchQuery().getSorts()
.stream()
.reduce(
client.prepareSearch(ElasticSearchIndexer.MAILBOX_INDEX)
.setTypes(ElasticSearchIndexer.MESSAGE_TYPE)
.setScroll(TIMEOUT)
.addFields(JsonMessageConstants.ID, JsonMessageConstants.MAILBOX_ID)
- .setQuery(queryConverter.from(searchQuery, users, mailboxIds))
+ .setQuery(queryConverter.from(users, query))
.setSize(size),
(searchBuilder, sort) -> searchBuilder.addSort(SortConverter.convertSort(sort)),
(partialResult1, partialResult2) -> partialResult1);
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/SimpleMessageSearchIndex.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/SimpleMessageSearchIndex.java
index 667f2ab..6ada22f 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/SimpleMessageSearchIndex.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/SimpleMessageSearchIndex.java
@@ -49,6 +49,9 @@
import org.apache.james.mailbox.store.mail.model.MailboxMessage;
import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableMultimap.Builder;
@@ -106,7 +109,7 @@
.iterator();
}
- private Multimap<MailboxId, Long> searchMultimap(MailboxSession session, List<Mailbox> mailboxes, SearchQuery query) throws MailboxException {
+ private Multimap<MailboxId, Long> searchMultimap(MailboxSession session, Iterable<Mailbox> mailboxes, SearchQuery query) throws MailboxException {
Builder<MailboxId, Long> multimap = ImmutableMultimap.builder();
for (Mailbox mailbox: mailboxes) {
multimap.putAll(searchMultimap(session, mailbox, query));
@@ -154,15 +157,21 @@
}
@Override
- public Map<MailboxId, Collection<Long>> search(MailboxSession session, MultimailboxesSearchQuery searchQuery) throws MailboxException {
+ public Map<MailboxId, Collection<Long>> search(MailboxSession session, final MultimailboxesSearchQuery searchQuery) throws MailboxException {
List<Mailbox> allUserMailboxes = mailboxMapperFactory.getMailboxMapper(session)
.findMailboxWithPathLike(new MailboxPath(session.getPersonalSpace(), session.getUser().getUserName(), WILDCARD));
+ FluentIterable<Mailbox> filteredMailboxes = FluentIterable.from(allUserMailboxes).filter(new Predicate<Mailbox>() {
+ @Override
+ public boolean apply(Mailbox input) {
+ return !searchQuery.getNotInMailboxes().contains(input.getMailboxId());
+ }
+ });
if (searchQuery.getInMailboxes().isEmpty()) {
- return searchMultimap(session, allUserMailboxes, searchQuery.getSearchQuery())
+ return searchMultimap(session, filteredMailboxes, searchQuery.getSearchQuery())
.asMap();
}
List<Mailbox> queriedMailboxes = new ArrayList<Mailbox>();
- for (Mailbox mailbox: allUserMailboxes) {
+ for (Mailbox mailbox: filteredMailboxes) {
if (searchQuery.getInMailboxes().contains(mailbox.getMailboxId())) {
queriedMailboxes.add(mailbox);
}
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMessageListMethodTest.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMessageListMethodTest.java
index c8b3d2e..ee24d6b 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMessageListMethodTest.java
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMessageListMethodTest.java
@@ -254,7 +254,6 @@
.body(ARGUMENTS + ".messageIds", contains("username@domain.tld|mailbox|1"));
}
- @Ignore("Temporay break inMailboxes/notInMailboxes support")
@Test
public void getMessageListShouldFilterMessagesWhenNotInMailboxesFilterMatches() throws Exception {
jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "mailbox");
@@ -276,7 +275,6 @@
.body(ARGUMENTS + ".messageIds", empty());
}
- @Ignore("Temporay break inMailboxes/notInMailboxes support")
@Test
public void getMessageListShouldFilterMessagesWhenNotInMailboxesFilterMatchesTwice() throws Exception {
jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "mailbox");
@@ -301,7 +299,6 @@
.body(ARGUMENTS + ".messageIds", empty());
}
- @Ignore("Temporay break inMailboxes/notInMailboxes support")
@Test
public void getMessageListShouldFilterMessagesWhenIdenticalNotInMailboxesAndInmailboxesFilterMatch() throws Exception {
jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "mailbox");
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/GetMessageListMethod.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/GetMessageListMethod.java
index 979eb60..5fb1dae 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/GetMessageListMethod.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/GetMessageListMethod.java
@@ -25,6 +25,7 @@
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.function.Function;
import java.util.stream.Stream;
import javax.inject.Inject;
@@ -167,17 +168,23 @@
SearchQuery searchQuery = messageListRequest.getFilter()
.map(filter -> new FilterToSearchQuery().convert(filter))
.orElse(new SearchQuery());
- Set<MailboxId> inMailboxes = filterToFilterCondition(messageListRequest.getFilter())
- .flatMap(condition -> Guavate.stream(condition.getInMailboxes()))
- .flatMap(List::stream)
- .map(mailboxIdFactory::fromString)
- .collect(Guavate.toImmutableSet());
+ Set<MailboxId> inMailboxes = buildFilterMailboxesSet(messageListRequest.getFilter(), condition -> condition.getInMailboxes());
+ Set<MailboxId> notInMailboxes = buildFilterMailboxesSet(messageListRequest.getFilter(), condition -> condition.getNotInMailboxes());
return MultimailboxesSearchQuery
.from(searchQuery)
.inMailboxes(inMailboxes)
+ .notInMailboxes(notInMailboxes)
.build();
}
+ private Set<MailboxId> buildFilterMailboxesSet(Optional<Filter> maybeFilter, Function<FilterCondition, Optional<List<String>>> mailboxListExtractor) {
+ return filterToFilterCondition(maybeFilter)
+ .flatMap(condition -> Guavate.stream(mailboxListExtractor.apply(condition)))
+ .flatMap(List::stream)
+ .map(mailboxIdFactory::fromString)
+ .collect(Guavate.toImmutableSet());
+ }
+
private Stream<FilterCondition> filterToFilterCondition(Optional<Filter> maybeCondition) {
return Guavate.stream(maybeCondition)
.flatMap(c -> {