001/*
002 * (C) Copyright 2014-2015 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Benoit Delbosc
018 *     Florent Guillaume
019 */
020package org.nuxeo.elasticsearch.core;
021
022import java.io.Serializable;
023import java.util.Iterator;
024import java.util.List;
025import java.util.Map;
026import java.util.NoSuchElementException;
027
028import org.elasticsearch.action.search.SearchResponse;
029import org.nuxeo.ecm.core.api.IterableQueryResult;
030import org.nuxeo.ecm.core.schema.types.Type;
031
032/**
033 * Iterable query result of the results of an Elasticsearch query.
034 * <p>
035 * Loads all results in memory.
036 *
037 * @since 7.2
038 */
039public class EsResultSetImpl implements IterableQueryResult, Iterator<Map<String, Serializable>> {
040
041    private final SearchResponse response;
042
043    private final Map<String, Type> selectFieldsAndTypes;
044
045    protected List<Map<String, Serializable>> maps;
046
047    protected long size;
048
049    boolean closed;
050
051    private long pos;
052
053    public EsResultSetImpl(SearchResponse response, Map<String, Type> selectFieldsAndTypes) {
054        this.response = response;
055        this.selectFieldsAndTypes = selectFieldsAndTypes;
056        maps = buildMaps();
057        size = maps.size();
058    }
059
060    protected List<Map<String, Serializable>> buildMaps() {
061        return new EsSearchHitConverter(selectFieldsAndTypes).convert(response.getHits().getHits());
062    }
063
064    @Override
065    public void close() {
066        closed = true;
067        pos = -1;
068    }
069
070    @SuppressWarnings("deprecation")
071    @Override
072    public boolean isLife() {
073        return !closed;
074    }
075
076    @Override
077    public boolean mustBeClosed() {
078        return false; // holds no resources
079    }
080
081    /**
082     * Returns the number of results available in the iterator. Note that before 9.1 this method was returning the
083     * totalSize.
084     *
085     * @since 9.1
086     */
087    @Override
088    public long size() {
089        return size;
090    }
091
092    /**
093     * Returns the total number of result that match the query.
094     *
095     * @since 9.1
096     */
097    public long totalSize() {
098        return response.getHits().getTotalHits().value;
099    }
100
101    @Override
102    public long pos() {
103        return pos;
104    }
105
106    @Override
107    public void skipTo(long pos) {
108        if (pos < 0) {
109            pos = 0;
110        } else if (pos > size) {
111            pos = size;
112        }
113        this.pos = pos;
114    }
115
116    @Override
117    public Iterator<Map<String, Serializable>> iterator() {
118        return this; // NOSONAR this iterable does not support multiple traversals
119    }
120
121    @Override
122    public boolean hasNext() {
123        return pos < size;
124    }
125
126    @Override
127    public Map<String, Serializable> next() {
128        if (closed || pos == size) {
129            throw new NoSuchElementException();
130        }
131        Map<String, Serializable> map = maps.get((int) pos);
132        pos++;
133        return map;
134    }
135
136}