org.apache.usergrid.persistence.Results.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.usergrid.persistence.Results.java

Source

/*
 * 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.usergrid.persistence;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;

import javax.xml.bind.annotation.XmlRootElement;

import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;
import org.apache.usergrid.persistence.cassandra.QueryProcessor;
import org.apache.usergrid.persistence.query.ir.SearchVisitor;
import org.apache.usergrid.utils.MapUtils;
import org.apache.usergrid.utils.StringUtils;

import static org.apache.commons.codec.binary.Base64.encodeBase64URLSafeString;
import static org.apache.usergrid.persistence.SimpleEntityRef.ref;
import static org.apache.usergrid.utils.ClassUtils.cast;
import static org.apache.usergrid.utils.ConversionUtils.bytes;

@XmlRootElement
public class Results implements Iterable<Entity> {

    public enum Level {
        IDS, REFS, CORE_PROPERTIES, ALL_PROPERTIES, LINKED_PROPERTIES
    }

    Level level = Level.IDS;
    UUID id;
    List<UUID> ids;
    Set<UUID> idSet;

    EntityRef ref;
    List<EntityRef> refs;
    Map<UUID, EntityRef> refsMap;
    Map<String, List<EntityRef>> refsByType;

    Entity entity;
    List<Entity> entities;
    Map<UUID, Entity> entitiesMap;
    Map<String, List<Entity>> entitiesByType;

    List<ConnectionRef> connections;
    boolean forwardConnections = true;

    List<AggregateCounterSet> counters;

    Set<String> types;

    Map<UUID, Map<String, Object>> metadata;
    boolean metadataMerged = true;

    UUID nextResult;
    String cursor;

    Query query;
    Object data;
    String dataName;

    private QueryProcessor queryProcessor;
    private SearchVisitor searchVisitor;

    public Results() {
    }

    public Results(Results r) {
        if (r != null) {
            level = r.level;

            id = r.id;
            ids = r.ids;
            idSet = r.idSet;

            ref = r.ref;
            refs = r.refs;
            refsMap = r.refsMap;
            refsByType = r.refsByType;

            entity = r.entity;
            entities = r.entities;
            entitiesMap = r.entitiesMap;
            entitiesByType = r.entitiesByType;

            connections = r.connections;
            forwardConnections = r.forwardConnections;

            counters = r.counters;

            types = r.types;

            metadata = r.metadata;
            metadataMerged = r.metadataMerged;

            nextResult = r.nextResult;
            cursor = r.cursor;

            query = r.query;
            data = r.data;
            dataName = r.dataName;
        }
    }

    public void init() {
        level = Level.IDS;

        id = null;
        ids = null;
        idSet = null;

        ref = null;
        refs = null;
        refsMap = null;
        refsByType = null;

        entity = null;
        entities = null;
        entitiesMap = null;
        entitiesByType = null;

        connections = null;
        forwardConnections = true;

        counters = null;

        types = null;

        // metadata = null;
        metadataMerged = false;

        query = null;
        data = null;
        dataName = null;
    }

    public static Results fromIdList(List<UUID> l) {
        Results r = new Results();
        r.setIds(l);
        return r;
    }

    public static Results fromIdList(List<UUID> l, String type) {
        if (type == null) {
            return fromIdList(l);
        }
        List<EntityRef> refs = new ArrayList<EntityRef>();
        for (UUID u : l) {
            refs.add(ref(type, u));
        }
        Results r = new Results();
        r.setRefs(refs);
        return r;
    }

    public static Results fromId(UUID id) {
        Results r = new Results();
        if (id != null) {
            List<UUID> l = new ArrayList<UUID>();
            l.add(id);
            r.setIds(l);
        }
        return r;
    }

    public static Results fromRefList(List<EntityRef> l) {
        Results r = new Results();
        r.setRefs(l);
        return r;
    }

    public static Results fromEntities(List<? extends Entity> l) {
        Results r = new Results();
        r.setEntities(l);
        return r;
    }

    public static Results fromEntity(Entity e) {
        Results r = new Results();
        r.setEntity(e);
        return r;
    }

    public static Results fromRef(EntityRef ref) {
        if (ref instanceof Entity) {
            return fromEntity((Entity) ref);
        }
        Results r = new Results();
        r.setRef(ref);
        return r;
    }

    public static Results fromData(Object obj) {
        Results r = new Results();
        r.setData(obj);
        return r;
    }

    public static Results fromCounters(AggregateCounterSet counters) {
        Results r = new Results();
        List<AggregateCounterSet> l = new ArrayList<AggregateCounterSet>();
        l.add(counters);
        r.setCounters(l);
        return r;
    }

    public static Results fromCounters(List<AggregateCounterSet> counters) {
        Results r = new Results();
        r.setCounters(counters);
        return r;
    }

    @SuppressWarnings("unchecked")
    public static Results fromConnections(List<? extends ConnectionRef> connections) {
        Results r = new Results();
        r.setConnections((List<ConnectionRef>) connections, true);
        return r;
    }

    @SuppressWarnings("unchecked")
    public static Results fromConnections(List<? extends ConnectionRef> connections, boolean forward) {
        Results r = new Results();
        r.setConnections((List<ConnectionRef>) connections, forward);
        return r;
    }

    public Level getLevel() {
        return level;
    }

    @JsonSerialize(include = Inclusion.NON_NULL)
    public Query getQuery() {
        return query;
    }

    public void setQuery(Query query) {
        this.query = query;
    }

    public Results withQuery(Query query) {
        this.query = query;
        return this;
    }

    @JsonSerialize(include = Inclusion.NON_NULL)
    public UUID getId() {
        if (id != null) {
            return id;
        }
        if (entity != null) {
            id = entity.getUuid();
            return id;
        }
        if ((ids != null) && (ids.size() > 0)) {
            id = ids.get(0);
            return id;
        }
        if ((entities != null) && (entities.size() > 0)) {
            entity = entities.get(0);
            id = entity.getUuid();
            return id;
        }
        if ((refs != null) && (refs.size() > 0)) {
            EntityRef ref = refs.get(0);
            id = ref.getUuid();
        }
        return id;
    }

    @JsonSerialize(include = Inclusion.NON_NULL)
    public List<UUID> getIds() {
        if (ids != null) {
            return ids;
        }
        /*
         * if (connectionTypeAndEntityTypeToEntityIdMap != null) { ids = new
         * ArrayList<UUID>(); Set<UUID> entitySet = new LinkedHashSet<UUID>();
         * for (String ctype : connectionTypeAndEntityTypeToEntityIdMap
         * .keySet()) { Map<String, List<UUID>> m =
         * connectionTypeAndEntityTypeToEntityIdMap .get(ctype); for (String
         * etype : m.keySet()) { List<UUID> l = m.get(etype); for (UUID id : l)
         * { if (!entitySet.contains(id)) { ids.add(id); } } } } return ids; }
         */
        if (connections != null) {
            ids = new ArrayList<UUID>();
            for (ConnectionRef connection : connections) {
                if (forwardConnections) {
                    ConnectedEntityRef c = connection.getConnectedEntity();
                    if (c != null) {
                        ids.add(c.getUuid());
                    }
                } else {
                    EntityRef c = connection.getConnectingEntity();
                    if (c != null) {
                        ids.add(c.getUuid());
                    }
                }
            }
            return ids;
        }
        if ((entities != null)
        /* || (connectionTypeAndEntityTypeToEntityMap != null) */ ) {
            // getEntities();
            ids = new ArrayList<UUID>();
            for (Entity entity : entities) {
                ids.add(entity.getUuid());
            }
            return ids;
        }
        if (refs != null) {
            ids = new ArrayList<UUID>();
            for (EntityRef ref : refs) {
                ids.add(ref.getUuid());
            }
            return ids;
        }
        if (id != null) {
            ids = new ArrayList<UUID>();
            ids.add(id);
            return ids;
        }
        if (entity != null) {
            ids = new ArrayList<UUID>();
            ids.add(entity.getUuid());
            return ids;
        }
        return new ArrayList<UUID>();
    }

    public void setIds(List<UUID> resultsIds) {
        init();
        ids = resultsIds;
        level = Level.IDS;
    }

    public Results withIds(List<UUID> resultsIds) {
        setIds(resultsIds);
        return this;
    }

    @JsonSerialize(include = Inclusion.NON_NULL)
    public Set<UUID> getIdSet() {
        if (idSet != null) {
            return idSet;
        }
        getIds();
        if (ids != null) {
            idSet = new LinkedHashSet<UUID>();
            idSet.addAll(ids);
            return idSet;
        }
        return new LinkedHashSet<UUID>();
    }

    @JsonSerialize(include = Inclusion.NON_NULL)
    @SuppressWarnings("unchecked")
    public List<EntityRef> getRefs() {
        if (refs != null) {
            return refs;
        }
        List<?> l = getEntities();
        if ((l != null) && (l.size() > 0)) {
            return (List<EntityRef>) l;
        }
        if (connections != null) {
            refs = new ArrayList<EntityRef>();
            for (ConnectionRef connection : connections) {
                if (forwardConnections) {
                    ConnectedEntityRef c = connection.getConnectedEntity();
                    if (c != null) {
                        refs.add(c);
                    }
                } else {
                    EntityRef c = connection.getConnectingEntity();
                    if (c != null) {
                        refs.add(c);
                    }
                }
            }
            return refs;
        }
        if (ref != null) {
            refs = new ArrayList<EntityRef>();
            refs.add(ref);
            return refs;
        }
        return new ArrayList<EntityRef>();
    }

    public void setRefs(List<EntityRef> resultsRefs) {
        init();
        refs = resultsRefs;
        level = Level.REFS;
    }

    public Results withRefs(List<EntityRef> resultsRefs) {
        setRefs(resultsRefs);
        return this;
    }

    public void setRef(EntityRef ref) {
        init();
        this.ref = ref;
        level = Level.REFS;
    }

    public Results withRef(EntityRef ref) {
        setRef(ref);
        return this;
    }

    @JsonSerialize(include = Inclusion.NON_NULL)
    public EntityRef getRef() {
        if (ref != null) {
            return ref;
        }
        ref = getEntity();
        if (ref != null) {
            return ref;
        }
        UUID u = getId();
        if (u != null) {
            return ref(u);
        }
        return null;
    }

    @JsonSerialize(include = Inclusion.NON_NULL)
    public Map<UUID, EntityRef> getRefsMap() {
        if (refsMap != null) {
            return refsMap;
        }
        getEntitiesMap();
        if (entitiesMap != null) {
            refsMap = cast(entitiesMap);
            return refsMap;
        }
        getRefs();
        if (refs != null) {
            refsMap = new LinkedHashMap<UUID, EntityRef>();
            for (EntityRef ref : refs) {
                refsMap.put(ref.getUuid(), ref);
            }
        }
        return refsMap;
    }

    @JsonSerialize(include = Inclusion.NON_NULL)
    public Entity getEntity() {
        mergeEntitiesWithMetadata();
        if (entity != null) {
            return entity;
        }
        if ((entities != null) && (entities.size() > 0)) {
            entity = entities.get(0);
            return entity;
        }
        return null;
    }

    public void setEntity(Entity resultEntity) {
        init();
        entity = resultEntity;
        level = Level.CORE_PROPERTIES;
    }

    public Results withEntity(Entity resultEntity) {
        setEntity(resultEntity);
        return this;
    }

    public Iterator<UUID> idIterator() {
        List<UUID> l = getIds();
        if (l != null) {
            return l.iterator();
        }
        return (new ArrayList<UUID>(0)).iterator();
    }

    @JsonSerialize(include = Inclusion.NON_NULL)
    public List<Entity> getEntities() {
        mergeEntitiesWithMetadata();
        if (entities != null) {
            return entities;
        }
        /*
         * if (connectionTypeAndEntityTypeToEntityMap != null) { entities = new
         * ArrayList<Entity>(); Map<UUID, Entity> eMap = new LinkedHashMap<UUID,
         * Entity>(); for (String ctype :
         * connectionTypeAndEntityTypeToEntityMap.keySet()) { Map<String,
         * List<Entity>> m = connectionTypeAndEntityTypeToEntityMap .get(ctype);
         * for (String etype : m.keySet()) { List<Entity> l = m.get(etype); for
         * (Entity e : l) { if (!eMap.containsKey(e.getUuid())) { entities.add(e);
         * eMap.put(e.getUuid(), e); } } } } return entities; }
         */
        if (entity != null) {
            entities = new ArrayList<Entity>();
            entities.add(entity);
            return entities;
        }
        return new ArrayList<Entity>();
    }

    @JsonSerialize(include = Inclusion.NON_NULL)
    public Map<UUID, Entity> getEntitiesMap() {
        if (entitiesMap != null) {
            return entitiesMap;
        }
        if (entities != null) {
            entitiesMap = new LinkedHashMap<UUID, Entity>();
            for (Entity entity : entities) {
                entitiesMap.put(entity.getUuid(), entity);
            }
        }
        return entitiesMap;
    }

    public List<EntityRef> getEntityRefsByType(String type) {
        if (entitiesByType != null) {
            return refsByType.get(type);
        }
        List<EntityRef> l = cast(getEntitiesByType(type));
        if (l != null) {
            return l;
        }
        getRefs();
        if (refs == null) {
            return null;
        }
        refsByType = new LinkedHashMap<String, List<EntityRef>>();
        for (Entity entity : entities) {
            l = refsByType.get(entity.getType());
            if (l == null) {
                l = new ArrayList<EntityRef>();
                refsByType.put(entity.getType(), l);
            }
            l.add(entity);
        }
        return l;
    }

    public List<Entity> getEntitiesByType(String type) {
        if (entitiesByType != null) {
            return entitiesByType.get(type);
        }
        getEntities();
        if (entities == null) {
            return null;
        }
        List<Entity> l = null;
        entitiesByType = new LinkedHashMap<String, List<Entity>>();
        for (Entity entity : entities) {
            l = entitiesByType.get(entity.getType());
            if (l == null) {
                l = new ArrayList<Entity>();
                entitiesByType.put(entity.getType(), l);
            }
            l.add(entity);
        }
        return l;
    }

    @JsonSerialize(include = Inclusion.NON_NULL)
    public Set<String> getTypes() {
        if (types != null) {
            return types;
        }
        getEntityRefsByType("entity");
        if (entitiesByType != null) {
            types = entitiesByType.keySet();
        } else if (refsByType != null) {
            types = refsByType.keySet();
        }
        return types;
    }

    public void merge(Results results) {
        getEntitiesMap();
        results.getEntitiesMap();
        if (entitiesMap != null || results.entitiesMap != null) {

            level = Level.ALL_PROPERTIES;

            // do nothing, nothing to union
            if (entitiesMap != null && results.entitiesMap == null) {
                return;
                // other side has the results, assign and return
            } else if (entitiesMap == null && results.entitiesMap != null) {
                entities = results.entities;
                return;
            }

            entitiesMap.putAll(results.entitiesMap);
            entities = new ArrayList<Entity>(entitiesMap.values());

            return;
        }

        getRefsMap();
        results.getRefsMap();
        if ((refsMap != null) || (results.refsMap != null)) {

            level = Level.REFS;

            // do nothing, nothing to union
            if (refsMap != null && results.refsMap == null) {
                return;
                // other side has the results, assign and return
            } else if (refsMap == null && results.refsMap != null) {
                refs = results.refs;
                return;
            }

            refsMap.putAll(results.refsMap);
            refs = new ArrayList<EntityRef>(refsMap.values());

            return;
        }

        getIdSet();
        results.getIdSet();
        if ((idSet != null) && (results.idSet != null)) {

            level = Level.IDS;

            // do nothing, nothing to union
            if (idSet != null && results.idSet == null) {
                return;
                // other side has the results, assign and return
            } else if (idSet == null && results.idSet != null) {
                ids = results.ids;
                return;
            }

            idSet.addAll(results.idSet);
            ids = new ArrayList<UUID>(idSet);

        }
    }

    /** Remove the passed in results from the current results */
    public void subtract(Results results) {
        getEntitiesMap();
        results.getEntitiesMap();

        if ((entitiesMap != null) && (results.entitiesMap != null)) {
            Map<UUID, Entity> newMap = new LinkedHashMap<UUID, Entity>();
            for (Map.Entry<UUID, Entity> e : entitiesMap.entrySet()) {
                if (!results.entitiesMap.containsKey(e.getKey())) {
                    newMap.put(e.getKey(), e.getValue());
                }
            }
            entitiesMap = newMap;
            entities = new ArrayList<Entity>(entitiesMap.values());
            level = Level.ALL_PROPERTIES;
            return;
        }

        getRefsMap();
        results.getRefsMap();
        if ((refsMap != null) && (results.refsMap != null)) {
            Map<UUID, EntityRef> newMap = new LinkedHashMap<UUID, EntityRef>();
            for (Map.Entry<UUID, EntityRef> e : refsMap.entrySet()) {
                if (!results.refsMap.containsKey(e.getKey())) {
                    newMap.put(e.getKey(), e.getValue());
                }
            }
            refsMap = newMap;
            refs = new ArrayList<EntityRef>(refsMap.values());
            level = Level.REFS;
            return;
        }

        getIdSet();
        results.getIdSet();
        if ((idSet != null) && (results.idSet != null)) {
            Set<UUID> newSet = new LinkedHashSet<UUID>();
            for (UUID uuid : idSet) {
                if (!results.idSet.contains(uuid)) {
                    newSet.add(uuid);
                }
            }
            idSet = newSet;
            ids = new ArrayList<UUID>(idSet);
            level = Level.IDS;
        }
    }

    /** Perform an intersection of the 2 results */
    public void and(Results results) {
        getEntitiesMap();
        results.getEntitiesMap();

        if ((entitiesMap != null) && (results.entitiesMap != null)) {
            Map<UUID, Entity> newMap = new LinkedHashMap<UUID, Entity>();
            for (Map.Entry<UUID, Entity> e : entitiesMap.entrySet()) {
                if (results.entitiesMap.containsKey(e.getKey())) {
                    newMap.put(e.getKey(), e.getValue());
                }
            }
            entitiesMap = newMap;
            entities = new ArrayList<Entity>(entitiesMap.values());
            level = Level.ALL_PROPERTIES;
            return;
        }

        getRefsMap();
        results.getRefsMap();
        if ((refsMap != null) && (results.refsMap != null)) {
            Map<UUID, EntityRef> newMap = new LinkedHashMap<UUID, EntityRef>();
            for (Map.Entry<UUID, EntityRef> e : refsMap.entrySet()) {
                if (results.refsMap.containsKey(e.getKey())) {
                    newMap.put(e.getKey(), e.getValue());
                }
            }
            refsMap = newMap;
            refs = new ArrayList<EntityRef>(refsMap.values());
            level = Level.REFS;
            ids = null;
            return;
        }

        getIdSet();
        results.getIdSet();
        if ((idSet != null) && (results.idSet != null)) {
            Set<UUID> newSet = new LinkedHashSet<UUID>();
            for (UUID uuid : idSet) {
                if (results.idSet.contains(uuid)) {
                    newSet.add(uuid);
                }
            }
            idSet = newSet;
            ids = new ArrayList<UUID>(idSet);
            level = Level.IDS;
            return;
        }

        // should be empty
        init();
    }

    public void replace(Entity entity) {
        entitiesMap = null;
        if ((this.entity != null) && (this.entity.getUuid().equals(entity.getUuid()))) {
            this.entity = entity;
        }
        if (entities != null) {
            ListIterator<Entity> i = entities.listIterator();
            while (i.hasNext()) {
                Entity e = i.next();
                if (e.getUuid().equals(entity.getUuid())) {
                    i.set(entity);
                }
            }
        }
    }

    public Results startingFrom(UUID entityId) {
        if (entities != null) {
            for (int i = 0; i < entities.size(); i++) {
                Entity entity = entities.get(i);
                if (entityId.equals(entity.getUuid())) {
                    if (i == 0) {
                        return this;
                    }
                    return Results.fromEntities(entities.subList(i, entities.size()));
                }
            }
        }
        if (refs != null) {
            for (int i = 0; i < refs.size(); i++) {
                EntityRef entityRef = refs.get(i);
                if (entityId.equals(entityRef.getUuid())) {
                    if (i == 0) {
                        return this;
                    }
                    return Results.fromRefList(refs.subList(i, refs.size()));
                }
            }
        }
        if (ids != null) {
            for (int i = 0; i < ids.size(); i++) {
                UUID uuid = ids.get(i);
                if (entityId.equals(uuid)) {
                    if (i == 0) {
                        return this;
                    }
                    return Results.fromIdList(ids.subList(i, ids.size()));
                }
            }
        }
        return this;
    }

    @SuppressWarnings("unchecked")
    @JsonSerialize(include = Inclusion.NON_NULL)
    public <E extends Entity> List<E> getList() {
        List<Entity> l = getEntities();
        return (List<E>) l;
    }

    public <E extends Entity> Iterator<E> iterator(Class<E> cls) {
        List<E> l = getList();
        if (l != null) {
            return l.iterator();
        }
        return (new ArrayList<E>(0)).iterator();
    }

    @Override
    public Iterator<Entity> iterator() {
        List<Entity> l = getEntities();
        if (l != null) {
            return l.iterator();
        }
        return (new ArrayList<Entity>(0)).iterator();
    }

    public Results findForProperty(String propertyName, Object propertyValue) {
        return findForProperty(propertyName, propertyValue, 1);
    }

    public Results findForProperty(String propertyName, Object propertyValue, int count) {
        if (propertyValue == null) {
            return new Results();
        }
        List<Entity> l = getEntities();
        if (l == null) {
            return new Results();
        }
        List<Entity> found = new ArrayList<Entity>();
        for (Entity e : l) {
            if (propertyValue.equals(e.getProperty(propertyName))) {
                found.add(e);
                if ((count > 0) && (found.size() == count)) {
                    break;
                }
            }
        }
        return Results.fromEntities(found);
    }

    @SuppressWarnings("unchecked")
    public void setEntities(List<? extends Entity> resultsEntities) {
        init();
        entities = (List<Entity>) resultsEntities;
        level = Level.CORE_PROPERTIES;
    }

    public Results withEntities(List<? extends Entity> resultsEntities) {
        setEntities(resultsEntities);
        return this;
    }

    public boolean hasConnections() {
        return connections != null;
    }

    @JsonSerialize(include = Inclusion.NON_NULL)
    public List<ConnectionRef> getConnections() {
        return connections;
    }

    private void setConnections(List<ConnectionRef> connections, boolean forwardConnections) {
        init();
        this.connections = connections;
        this.forwardConnections = forwardConnections;
        level = Level.REFS;
        for (ConnectionRef connection : connections) {
            if (forwardConnections) {
                this.setMetadata(connection.getConnectedEntity().getUuid(), "connection",
                        connection.getConnectionType());
            } else {
                this.setMetadata(connection.getConnectingEntity().getUuid(), "connection",
                        connection.getConnectionType());
            }
        }
    }

    @JsonSerialize(include = Inclusion.NON_NULL)
    public Object getObject() {
        if (data != null) {
            return data;
        }
        if (entities != null) {
            return entities;
        }
        if (ids != null) {
            return ids;
        }
        if (entity != null) {
            return entity;
        }
        if (id != null) {
            return id;
        }
        if (counters != null) {
            return counters;
        }
        return null;
    }

    @JsonSerialize(include = Inclusion.NON_NULL)
    public String getObjectName() {
        if (dataName != null) {
            return dataName;
        }
        if (entities != null) {
            return "entities";
        }
        if (ids != null) {
            return "ids";
        }
        if (entity != null) {
            return "entity";
        }
        if (id != null) {
            return "id";
        }
        return null;
    }

    public void setDataName(String dataName) {
        this.dataName = dataName;
    }

    public Results withDataName(String dataName) {
        this.dataName = dataName;
        return this;
    }

    public boolean hasData() {
        return data != null;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public Results withData(Object data) {
        this.data = data;
        return this;
    }

    @JsonSerialize(include = Inclusion.NON_NULL)
    public Object getData() {
        return data;
    }

    @JsonSerialize(include = Inclusion.NON_NULL)
    public List<AggregateCounterSet> getCounters() {
        return counters;
    }

    public void setCounters(List<AggregateCounterSet> counters) {
        this.counters = counters;
    }

    public Results withCounters(List<AggregateCounterSet> counters) {
        this.counters = counters;
        return this;
    }

    public int size() {
        if (entities != null) {
            return entities.size();
        }
        if (refs != null) {
            return refs.size();
        }
        if (ids != null) {
            return ids.size();
        }
        if (entity != null) {
            return 1;
        }
        if (ref != null) {
            return 1;
        }
        if (id != null) {
            return 1;
        }
        return 0;
    }

    public boolean isEmpty() {
        return size() == 0;
    }

    @JsonSerialize(include = Inclusion.NON_NULL)
    public UUID getNextResult() {
        return nextResult;
    }

    public Results excludeCursorMetadataAttribute() {
        if (metadata != null) {
            for (Entry<UUID, Map<String, Object>> entry : metadata.entrySet()) {
                Map<String, Object> map = entry.getValue();
                if (map != null) {
                    map.remove(Schema.PROPERTY_CURSOR);
                }
            }
        }
        return new Results(this);
    }

    public Results trim(int count) {
        if (count == 0) {
            return this;
        }

        int size = size();
        if (size <= count) {
            return this;
        }

        List<UUID> ids = getIds();
        UUID nextResult = null;
        String cursor = null;
        if (ids.size() > count) {
            nextResult = ids.get(count);
            ids = ids.subList(0, count);
            if (metadata != null) {
                cursor = StringUtils.toString(MapUtils.getMapMap(metadata, nextResult, "cursor"));
            }
            if (cursor == null) {
                cursor = encodeBase64URLSafeString(bytes(nextResult));
            }
        }

        Results r = new Results(this);
        if (r.entities != null) {
            r.entities = r.entities.subList(0, count);
        }
        if (r.refs != null) {
            r.refs = r.refs.subList(0, count);
        }
        if (r.ids != null) {
            r.ids = r.ids.subList(0, count);
        }
        r.setNextResult(nextResult);
        r.setCursor(cursor);

        return r;
    }

    public boolean hasMoreResults() {
        return nextResult != null;
    }

    public void setNextResult(UUID nextResult) {
        this.nextResult = nextResult;
    }

    public Results withNextResult(UUID nextResult) {
        this.nextResult = nextResult;
        return this;
    }

    @JsonSerialize(include = Inclusion.NON_NULL)
    public String getCursor() {
        return cursor;
    }

    public boolean hasCursor() {
        return cursor != null && cursor.length() > 0;
    }

    public void setCursor(String cursor) {
        this.cursor = cursor;
    }

    public Results withCursor(String cursor) {
        this.cursor = cursor;
        return this;
    }

    public void setMetadata(UUID id, String name, Object value) {
        if (metadata == null) {
            metadata = new LinkedHashMap<UUID, Map<String, Object>>();
        }
        Map<String, Object> entityMetadata = metadata.get(id);
        if (entityMetadata == null) {
            entityMetadata = new LinkedHashMap<String, Object>();
            metadata.put(id, entityMetadata);
        }
        entityMetadata.put(name, value);
        metadataMerged = false;
        // updateIndex(id, name, value);
    }

    public Results withMetadata(UUID id, String name, Object value) {
        setMetadata(id, name, value);
        return this;
    }

    public void setMetadata(UUID id, Map<String, Object> data) {
        if (metadata == null) {
            metadata = new LinkedHashMap<UUID, Map<String, Object>>();
        }
        Map<String, Object> entityMetadata = metadata.get(id);
        if (entityMetadata == null) {
            entityMetadata = new LinkedHashMap<String, Object>();
            metadata.put(id, entityMetadata);
        }
        entityMetadata.putAll(data);
        metadataMerged = false;
        /*
         * for (Entry<String, Object> m : data.entrySet()) { updateIndex(id,
         * m.getKey(), m.getValue()); }
         */
    }

    public Results withMetadata(UUID id, Map<String, Object> data) {
        setMetadata(id, data);
        return this;
    }

    public void setMetadata(Map<UUID, Map<String, Object>> metadata) {
        this.metadata = metadata;
    }

    public Results withMetadata(Map<UUID, Map<String, Object>> metadata) {
        this.metadata = metadata;
        return this;
    }

    public void mergeEntitiesWithMetadata() {
        if (metadataMerged) {
            return;
        }
        if (metadata == null) {
            return;
        }
        metadataMerged = true;
        getEntities();
        if (entities != null) {
            for (Entity entity : entities) {
                entity.clearMetadata();
                Map<String, Object> entityMetadata = metadata.get(entity.getUuid());
                if (entityMetadata != null) {
                    entity.mergeMetadata(entityMetadata);
                }
            }
        }
    }

    protected QueryProcessor getQueryProcessor() {
        return queryProcessor;
    }

    public void setQueryProcessor(QueryProcessor queryProcessor) {
        this.queryProcessor = queryProcessor;
    }

    public void setSearchVisitor(SearchVisitor searchVisitor) {
        this.searchVisitor = searchVisitor;
    }

    /** uses cursor to get next batch of Results (returns null if no cursor) */
    public Results getNextPageResults() throws Exception {
        if (!hasCursor()) {
            return null;
        }

        Query q = new Query(query);
        q.setCursor(getCursor());
        queryProcessor.setQuery(q);

        return queryProcessor.getResults(searchVisitor);
    }
}