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.ArrayList; 024import java.util.HashMap; 025import java.util.Iterator; 026import java.util.List; 027import java.util.Map; 028import java.util.NoSuchElementException; 029 030import org.elasticsearch.action.search.SearchResponse; 031import org.elasticsearch.search.SearchHit; 032import org.elasticsearch.search.SearchHitField; 033import org.nuxeo.ecm.core.api.IterableQueryResult; 034import org.nuxeo.ecm.core.query.sql.NXQL; 035import org.nuxeo.ecm.core.schema.types.Type; 036import org.nuxeo.ecm.core.schema.types.primitives.DateType; 037 038/** 039 * Iterable query result of the results of an Elasticsearch query. 040 * <p> 041 * Loads all results in memory. 042 * 043 * @since 7.2 044 */ 045public class EsResultSetImpl implements IterableQueryResult, Iterator<Map<String, Serializable>> { 046 047 private final SearchResponse response; 048 049 private final Map<String, Type> selectFieldsAndTypes; 050 051 boolean closed; 052 053 protected List<Map<String, Serializable>> maps; 054 055 protected long size; 056 057 private long pos; 058 059 public EsResultSetImpl(SearchResponse response, Map<String, Type> selectFieldsAndTypes) { 060 this.response = response; 061 this.selectFieldsAndTypes = selectFieldsAndTypes; 062 maps = buildMaps(); 063 size = maps.size(); 064 } 065 066 protected List<Map<String, Serializable>> buildMaps() { 067 List<Map<String, Serializable>> rows = new ArrayList<>(response.getHits().getHits().length); 068 Map<String, Serializable> emptyRow = new HashMap<>(selectFieldsAndTypes.size()); 069 for (String fieldName : selectFieldsAndTypes.keySet()) { 070 emptyRow.put(fieldName, null); 071 } 072 for (SearchHit hit : response.getHits().getHits()) { 073 Map<String, Serializable> row = new HashMap<>(emptyRow); 074 for (SearchHitField field : hit.getFields().values()) { 075 String name = field.getName(); 076 Serializable value = field.<Serializable> getValue(); 077 // type conversion 078 Type type; 079 if (value instanceof String && (type = selectFieldsAndTypes.get(name)) instanceof DateType) { 080 // convert back to calendar 081 value = (Serializable) type.decode(((String) value)); 082 } 083 row.put(name, value); 084 } 085 if (selectFieldsAndTypes.containsKey(NXQL.ECM_FULLTEXT_SCORE)) { 086 row.put(NXQL.ECM_FULLTEXT_SCORE, Double.valueOf(hit.getScore())); 087 } 088 rows.add(row); 089 } 090 return rows; 091 } 092 093 @Override 094 public void close() { 095 closed = true; 096 pos = -1; 097 } 098 099 @Override 100 public boolean isLife() { 101 return !closed; 102 } 103 104 @Override 105 public boolean mustBeClosed() { 106 return false; // holds no resources 107 } 108 109 @Override 110 public long size() { 111 return response.getHits().getTotalHits(); 112 } 113 114 @Override 115 public long pos() { 116 return pos; 117 } 118 119 @Override 120 public void skipTo(long pos) { 121 if (pos < 0) { 122 pos = 0; 123 } else if (pos > size) { 124 pos = size; 125 } 126 this.pos = pos; 127 } 128 129 @Override 130 public Iterator<Map<String, Serializable>> iterator() { 131 return this; 132 } 133 134 @Override 135 public boolean hasNext() { 136 return pos < size; 137 } 138 139 @Override 140 public Map<String, Serializable> next() { 141 if (closed || pos == size) { 142 throw new NoSuchElementException(); 143 } 144 Map<String, Serializable> map = maps.get((int) pos); 145 pos++; 146 return map; 147 } 148 149}