
sirius.search.EntityRefList Maven / Gradle / Ivy
Show all versions of sirius-search Show documentation
/*
* Made with all the love in the world
* by scireum in Remshalden, Germany
*
* Copyright by scireum GmbH
* http://www.scireum.de - [email protected]
*/
package sirius.search;
import com.google.common.cache.Cache;
import com.google.common.collect.Lists;
import sirius.kernel.commons.Explain;
import sirius.kernel.commons.Strings;
import sirius.kernel.commons.Tuple;
import sirius.kernel.di.std.Part;
import sirius.kernel.health.Exceptions;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* Used as field type which references a list of other entities.
*
* This permits elegant lazy loading, as only the IDs are eagerly loaded and stored into the database. The objects
* itself are only loaded on demand.
*
* @param the type of the referenced entities
*/
public class EntityRefList {
private List values;
private boolean valueFromCache;
private Class clazz;
private List ids = Lists.newArrayList();
@Part
private static IndexAccess index;
/**
* Creates a new reference.
*
* Fields of this type don't need to be initialized, as this is done by the
* {@link sirius.search.properties.EntityProperty}.
*
* @param ref the type of the referenced entity
*/
public EntityRefList(Class ref) {
this.clazz = ref;
}
/**
* Determines if the entity value is present.
*
* @return true if the value was already loaded (or set). false otherwise.
*/
public boolean isValueLoaded() {
return values != null || ids.isEmpty();
}
/**
* Returns the entities represented by this reference as unmodifyable list.
*
* @return the values represented by this reference. Note that this list is not modifyable. Use {@link
* #addValue(Entity)} or {@link #setIds(List)} to modify this list.
*/
public List getValues() {
return getValuesWithRouting(null);
}
/**
* Returns the entities represented by this reference as unmodifyable list.
*
* @param routing the routing info used to lookup the entities (might be null if no routing is required).
* @return the values represented by this reference. Note that this list is not modifyable. Use {@link
* #addValue(Entity)} or {@link #setIds(List)} to modify this list.
*/
public List getValuesWithRouting(String routing) {
if (!isValueLoaded() || valueFromCache) {
EntityDescriptor descriptor = index.getDescriptor(clazz);
List result = Lists.newArrayList();
for (String id : ids) {
result.add(getValueWithRouting(id, routing, descriptor));
}
values = result.stream().filter(Objects::nonNull).collect(Collectors.toList());
valueFromCache = false;
}
if (values == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(values);
}
private E getValueWithRouting(String id, String routing, EntityDescriptor descriptor) {
if (descriptor.hasRouting()) {
if (Strings.isFilled(routing)) {
return index.find(routing, clazz, id);
} else {
Exceptions.handle()
.to(IndexAccess.LOG)
.withSystemErrorMessage("Fetching an entity of type %s (%s) without routing! "
+ "Using SELECT which might be slower!", clazz.getName(), id)
.handle();
return index.select(clazz).eq(IndexAccess.ID_FIELD, id).queryFirst();
}
} else {
if (Strings.isFilled(routing)) {
Exceptions.handle()
.to(IndexAccess.LOG)
.withSystemErrorMessage("Fetching an entity of type %s (%s) with routing "
+ "(which is not required for this type)!", clazz.getName(), id)
.handle();
}
return index.find(clazz, id);
}
}
/**
* Returns the entities represented by this reference as unmodifyable list.
*
* The framework is permitted to load the value from a given local cache.
*
* @param localCache the cache to used when looking up values
* @return the values represented by this reference. Note that this list is not modifyable. Use {@link
* #addValue(Entity)} or {@link #setIds(List)} to modify this list.
*/
public List getCachedValue(Cache localCache) {
return getCachedValueWithRouting(null, localCache);
}
/**
* Returns the entities represented by this reference as unmodifyable list.
*
* The framework is permitted to load the value from a given local cache.
*
* @param routing the routing info used to lookup the entities (might be null if no routing is
* required).
* @param localCache the cache to used when looking up values
* @return the values represented by this reference. Note that this list is not modifyable. Use {@link
* #addValue(Entity)} or {@link #setIds(List)} to modify this list.
*/
public List getCachedValueWithRouting(String routing, Cache localCache) {
if (isValueLoaded()) {
if (values == null) {
return Collections.emptyList();
}
return values;
}
List result = Lists.newArrayList();
valueFromCache = false;
for (String id : ids) {
Tuple tuple =
localCache == null ? index.fetch(routing, clazz, id) : index.fetch(routing, clazz, id, localCache);
if (tuple.getFirst() != null) {
result.add(tuple.getFirst());
valueFromCache |= tuple.getSecond();
}
}
values = result;
return Collections.unmodifiableList(values);
}
/**
* Returns the entities represented by this reference as unmodifyable list.
*
* The framework is permitted to load the value from the global cache.
*
* @return the values represented by this reference. Note that this list is not modifyable. Use {@link
* #addValue(Entity)} or {@link #setIds(List)} to modify this list.
*/
public List getCachedValue() {
return getCachedValueWithRouting(null);
}
/**
* Returns the entities represented by this reference as unmodifyable list.
*
* The framework is permitted to load the value from the global cache.
*
* @param routing the routing info used to lookup the entities (might be null if no routing is required).
* @return the values represented by this reference. Note that this list is not modifyable. Use {@link
* #addValue(Entity)} or {@link #setIds(List)} to modify this list.
*/
public List getCachedValueWithRouting(String routing) {
return getCachedValueWithRouting(routing, null);
}
/**
* Adds the value to be represented by this reference.
*
* @param value the value to be stored
*/
public void addValue(E value) {
if (value != null) {
if (ids.isEmpty()) {
values = Lists.newArrayList();
}
this.ids.add(value.getId());
if (values != null) {
values.add(value);
}
}
}
/**
* Determines if the list of referenced entities contains the given entity.
*
* @param value the value to check for
* @return true if the value is non null and contained in the list of referenced entities
*/
@SuppressWarnings("squid:S2250")
@Explain("ids needs to be a List for Elasticsearch")
public boolean contains(@Nullable E value) {
if (value == null) {
return false;
}
return ids.contains(value.getId());
}
/**
* Determines if the list of referenced entities contains the given entity (referenced via the given id).
*
* @param id the id of the entity to check for
* @return true if the id is non null and the id of an entity contained in the list of referenced entities
*/
@SuppressWarnings("squid:S2250")
@Explain("ids needs to be a List for Elasticsearch")
public boolean containsId(String id) {
if (Strings.isEmpty(id)) {
return false;
}
return ids.contains(id);
}
/**
* Returns the IDs of the represented values.
*
* This can always be fetched without a DB lookup.
*
* @return the IDs of the represented values
*/
public List getIds() {
return ids;
}
/**
* Sets the IDs of the represented values.
*
* @param ids the ids of the represented values
*/
public void setIds(List ids) {
this.ids.clear();
if (ids != null) {
for (String id : ids) {
if (Strings.isFilled(id)) {
this.ids.add(id);
}
}
}
this.values = null;
this.valueFromCache = false;
}
/**
* Removes all references from the list and clears the locally cached values
*/
public void clear() {
this.ids.clear();
this.values = null;
this.valueFromCache = false;
}
@Override
public String toString() {
if (!getIds().isEmpty()) {
return clazz.getSimpleName() + ": " + ids;
} else {
return clazz.getSimpleName() + ": ";
}
}
}