com.antgroup.tugraph.ogm.context.RestModelMapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tugraph-db-ogm-core Show documentation
Show all versions of tugraph-db-ogm-core Show documentation
TuGraph-DB-OGM is an Object Graph Mapping Library for TuGraph.
The newest version!
/*
* Copyright (c) 2002-2022 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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 com.antgroup.tugraph.ogm.context;
import java.lang.reflect.Array;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import com.antgroup.tugraph.ogm.response.Response;
import com.antgroup.tugraph.ogm.response.model.DefaultGraphModel;
import com.antgroup.tugraph.ogm.response.model.NodeModel;
import com.antgroup.tugraph.ogm.response.model.RelationshipModel;
import com.antgroup.tugraph.ogm.session.EntityInstantiator;
import com.antgroup.tugraph.ogm.session.Utils;
import com.antgroup.tugraph.ogm.metadata.MetaData;
import com.antgroup.tugraph.ogm.model.GraphModel;
import com.antgroup.tugraph.ogm.model.PropertyContainer;
import com.antgroup.tugraph.ogm.model.RestModel;
/**
* Map NodeModels and RelationshipModels obtained from cypher queries to domain entities
*
* @author Luanne Misquitta
* @author Michael J. Simons
*/
public class RestModelMapper {
private final MappingContext mappingContext;
private final GraphEntityMapper delegate;
public RestModelMapper(MetaData metaData, MappingContext mappingContext,
EntityInstantiator entityInstantiator) {
this.mappingContext = mappingContext;
this.delegate = new GraphEntityMapper(metaData, mappingContext, entityInstantiator);
}
public RestStatisticsModel map(Response response) {
List resultRowBuilders = new ArrayList<>();
Response graphModelResponse = new Response() {
@Override
public GraphModel next() {
RestModel model = response.next();
if (model == null) {
return null;
}
ResultRowBuilder resultRowBuilder = new ResultRowBuilder(
RestModelMapper.this::getEntityOrNodeModel,
mappingContext::getRelationshipEntity
);
resultRowBuilders.add(resultRowBuilder);
model.getRow().forEach(resultRowBuilder::handle);
return resultRowBuilder.buildGraphModel();
}
@Override
public void close() {
response.close();
}
@Override
public String[] columns() {
return response.columns();
}
};
// Run the actual mapping
delegate.map(Object.class, graphModelResponse);
// Recreate the original structure
RestStatisticsModel restStatisticsModel = new RestStatisticsModel();
response.getStatistics().ifPresent(restStatisticsModel::setStatistics);
restStatisticsModel.setResult(resultRowBuilders.stream().map(ResultRowBuilder::finish).collect(Collectors.toList()));
return restStatisticsModel;
}
/**
* Retrieves a mapped entity from this sessions mapping context after a call to {@link GraphEntityMapper#map(Class, Response)}.
* If there's no mapped entity, this method tries to find the {@link NodeModel} with the same id in the {@link GraphModel} which
* has been the base of the mapping process.
*
* @param id The id of the entity to retrieve
* @param graphModel The graph model that has been used for mapping
* @return An entity or a NodeModel
* @throws RuntimeException When neither entity nor {@link NodeModel} can be found
*/
private Object getEntityOrNodeModel(Long id, DefaultGraphModel graphModel) {
return Optional.ofNullable(mappingContext.getNodeEntity(id)).orElseGet(() ->
graphModel.findNode(id).orElseThrow(() -> new RuntimeException("Lost NodeModel for id " + id))
);
}
static class ResultRowBuilder {
/**
* Stores all nodes extracted from result.
*/
private final List nodeModels = new ArrayList<>();
/**
* Stores all relationships extracted from result.
*/
private final Map relationshipModels = new LinkedHashMap<>();
/**
* The graph model build from the above.
*/
private DefaultGraphModel graphModel;
/**
* Used to resolve mapped entities.
*/
private final BiFunction resolveNodeId;
/**
* Used to resolved mapped relationship entities.
*/
private final Function resolveRelationshipId;
/**
* Contains the aliases mapped to one or more node entities.
*/
private Map> aliasToNodeIdMapping = new HashMap<>();
/**
* Contains the aliases mapped to one or more relationship entities.
*/
private Map> aliasToRelationshipIdMapping = new HashMap<>();
/**
* Contains the aliases mapped to lists of things.
*/
private Set aliasesOfListResults = new HashSet<>();
/**
* A tree pointing to lists of lists.
*/
private Map> nestedListsPointers = new TreeMap<>(Comparator.reverseOrder());
/**
* The result row being build.
*/
private Map resultRow = new HashMap<>();
ResultRowBuilder(BiFunction resolveNodeId,
Function resolveRelationshipId) {
this.resolveNodeId = resolveNodeId;
this.resolveRelationshipId = id -> {
Object result = resolveRelationshipId.apply(id);
if (result == null) {
return relationshipModels.get(id);
} else {
return result;
}
};
}
void handle(String alias, Object resultObject) {
handle(alias, resultObject, 0, null);
}
/**
* Handles one result object.
*
* @param alias The alias in the result set
* @param resultObject The result object
*/
void handle(String alias, Object resultObject, int level, Integer index) {
if (!canBeMapped(resultObject)) {
// The entity mapper can only deal with models
this.resultRow.put(alias, convertToTargetContainer(resultObject));
} else if (resultObject instanceof List) {
// Mark that result object as list, as it needs to be reconstructed later
String next = alias;
if (level > 0) { // This caters for hierarchies (levels)
next += ("-" + level);
}
if (index != null) { // This caters for siblings (index in lists of lists)
next += ("-" + index);
}
if (!next.equals(alias)) {
next += ("-" + ThreadLocalRandom.current().nextInt()); // Make sure we have unique generated aliases
nestedListsPointers.computeIfAbsent(alias, k -> new ArrayList<>()).add(next);
alias = next;
resultRow.put(alias, new ArrayList<>());
}
this.aliasesOfListResults.add(alias);
int idx = 0;
for (Object o : (List