From 1746691826a81adb4558a5630cf25ac65f76761e Mon Sep 17 00:00:00 2001 From: Greg Burd Date: Fri, 20 Oct 2017 12:33:42 -0400 Subject: [PATCH] Facet cache is working, able to fetch by non-primary key within UOW. --- helenus-core.iml | 1 - pom.xml | 6 - .../net/helenus/core/AbstractUnitOfWork.java | 128 ++++++------------ src/main/java/net/helenus/core/Filter.java | 4 + src/main/java/net/helenus/core/Postulate.java | 4 + .../java/net/helenus/core/SchemaUtil.java | 29 +++- .../java/net/helenus/core/UnitOfWork.java | 13 +- .../net/helenus/core/cache/BoundFacet.java | 16 ++- .../java/net/helenus/core/cache/Facet.java | 55 ++++++-- ...dentifyingFacet.java => UnboundFacet.java} | 39 +++--- .../operation/AbstractOptionalOperation.java | 9 +- .../operation/AbstractStatementOperation.java | 77 ++++------- .../operation/AbstractStreamOperation.java | 10 +- .../core/operation/BoundStreamOperation.java | 9 +- .../core/operation/InsertOperation.java | 19 +-- .../net/helenus/core/operation/Operation.java | 11 +- .../core/operation/SelectFirstOperation.java | 15 +- .../SelectFirstTransformingOperation.java | 9 +- .../core/operation/SelectOperation.java | 44 +++--- .../SelectTransformingOperation.java | 15 +- .../core/operation/UpdateOperation.java | 2 +- .../net/helenus/mapping/HelenusEntity.java | 6 +- .../helenus/mapping/HelenusMappingEntity.java | 41 +++--- 23 files changed, 248 insertions(+), 314 deletions(-) rename src/main/java/net/helenus/core/cache/{EntityIdentifyingFacet.java => UnboundFacet.java} (59%) diff --git a/helenus-core.iml b/helenus-core.iml index 1f3247f..be96637 100644 --- a/helenus-core.iml +++ b/helenus-core.iml @@ -35,7 +35,6 @@ - diff --git a/pom.xml b/pom.xml index 8e4391b..71feca8 100644 --- a/pom.xml +++ b/pom.xml @@ -148,12 +148,6 @@ 20.0 - - org.ahocorasick - ahocorasick - 0.4.0 - - io.zipkin.java diff --git a/src/main/java/net/helenus/core/AbstractUnitOfWork.java b/src/main/java/net/helenus/core/AbstractUnitOfWork.java index b689ab2..1bc6683 100644 --- a/src/main/java/net/helenus/core/AbstractUnitOfWork.java +++ b/src/main/java/net/helenus/core/AbstractUnitOfWork.java @@ -16,17 +16,13 @@ package net.helenus.core; import java.util.*; -import java.util.stream.Collectors; - -import org.ahocorasick.trie.Emit; -import org.ahocorasick.trie.Trie; import com.diffplug.common.base.Errors; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; import com.google.common.collect.TreeTraverser; -import net.helenus.core.cache.BoundFacet; import net.helenus.core.cache.Facet; -import net.helenus.support.Either; /** Encapsulates the concept of a "transaction" as a unit-of-work. */ public abstract class AbstractUnitOfWork implements UnitOfWork, AutoCloseable { @@ -34,11 +30,12 @@ public abstract class AbstractUnitOfWork implements UnitOfW private final HelenusSession session; private final AbstractUnitOfWork parent; private List postCommit = new ArrayList(); - private final Map>> cache = new HashMap>>(); - private Trie cacheIndex = Trie.builder().ignoreOverlaps().build(); private boolean aborted = false; private boolean committed = false; + // Cache: + private final Table cache = HashBasedTable.create(); + protected AbstractUnitOfWork(HelenusSession session, AbstractUnitOfWork parent) { Objects.requireNonNull(session, "containing session cannot be null"); @@ -68,62 +65,40 @@ public abstract class AbstractUnitOfWork implements UnitOfW } @Override - public Optional>> cacheLookupByFacet(Set facets) { - Optional>> result = Optional.empty(); - Collection emits = cacheIndex.parseText( - String.join(" ", facets.stream().map(facet -> facet.toString()).collect(Collectors.toList()))); - for (Emit emit : emits) { - // NOTE: rethink. should this match *all* facets? how do I know which emit - // keyword is the primary key? - String key = emit.getKeyword(); - result = cacheLookup(key); - if (result.isPresent()) { - return result; + public Optional cacheLookup(List facets) { + Facet table = facets.remove(0); + String tableName = table.value().toString(); + Optional result = Optional.empty(); + for (Facet facet : facets) { + String columnName = facet.name() + "==" + facet.value(); + Object value = cache.get(tableName, columnName); + if (value != null) { + if (result.isPresent() && result.get() != value) { + // One facet matched, but another did not. + result = Optional.empty(); + break; + } else { + result = Optional.of(value); + } } } if (!result.isPresent()) { // Be sure to check all enclosing UnitOfWork caches as well, we may be nested. if (parent != null) { - return parent.cacheLookupByFacet(facets); + return parent.cacheLookup(facets); } } return result; } @Override - public Optional>> cacheLookupByStatement(String[] statementKeys) { - String key = String.join(",", statementKeys); - return cacheLookup(key); - } - - @Override - public Optional>> cacheLookup(String key) { - Optional>> result = (cache.containsKey(key)) - ? Optional.of(cache.get(key)) - : Optional.empty(); - - if (!result.isPresent()) { - // Be sure to check all enclosing UnitOfWork caches as well, we may be nested. - if (parent != null) { - return parent.cacheLookup(key); - } + public void cacheUpdate(Object value, List facets) { + Facet table = facets.remove(0); + String tableName = table.value().toString(); + for (Facet facet : facets) { + String columnName = facet.name() + "==" + facet.value(); + cache.put(tableName, columnName, value); } - return result; - } - - @Override - public void cacheUpdate(Either> value, String[] statementKeys, - Map facetMap) { - String key = "CQL::" + String.join(",", statementKeys); - cache.put(key, value); - Trie.TrieBuilder builder = cacheIndex.builder().ignoreOverlaps(); - facetMap.forEach((facetName, facet) -> { - builder.addKeyword(facet.toString()); - if (facetName.equals("*")) { - cache.put(facet.toString(), value); - } - }); - cacheIndex = builder.build(); } private Iterator> getChildNodes() { @@ -161,15 +136,14 @@ public abstract class AbstractUnitOfWork implements UnitOfW committed = true; aborted = false; - // TODO(gburd): union this cache with parent's (if there is a parent) or with - // the session cache for all cacheable entities we currently hold - nested.forEach((uow) -> Errors.rethrow().wrap(uow::commit)); // Merge UOW cache into parent's cache. if (parent != null) { - parent.assumeCache(cache, cacheIndex); - } + parent.mergeCache(cache); + } // else { + // TODO... merge into session cache objects marked cacheable + // } // Apply all post-commit functions for if (parent == null) { @@ -197,35 +171,21 @@ public abstract class AbstractUnitOfWork implements UnitOfW // cache.invalidateSince(txn::start time) } - private void assumeCache(Map>> childCache, Trie childCacheIndex) { - for (String key : childCache.keySet()) { - if (cache.containsKey(key)) { - Either> value = cache.get(key); - if (value.isLeft()) { - Object obj = value.getLeft(); - // merge objects - Either> childValue = childCache.get(key); - if (childValue.isLeft()) { - Object childObj = childValue.getLeft(); - } else { - Set childSet = childValue.getRight(); - } + private void mergeCache(Table from) { + Table to = this.cache; + from.rowMap().forEach((rowKey, columnMap) -> { + columnMap.forEach((columnKey, value) -> { + if (to.contains(rowKey, columnKey)) { + to.put(rowKey, columnKey, merge(to.get(rowKey, columnKey), from.get(rowKey, columnKey))); } else { - // merge the sets - Set set = value.getRight(); - Either> childValue = childCache.get(key); - if (childValue.isLeft()) { - Object childObj = childValue.getLeft(); - set.add(childObj); - } else { - Set childSet = childValue.getRight(); - set.addAll(childSet); - } + to.put(rowKey, columnKey, from.get(rowKey, columnKey)); } - } else { - cache.put(key, childCache.get(key)); - } - } + }); + }); + } + + private Object merge(Object to, Object from) { + return to; // TODO(gburd): yeah... } public String describeConflicts() { diff --git a/src/main/java/net/helenus/core/Filter.java b/src/main/java/net/helenus/core/Filter.java index 3d6762f..79d9a34 100644 --- a/src/main/java/net/helenus/core/Filter.java +++ b/src/main/java/net/helenus/core/Filter.java @@ -105,6 +105,10 @@ public final class Filter { return new Filter(node, postulate); } + public V[] postulateValues() { + return postulate.values(); + } + @Override public String toString() { return node.getColumnName() + postulate.toString(); diff --git a/src/main/java/net/helenus/core/Postulate.java b/src/main/java/net/helenus/core/Postulate.java index a31d0a8..05e9f57 100644 --- a/src/main/java/net/helenus/core/Postulate.java +++ b/src/main/java/net/helenus/core/Postulate.java @@ -71,6 +71,10 @@ public final class Postulate { } } + public V[] values() { + return values; + } + @Override public String toString() { diff --git a/src/main/java/net/helenus/core/SchemaUtil.java b/src/main/java/net/helenus/core/SchemaUtil.java index 8e421c0..44ccae8 100644 --- a/src/main/java/net/helenus/core/SchemaUtil.java +++ b/src/main/java/net/helenus/core/SchemaUtil.java @@ -140,6 +140,28 @@ public final class SchemaUtil { return SchemaBuilder.dropType(type.getTypeName()).ifExists(); } + public static String createPrimaryKeyPhrase(Collection properties) { + List p = new ArrayList(properties.size()); + List c = new ArrayList(properties.size()); + + for (HelenusProperty prop : properties) { + String columnName = prop.getColumnName().toCql(); + switch (prop.getColumnType()) { + case PARTITION_KEY : + p.add(columnName); + break; + case CLUSTERING_COLUMN : + c.add(columnName); + break; + default : + break; + } + } + + return "(" + ((p.size() > 1) ? "(" + String.join(", ", p) + ")" : p.get(0)) + + ((c.size() > 0) ? ", " + ((c.size() > 1) ? "(" + String.join(", ", c) + ")" : c.get(0)) : "") + ")"; + } + public static SchemaStatement createMaterializedView(String keyspace, String viewName, HelenusEntity entity) { if (entity.getType() != HelenusEntityType.VIEW) { throw new HelenusMappingException("expected view entity " + entity); @@ -162,20 +184,16 @@ public final class SchemaUtil { Class iface = entity.getMappingInterface(); String tableName = Helenus.entity(iface.getInterfaces()[0]).getName().toCql(); Select.Where where = selection.from(tableName).where(); - List p = new ArrayList(props.size()); - List c = new ArrayList(props.size()); List o = new ArrayList(props.size()); for (HelenusPropertyNode prop : props) { String columnName = prop.getColumnName(); switch (prop.getProperty().getColumnType()) { case PARTITION_KEY : - p.add(columnName); where = where.and(new IsNotNullClause(columnName)); break; case CLUSTERING_COLUMN : - c.add(columnName); where = where.and(new IsNotNullClause(columnName)); ClusteringColumn clusteringColumn = prop.getProperty().getGetterMethod() @@ -189,8 +207,7 @@ public final class SchemaUtil { } } - String primaryKey = "PRIMARY KEY (" + ((p.size() > 1) ? "(" + String.join(", ", p) + ")" : p.get(0)) - + ((c.size() > 0) ? ", " + ((c.size() > 1) ? "(" + String.join(", ", c) + ")" : c.get(0)) : "") + ")"; + String primaryKey = "PRIMARY KEY " + createPrimaryKeyPhrase(entity.getOrderedProperties()); String clustering = ""; if (o.size() > 0) { diff --git a/src/main/java/net/helenus/core/UnitOfWork.java b/src/main/java/net/helenus/core/UnitOfWork.java index 36f1291..0242a00 100644 --- a/src/main/java/net/helenus/core/UnitOfWork.java +++ b/src/main/java/net/helenus/core/UnitOfWork.java @@ -15,13 +15,10 @@ */ package net.helenus.core; -import java.util.Map; +import java.util.List; import java.util.Optional; -import java.util.Set; -import net.helenus.core.cache.BoundFacet; import net.helenus.core.cache.Facet; -import net.helenus.support.Either; public interface UnitOfWork extends AutoCloseable { @@ -56,11 +53,7 @@ public interface UnitOfWork extends AutoCloseable { boolean hasCommitted(); - Optional>> cacheLookup(String key); + Optional cacheLookup(List facets); - Optional>> cacheLookupByFacet(Set facets); - - Optional>> cacheLookupByStatement(String[] statementKeys); - - void cacheUpdate(Either> pojo, String[] statementKeys, Map facets); + void cacheUpdate(Object pojo, List facets); } diff --git a/src/main/java/net/helenus/core/cache/BoundFacet.java b/src/main/java/net/helenus/core/cache/BoundFacet.java index d6bc7d1..905a67f 100644 --- a/src/main/java/net/helenus/core/cache/BoundFacet.java +++ b/src/main/java/net/helenus/core/cache/BoundFacet.java @@ -20,15 +20,19 @@ import java.util.stream.Collectors; import net.helenus.mapping.HelenusProperty; -public class BoundFacet extends Facet { +public class BoundFacet extends Facet { private final Map properties; - BoundFacet(Map properties) { + BoundFacet(String name, Map properties) { + super(name, + (properties.keySet().size() > 1) + ? "[" + String.join(", ", + properties.keySet().stream().map(key -> properties.get(key).toString()) + .collect(Collectors.toSet())) + + "]" + : String.join("", properties.keySet().stream().map(key -> properties.get(key).toString()) + .collect(Collectors.toSet()))); this.properties = properties; } - public String toString() { - return String.join(";", - properties.keySet().stream().map(key -> properties.get(key).toString()).collect(Collectors.toSet())); - } } diff --git a/src/main/java/net/helenus/core/cache/Facet.java b/src/main/java/net/helenus/core/cache/Facet.java index fd5eaa2..d0c3e30 100644 --- a/src/main/java/net/helenus/core/cache/Facet.java +++ b/src/main/java/net/helenus/core/cache/Facet.java @@ -1,23 +1,56 @@ +/* + * Copyright (C) 2015 The Helenus Authors + * + * Licensed 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 net.helenus.core.cache; -public class Facet { -} -/* - * +/** * An Entity is identifiable via one or more Facets - * + * * A Facet is is a set of Properties and bound Facets - * + * * An Entity will have it's Keyspace, Table and Schema Version Facets bound. - * + * * A property may also have a TTL or write time bound. - * + * * The cache contains key->value mappings of merkel-hash -> Entity or * Set The only way a Set is put into the cache is with a key = * hash([Entity's bound Facets, hash(filter clause from SELECT)]) - * + * * REMEMBER to update the cache on build() for all impacted facets, delete * existing keys and add new keys - * - * */ +public class Facet { + private final String name; + private T value; + + public Facet(String name) { + this.name = name; + } + + public Facet(String name, T value) { + this.name = name; + this.value = value; + } + + public String name() { + return name; + } + + public T value() { + return value; + } + +} diff --git a/src/main/java/net/helenus/core/cache/EntityIdentifyingFacet.java b/src/main/java/net/helenus/core/cache/UnboundFacet.java similarity index 59% rename from src/main/java/net/helenus/core/cache/EntityIdentifyingFacet.java rename to src/main/java/net/helenus/core/cache/UnboundFacet.java index fb28dc1..e2b618b 100644 --- a/src/main/java/net/helenus/core/cache/EntityIdentifyingFacet.java +++ b/src/main/java/net/helenus/core/cache/UnboundFacet.java @@ -15,44 +15,45 @@ */ package net.helenus.core.cache; +import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; +import java.util.List; import java.util.Map; -import java.util.Set; +import net.helenus.core.SchemaUtil; import net.helenus.mapping.HelenusProperty; -public class EntityIdentifyingFacet extends Facet { +public class UnboundFacet extends Facet { - private final Set properties; + private final List properties; - public EntityIdentifyingFacet(HelenusProperty prop) { - properties = new HashSet(); - properties.add(prop); + public UnboundFacet(List properties) { + super(SchemaUtil.createPrimaryKeyPhrase(properties)); + this.properties = properties; } - public EntityIdentifyingFacet(Set props) { - properties = props; + public UnboundFacet(HelenusProperty property) { + super(property.getPropertyName()); + properties = new ArrayList(); + properties.add(property); } - public boolean isFullyBound() { - return false; - } - - public Set getProperties() { + public List getProperties() { return properties; } public Binder binder() { - return new Binder(properties); + return new Binder(name(), properties); } public static class Binder { - private final Set properties = new HashSet(); + private final String name; + private final List properties = new ArrayList(); private Map boundProperties = new HashMap(); - Binder(Set properties) { + Binder(String name, List properties) { + this.name = name; this.properties.addAll(properties); } @@ -62,12 +63,12 @@ public class EntityIdentifyingFacet extends Facet { return this; } - public boolean isFullyBound() { + public boolean isBound() { return properties.isEmpty(); } public BoundFacet bind() { - return new BoundFacet(boundProperties); + return new BoundFacet(name, boundProperties); } } } diff --git a/src/main/java/net/helenus/core/operation/AbstractOptionalOperation.java b/src/main/java/net/helenus/core/operation/AbstractOptionalOperation.java index f644c2e..d74b807 100644 --- a/src/main/java/net/helenus/core/operation/AbstractOptionalOperation.java +++ b/src/main/java/net/helenus/core/operation/AbstractOptionalOperation.java @@ -15,8 +15,8 @@ */ package net.helenus.core.operation; +import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.TimeoutException; @@ -80,9 +80,8 @@ public abstract class AbstractOptionalOperation facets = bindFacetValues(); - statementKeys = getQueryKeys(); - cacheResult = checkCache(uow, facets, statementKeys); + List facets = bindFacetValues(); + cacheResult = checkCache(uow, facets); if (cacheResult != null) { result = Optional.of(cacheResult); } @@ -101,7 +100,7 @@ public abstract class AbstractOptionalOperation> extends Operation { @@ -320,42 +318,16 @@ public abstract class AbstractStatementOperation uow, Set facets, String[] statementKeys) { + protected E checkCache(UnitOfWork uow, List facets) { E result = null; - Optional>> optionalCachedResult = Optional.empty(); + Optional optionalCachedResult = Optional.empty(); if (!facets.isEmpty()) { - // TODO(gburd): what about select ResultSet, Tuple... etc.? - optionalCachedResult = uow.cacheLookupByFacet(facets); + optionalCachedResult = uow.cacheLookup(facets); if (optionalCachedResult.isPresent()) { - Either> eitherCachedResult = optionalCachedResult.get(); - if (eitherCachedResult.isLeft()) { - uowCacheHits.mark(); - logger.info("UnitOfWork({}) cache hit using facets", uow.hashCode()); - result = (E) eitherCachedResult.getLeft(); - } - } - } - - if (result == null && statementKeys != null) { - // Then check to see if this query happens to uniquely identify a single object - // in thecache. - optionalCachedResult = uow.cacheLookupByStatement(statementKeys); - if (optionalCachedResult.isPresent()) { - Either> eitherCachedResult = optionalCachedResult.get(); - // Statements always store Set as the value in the cache. - if (eitherCachedResult.isRight()) { - Set cachedResult = eitherCachedResult.getRight(); - if (cachedResult.size() == 1) { - Optional maybeResult = cachedResult.stream().findFirst(); - if (maybeResult.isPresent()) { - uowCacheHits.mark(); - logger.info("UnitOfWork({}) cache hit for stmt", uow.hashCode()); - } else { - result = null; - } - } - } + uowCacheHits.mark(); + logger.info("UnitOfWork({}) cache hit using facets", uow.hashCode()); + result = (E) optionalCachedResult.get(); } } @@ -367,30 +339,29 @@ public abstract class AbstractStatementOperation uow, E pojo, Map facetMap, - String[] statementKeys) { - - // Insert this entity into the cache for each facet for this entity that we can - // fully bind. - Map boundFacets = new HashMap(); + protected void updateCache(UnitOfWork uow, E pojo, List identifyingFacets) { + List facets = new ArrayList<>(); Map valueMap = pojo instanceof MapExportable ? ((MapExportable) pojo).toMap() : null; - facetMap.forEach((facetName, facet) -> { - if (!facet.isFullyBound()) { - EntityIdentifyingFacet.Binder binder = facet.binder(); - facet.getProperties().forEach(prop -> { + + for (Facet facet : identifyingFacets) { + if (facet instanceof UnboundFacet) { + UnboundFacet unboundFacet = (UnboundFacet) facet; + UnboundFacet.Binder binder = unboundFacet.binder(); + unboundFacet.getProperties().forEach(prop -> { if (valueMap == null) { Object value = BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, false); - binder.setValueForProperty(prop, prop.getColumnName().toCql() + "==" + value.toString()); + binder.setValueForProperty(prop, value.toString()); } else { - binder.setValueForProperty(prop, - prop.getColumnName().toCql() + "==" + valueMap.get(prop.getPropertyName()).toString()); + binder.setValueForProperty(prop, valueMap.get(prop.getPropertyName()).toString()); } + facets.add(binder.bind()); }); - boundFacets.put(facetName, binder.bind()); + } else { + facets.add(facet); } - }); + } // Cache the value (pojo), the statement key, and the fully bound facets. - uow.cacheUpdate(Either.left(pojo), statementKeys, boundFacets); + uow.cacheUpdate(pojo, facets); } } diff --git a/src/main/java/net/helenus/core/operation/AbstractStreamOperation.java b/src/main/java/net/helenus/core/operation/AbstractStreamOperation.java index 4f349b9..0d3e1d8 100644 --- a/src/main/java/net/helenus/core/operation/AbstractStreamOperation.java +++ b/src/main/java/net/helenus/core/operation/AbstractStreamOperation.java @@ -15,7 +15,7 @@ */ package net.helenus.core.operation; -import java.util.Set; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.TimeoutException; @@ -76,12 +76,10 @@ public abstract class AbstractStreamOperation result = null; E cachedResult = null; - String[] statementKeys = null; if (enableCache) { - Set facets = bindFacetValues(); - statementKeys = getQueryKeys(); - cachedResult = checkCache(uow, facets, statementKeys); + List facets = bindFacetValues(); + cachedResult = checkCache(uow, facets); if (cachedResult != null) { result = Stream.of(cachedResult); } @@ -96,7 +94,7 @@ public abstract class AbstractStreamOperation extends AbstractStreamOperation bindFacetValues() { + public List bindFacetValues() { return delegate.bindFacetValues(); } diff --git a/src/main/java/net/helenus/core/operation/InsertOperation.java b/src/main/java/net/helenus/core/operation/InsertOperation.java index 4812df3..c92dac4 100644 --- a/src/main/java/net/helenus/core/operation/InsertOperation.java +++ b/src/main/java/net/helenus/core/operation/InsertOperation.java @@ -236,23 +236,6 @@ public final class InsertOperation extends AbstractOperation keys = new ArrayList<>(values.size()); - values.forEach(t -> { - HelenusPropertyNode prop = t._1; - switch (prop.getProperty().getColumnType()) { - case PARTITION_KEY : - case CLUSTERING_COLUMN : - keys.add(entity.getName().toCql() + '.' + prop.getColumnName() + "==" + t._2.toString()); - break; - default : - break; - } - }); - return keys.toArray(new String[keys.size()]); - } - @Override public T sync(UnitOfWork uow) throws TimeoutException { if (uow == null) { @@ -261,7 +244,7 @@ public final class InsertOperation extends AbstractOperation iface = entity.getMappingInterface(); if (resultType == iface) { - updateCache(uow, result, entity.getIdentifyingFacets(), getQueryKeys()); + updateCache(uow, result, entity.getFacets()); } return result; } diff --git a/src/main/java/net/helenus/core/operation/Operation.java b/src/main/java/net/helenus/core/operation/Operation.java index 0953dc7..a9f1b51 100644 --- a/src/main/java/net/helenus/core/operation/Operation.java +++ b/src/main/java/net/helenus/core/operation/Operation.java @@ -15,8 +15,7 @@ */ package net.helenus.core.operation; -import java.util.Map; -import java.util.Set; +import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -32,7 +31,6 @@ import brave.Tracer; import brave.propagation.TraceContext; import net.helenus.core.AbstractSessionOperations; import net.helenus.core.UnitOfWork; -import net.helenus.core.cache.EntityIdentifyingFacet; import net.helenus.core.cache.Facet; public abstract class Operation { @@ -87,15 +85,12 @@ public abstract class Operation { return null; } - public String[] getQueryKeys() { + public List getFacets() { return null; } - public Map getIdentifyingFacets() { + public List bindFacetValues() { return null; } - public Set bindFacetValues() { - return null; - } } diff --git a/src/main/java/net/helenus/core/operation/SelectFirstOperation.java b/src/main/java/net/helenus/core/operation/SelectFirstOperation.java index e34d933..1419218 100644 --- a/src/main/java/net/helenus/core/operation/SelectFirstOperation.java +++ b/src/main/java/net/helenus/core/operation/SelectFirstOperation.java @@ -15,15 +15,13 @@ */ package net.helenus.core.operation; -import java.util.Map; +import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.function.Function; import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.querybuilder.BuiltStatement; -import net.helenus.core.cache.EntityIdentifyingFacet; import net.helenus.core.cache.Facet; public final class SelectFirstOperation extends AbstractFilterOptionalOperation> { @@ -48,17 +46,12 @@ public final class SelectFirstOperation extends AbstractFilterOptionalOperati } @Override - public String[] getQueryKeys() { - return delegate.getQueryKeys(); + public List getFacets() { + return delegate.getFacets(); } @Override - public Map getIdentifyingFacets() { - return delegate.getIdentifyingFacets(); - } - - @Override - public Set bindFacetValues() { + public List bindFacetValues() { return delegate.bindFacetValues(); } diff --git a/src/main/java/net/helenus/core/operation/SelectFirstTransformingOperation.java b/src/main/java/net/helenus/core/operation/SelectFirstTransformingOperation.java index 15dfed5..038324a 100644 --- a/src/main/java/net/helenus/core/operation/SelectFirstTransformingOperation.java +++ b/src/main/java/net/helenus/core/operation/SelectFirstTransformingOperation.java @@ -15,8 +15,8 @@ */ package net.helenus.core.operation; +import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.function.Function; import com.datastax.driver.core.ResultSet; @@ -41,12 +41,7 @@ public final class SelectFirstTransformingOperation } @Override - public String[] getQueryKeys() { - return delegate.getQueryKeys(); - } - - @Override - public Set bindFacetValues() { + public List bindFacetValues() { return delegate.bindFacetValues(); } diff --git a/src/main/java/net/helenus/core/operation/SelectOperation.java b/src/main/java/net/helenus/core/operation/SelectOperation.java index 1a9908e..59709fe 100644 --- a/src/main/java/net/helenus/core/operation/SelectOperation.java +++ b/src/main/java/net/helenus/core/operation/SelectOperation.java @@ -31,8 +31,8 @@ import com.datastax.driver.core.querybuilder.Select.Where; import com.google.common.collect.Iterables; import net.helenus.core.*; -import net.helenus.core.cache.EntityIdentifyingFacet; import net.helenus.core.cache.Facet; +import net.helenus.core.cache.UnboundFacet; import net.helenus.core.reflect.HelenusPropertyNode; import net.helenus.mapping.HelenusEntity; import net.helenus.mapping.MappingUtil; @@ -177,31 +177,37 @@ public final class SelectOperation extends AbstractFilterStreamOperation getIdentifyingFacets() { + public List getFacets() { HelenusEntity entity = props.get(0).getEntity(); - return entity.getIdentifyingFacets(); + return entity.getFacets(); } @Override - public Set bindFacetValues() { + public List bindFacetValues() { HelenusEntity entity = props.get(0).getEntity(); - Set boundFacets = new HashSet(); - // Check to see if this select statement has enough information to build one or - // more identifying facets. - entity.getIdentifyingFacets().forEach((facetName, facet) -> { - EntityIdentifyingFacet.Binder binder = facet.binder(); - facet.getProperties().forEach(prop -> { - Filter filter = filters.get(prop); - if (filter != null) { - binder.setValueForProperty(prop, filter.toString()); - } else if (facetName.equals("*")) { - binder.setValueForProperty(prop, ""); + List boundFacets = new ArrayList<>(); + + for (Facet facet : entity.getFacets()) { + if (facet instanceof UnboundFacet) { + UnboundFacet unboundFacet = (UnboundFacet) facet; + UnboundFacet.Binder binder = unboundFacet.binder(); + unboundFacet.getProperties().forEach(prop -> { + Filter filter = filters.get(prop); + if (filter != null) { + Object[] postulates = filter.postulateValues(); + for (Object p : postulates) { + binder.setValueForProperty(prop, p.toString()); + } + } + + }); + if (binder.isBound()) { + boundFacets.add(binder.bind()); } - }); - if (binder.isFullyBound()) { - boundFacets.add(binder.bind()); + } else { + boundFacets.add(facet); } - }); + } return boundFacets; } diff --git a/src/main/java/net/helenus/core/operation/SelectTransformingOperation.java b/src/main/java/net/helenus/core/operation/SelectTransformingOperation.java index 1d43ae9..4a7720e 100644 --- a/src/main/java/net/helenus/core/operation/SelectTransformingOperation.java +++ b/src/main/java/net/helenus/core/operation/SelectTransformingOperation.java @@ -15,15 +15,13 @@ */ package net.helenus.core.operation; -import java.util.Map; -import java.util.Set; +import java.util.List; import java.util.function.Function; import java.util.stream.Stream; import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.querybuilder.BuiltStatement; -import net.helenus.core.cache.EntityIdentifyingFacet; import net.helenus.core.cache.Facet; public final class SelectTransformingOperation @@ -43,18 +41,13 @@ public final class SelectTransformingOperation } @Override - public String[] getQueryKeys() { - return delegate.getQueryKeys(); - } - - @Override - public Set bindFacetValues() { + public List bindFacetValues() { return delegate.bindFacetValues(); } @Override - public Map getIdentifyingFacets() { - return delegate.getIdentifyingFacets(); + public List getFacets() { + return delegate.getFacets(); } @Override diff --git a/src/main/java/net/helenus/core/operation/UpdateOperation.java b/src/main/java/net/helenus/core/operation/UpdateOperation.java index 9237526..e7a2822 100644 --- a/src/main/java/net/helenus/core/operation/UpdateOperation.java +++ b/src/main/java/net/helenus/core/operation/UpdateOperation.java @@ -578,7 +578,7 @@ public final class UpdateOperation extends AbstractFilterOperation getIdentifyingFacets(); + List getFacets(); } diff --git a/src/main/java/net/helenus/mapping/HelenusMappingEntity.java b/src/main/java/net/helenus/mapping/HelenusMappingEntity.java index 509cac2..87797f1 100644 --- a/src/main/java/net/helenus/mapping/HelenusMappingEntity.java +++ b/src/main/java/net/helenus/mapping/HelenusMappingEntity.java @@ -28,7 +28,8 @@ import com.google.common.collect.ImmutableMap; import net.helenus.config.HelenusSettings; import net.helenus.core.Helenus; import net.helenus.core.annotation.Cacheable; -import net.helenus.core.cache.EntityIdentifyingFacet; +import net.helenus.core.cache.Facet; +import net.helenus.core.cache.UnboundFacet; import net.helenus.mapping.annotation.*; import net.helenus.support.HelenusMappingException; @@ -41,9 +42,7 @@ public final class HelenusMappingEntity implements HelenusEntity { private final ImmutableMap methods; private final ImmutableMap props; private final ImmutableList orderedProps; - private final EntityIdentifyingFacet primaryIdentityFacet; - private final ImmutableMap allIdentityFacets; - private final ImmutableMap ancillaryIdentityFacets; + private final List facets; public HelenusMappingEntity(Class iface, Metadata metadata) { this(iface, autoDetectType(iface), metadata); @@ -112,33 +111,31 @@ public final class HelenusMappingEntity implements HelenusEntity { // Caching cacheable = (null != iface.getDeclaredAnnotation(Cacheable.class)); - ImmutableMap.Builder allFacetsBuilder = ImmutableMap.builder(); - ImmutableMap.Builder ancillaryFacetsBuilder = ImmutableMap.builder(); - EntityIdentifyingFacet primaryFacet = null; - List primaryProperties = new ArrayList(4); - for (HelenusProperty prop : propsLocal) { + List primaryKeyProperties = new ArrayList<>(); + ImmutableList.Builder facetsBuilder = ImmutableList.builder(); + facetsBuilder.add(new Facet("table", name.toCql())); + for (HelenusProperty prop : orderedProps) { switch (prop.getColumnType()) { case PARTITION_KEY : case CLUSTERING_COLUMN : - primaryProperties.add(prop); + primaryKeyProperties.add(prop); break; default : - if (primaryProperties != null) { - primaryFacet = new EntityIdentifyingFacet(new HashSet(primaryProperties)); - allFacetsBuilder.put("*", primaryFacet); - primaryProperties = null; + if (primaryKeyProperties != null && primaryKeyProperties.size() > 0) { + facetsBuilder.add(new UnboundFacet(primaryKeyProperties)); + primaryKeyProperties = null; } Optional optionalIndexName = prop.getIndexName(); if (optionalIndexName.isPresent()) { - EntityIdentifyingFacet facet = new EntityIdentifyingFacet(prop); - ancillaryFacetsBuilder.put(prop.getPropertyName(), facet); - allFacetsBuilder.put(prop.getPropertyName(), facet); + UnboundFacet facet = new UnboundFacet(prop); + facetsBuilder.add(facet); } } } - this.primaryIdentityFacet = primaryFacet; - this.ancillaryIdentityFacets = ancillaryFacetsBuilder.build(); - this.allIdentityFacets = allFacetsBuilder.build(); + if (primaryKeyProperties != null && primaryKeyProperties.size() > 0) { + facetsBuilder.add(new UnboundFacet(primaryKeyProperties)); + } + this.facets = facetsBuilder.build(); } @Override @@ -172,8 +169,8 @@ public final class HelenusMappingEntity implements HelenusEntity { } @Override - public Map getIdentifyingFacets() { - return allIdentityFacets; + public List getFacets() { + return facets; } @Override