@Nonnull @Override default Optional<Id> referenceFor(ManyFeatureBean feature, Id reference) { Converter<Id, M> converter = manyReferenceConverter(); return this.valueFor(feature, converter.convert(reference)) .map(converter::revert); }
/** * Converts this document in a {@link SingleFeatureBean}. * * @return a bean */ @Nonnull public SingleFeatureBean toBean() { return SingleFeatureBean.of(CONVERTER.revert(owner), id); } }
/** * Constructs a new {@code ModelGraph}. * * @param baseGraph the base graph */ public ModelGraph(KeyIndexableGraph baseGraph) { super(baseGraph, true, false); classVerticesByName = getOrCreateIndex("instances-all", Vertex.class, ClassVertex::from); Graph originGraph = getOrigin(baseGraph); if (originGraph instanceof TinkerGraph) { // Tinkergraph is stored as XML: prefer using String-based identifiers idConverter = Converter.compose(IdConverters.withHexString(), Converter.from(Function.identity(), String.class::cast)); requiresUniqueLabels = true; } else { idConverter = Converter.compose(IdConverters.withLong(), Converter.from(Function.identity(), Long.class::cast)); requiresUniqueLabels = false; } }
@Nonnull @Override default Optional<Id> referenceFor(SingleFeatureBean feature, Id reference) { Converter<Id, M> converter = referenceConverter(); return this.valueFor(feature, converter.convert(reference)) .map(converter::revert); }
/** * Returns the converted identifier of this element vertex. * * @return the converted identifier */ @Nonnull public Id getElementId() { return graph.getIdConverter().revert(getId()); }
/** * Returns the vertex referenced by the provided {@code id}. * * @param id the identifier of the vertex to retrieve from the graph * * @return an {@link Optional} containing the vertex referenced by the provided identifier, or {@link * Optional#empty()} when no such vertex exists * * @see #getVertex(Object) * @see Converter#convert(Object) */ @Nonnull public Optional<ElementVertex> getVertex(Id id) { final Object vid = idConverter.convert(id); return Optional.ofNullable(elementCache.get(vid, i -> getElementVertex(i, () -> null))); }
@Override default int appendReference(SingleFeatureBean feature, Id reference) { Converter<Id, M> converter = manyReferenceConverter(); return this.appendValue(feature, converter.convert(reference)); }
@Override default void addReference(ManyFeatureBean feature, Id reference) { Converter<Id, M> converter = manyReferenceConverter(); this.addValue(feature, converter.convert(reference)); }
/** * Returns the vertex referenced by the provided {@code id}. If no such vertex exists, it will be created and added * to the graph. * * @param id the identifier of the vertex to retrieve, or create, from the graph * * @return the vertex * * @see #getVertex(Object) * @see #addVertex(Object) * @see Converter#convert(Object) */ @Nonnull public ElementVertex getOrCreateVertex(Id id) { final Object vid = idConverter.convert(id); return elementCache.get(vid, i -> getElementVertex(i, () -> addElementVertex(i))); }
@Nonnull @Override default Optional<Id> referenceFor(ManyFeatureBean feature, Id reference) { checkNotNull(feature, "feature"); checkNotNull(reference, "reference"); Converter<List<Id>, M> converter = manyReferenceMerger(); List<Id> ids = this.<M>valueOf(feature.withoutPosition()).map(converter::revert).orElse(null); if (isNull(ids) || feature.position() >= ids.size()) { throw new IndexOutOfBoundsException(); } Optional<Id> previousId = Optional.of(ids.get(feature.position())); ids.set(feature.position(), reference); valueFor(feature.withoutPosition(), converter.convert(ids)); return previousId; }
@Nonnull @Override public <V> Stream<V> allValuesOf(SingleFeatureBean feature) { checkNotNull(feature, "feature"); final String ownerId = idConverter.convert(feature.owner()); final String featureId = Integer.toString(feature.id()); final String fieldName = concat(ModelDocument.F_MANY_FEATURE, featureId); final Bson filter = and(eq(ModelDocument.F_ID, ownerId), exists(fieldName)); final Bson projection = include(fieldName); final ModelDocument instance = documents.find(filter).projection(projection).first(); return Optional.ofNullable(instance) .map(ModelDocument::getManyFeatures) .map(m -> m.get(featureId)) .map(Collection::stream) .map(s -> s.map(this::<V>deserializeValue)) .orElseGet(Stream::empty); }
@Nonnull @Override public <V> Optional<V> valueOf(SingleFeatureBean feature) { checkNotNull(feature, "feature"); final String ownerId = idConverter.convert(feature.owner()); final String featureId = Integer.toString(feature.id()); final String fieldName = concat(ModelDocument.F_SINGLE_FEATURE, featureId); final Bson filter = and(eq(ModelDocument.F_ID, ownerId), exists(fieldName)); final Bson projection = include(fieldName); final ModelDocument instance = documents.find(filter).projection(projection).first(); return Optional.ofNullable(instance) .map(ModelDocument::getSingleFeatures) .map(m -> m.get(featureId)) .map(this::deserializeValue); }
@Nonnull @Override public Optional<SingleFeatureBean> containerOf(Id id) { checkNotNull(id, "id"); final String ownerId = idConverter.convert(id); final Bson filter = and(eq(ModelDocument.F_ID, ownerId), exists(ModelDocument.F_CONTAINER)); final Bson projection = include(ModelDocument.F_CONTAINER); final ModelDocument instance = documents.find(filter).projection(projection).first(); return Optional.ofNullable(instance) .map(ModelDocument::getContainer) .map(ContainerDocument::toBean); }
@Nonnull @Override public Optional<ClassBean> metaClassOf(Id id) { checkNotNull(id, "id"); final String ownerId = idConverter.convert(id); final Bson filter = and(eq(ModelDocument.F_ID, ownerId), exists(ModelDocument.F_METACLASS)); final Bson projection = include(ModelDocument.F_METACLASS); final ModelDocument instance = documents.find(filter).projection(projection).first(); return Optional.ofNullable(instance) .map(ModelDocument::getMetaClass) .map(ClassDocument::toBean); }
@Nonnull @Override default Optional<Id> removeReference(ManyFeatureBean feature) { checkNotNull(feature, "feature"); Converter<List<Id>, M> converter = manyReferenceMerger(); List<Id> ids = this.<M>valueOf(feature.withoutPosition()) .map(converter::revert) .orElse(null); if (isNull(ids)) { return Optional.empty(); } Optional<Id> previousId = Optional.empty(); if (feature.position() < ids.size()) { previousId = Optional.of(ids.remove(feature.position())); if (ids.isEmpty()) { removeAllReferences(feature.withoutPosition()); } else { valueFor(feature.withoutPosition(), converter.convert(ids)); } } return previousId; }
@Nonnull @Override public <V> Optional<V> valueOf(ManyFeatureBean feature) { checkNotNull(feature, "feature"); final String ownerId = idConverter.convert(feature.owner()); final String featureId = Integer.toString(feature.id()); final String fieldName = concat(ModelDocument.F_MANY_FEATURE, featureId); final String fieldNameWithPos = concat(fieldName, Integer.toString(feature.position())); final Bson filter = and(eq(ModelDocument.F_ID, ownerId), exists(fieldNameWithPos)); final Bson projection = slice(fieldName, feature.position(), 1); final ModelDocument instance = documents.find(filter).projection(projection).first(); return Optional.ofNullable(instance) .map(ModelDocument::getManyFeatures) .map(m -> m.get(featureId)) .filter(l -> !l.isEmpty()) .map(l -> l.get(0)) .map(this::deserializeValue); }
@Override default void addReference(ManyFeatureBean feature, Id reference) { checkNotNull(feature, "feature"); checkNotNull(reference, "reference"); Converter<List<Id>, M> converter = manyReferenceMerger(); List<Id> ids = this.<M>valueOf(feature.withoutPosition()) .map(converter::revert) .orElseGet(ArrayList::new); checkPositionIndex(feature.position(), ids.size()); ids.add(feature.position(), reference); valueFor(feature.withoutPosition(), converter.convert(ids)); }
/** * Converts the specified {@code bean} in a {@link ContainerDocument}. * * @param bean the bean * * @return the document */ @Nonnull public static ContainerDocument fromBean(SingleFeatureBean bean) { ContainerDocument c = new ContainerDocument(); c.setOwner(CONVERTER.convert(bean.owner())); c.setId(bean.id()); return c; }
@Nonnull @Override public <V> Optional<V> valueFor(ManyFeatureBean feature, V value) { checkNotNull(feature, "feature"); checkNotNull(value, "value"); final String ownerId = idConverter.convert(feature.owner()); final String featureId = Integer.toString(feature.id()); final String fieldName = concat(ModelDocument.F_MANY_FEATURE, featureId); final String fieldNameWithPos = concat(fieldName, Integer.toString(feature.position())); final Bson filter = and(eq(ModelDocument.F_ID, ownerId), exists(fieldNameWithPos)); final Bson projection = slice(fieldName, feature.position(), 1); final Bson update = set(fieldNameWithPos, serializeValue(value)); final ModelDocument instance = documents.findOneAndUpdate(filter, update, new FindOneAndUpdateOptions().projection(projection)); final Optional<V> previousValue = Optional.ofNullable(instance) .map(ModelDocument::getManyFeatures) .map(m -> m.get(featureId)) .filter(l -> !l.isEmpty()) .map(l -> l.get(0)) .map(this::deserializeValue); if (!previousValue.isPresent()) { throw new IndexOutOfBoundsException(); } return previousValue; }
@Nonnull @Override public <V> Optional<V> valueFor(SingleFeatureBean feature, V value) { checkNotNull(feature, "feature"); checkNotNull(value, "value"); final String ownerId = idConverter.convert(feature.owner()); final String featureId = Integer.toString(feature.id()); final String fieldName = concat(ModelDocument.F_SINGLE_FEATURE, featureId); final Bson filter = eq(ModelDocument.F_ID, ownerId); final Bson projection = include(fieldName); final Bson update = set(fieldName, serializeValue(value)); final ModelDocument instance = documents.findOneAndUpdate(filter, update, new FindOneAndUpdateOptions().upsert(true).projection(projection)); return Optional.ofNullable(instance) .map(ModelDocument::getSingleFeatures) .map(l -> l.get(featureId)) .map(this::deserializeValue); }