/** * infer {@link RelationshipType}s that this {@link RelationshipAtom} can potentially have * NB: {@link EntityType}s and link {@link Role}s are treated separately as they behave differently: * {@link EntityType}s only play the explicitly defined {@link Role}s (not the relevant part of the hierarchy of the specified {@link Role}) and the {@link Role} inherited from parent * @return list of {@link RelationshipType}s this atom can have ordered by the number of compatible {@link Role}s */ private Set<Type> inferPossibleEntityTypePlayers(ConceptMap sub){ return inferPossibleRelationConfigurations(sub).asMap().entrySet().stream() .flatMap(e -> { Set<Role> rs = e.getKey().roles().collect(toSet()); rs.removeAll(e.getValue()); return rs.stream().flatMap(Role::players); }).collect(Collectors.toSet()); }
/** * * @param relationshipType The {@link RelationshipType} to validate * @return An error message if the relationTypes does not have at least 1 role */ static Optional<String> validateHasMinimumRoles(RelationshipType relationshipType) { if(relationshipType.isAbstract() || relationshipType.roles().iterator().hasNext()){ return Optional.empty(); } else { return Optional.of(VALIDATION_RELATION_TYPE.getMessage(relationshipType.label())); } }
@Override public Map<Role, Set<Thing>> allRolePlayers() { HashMap<Role, Set<Thing>> roleMap = new HashMap<>(); //We add the role types explicitly so we can return them when there are no roleplayers type().roles().forEach(roleType -> roleMap.put(roleType, new HashSet<>())); //All castings are used here because we need to iterate over all of them anyway castingsRelation().forEach(rp -> roleMap.computeIfAbsent(rp.getRole(), (k) -> new HashSet<>()).add(rp.getRolePlayer())); return roleMap; }
/** * Add relates edges to a var, given a type * @param var var to be modified * @param type type from which metadata extracted * @return var with appropriate relates edges */ private static VarPattern relates(VarPattern var, RelationshipType type){ for(Role role:type.roles().collect(Collectors.toSet())){ var = var.relates(Graql.label(role.label())); } return var; }
/** * @return a map of relationships and corresponding roles that could be played by this atom */ private Multimap<RelationshipType, Role> inferPossibleRelationConfigurations(ConceptMap sub){ Set<Role> roles = getExplicitRoles().filter(r -> !Schema.MetaSchema.isMetaLabel(r.label())).collect(toSet()); Map<Var, Type> varTypeMap = getParentQuery().getVarTypeMap(sub); Set<Type> types = getRolePlayers().stream().filter(varTypeMap::containsKey).map(varTypeMap::get).collect(toSet()); if (roles.isEmpty() && types.isEmpty()){ RelationshipType metaRelationType = tx().admin().getMetaRelationType(); Multimap<RelationshipType, Role> compatibleTypes = HashMultimap.create(); metaRelationType.subs() .filter(rt -> !rt.equals(metaRelationType)) .forEach(rt -> compatibleTypes.putAll(rt, rt.roles().collect(toSet()))); return compatibleTypes; } //intersect relation types from roles and types Multimap<RelationshipType, Role> compatibleTypes; Multimap<RelationshipType, Role> compatibleTypesFromRoles = compatibleRelationTypesWithRoles(roles, new RoleConverter()); Multimap<RelationshipType, Role> compatibleTypesFromTypes = compatibleRelationTypesWithRoles(types, new TypeConverter()); if (roles.isEmpty()){ compatibleTypes = compatibleTypesFromTypes; } //no types from roles -> roles correspond to mutually exclusive relations else if(compatibleTypesFromRoles.isEmpty() || types.isEmpty()){ compatibleTypes = compatibleTypesFromRoles; } else { compatibleTypes = multimapIntersection(compatibleTypesFromTypes, compatibleTypesFromRoles); } return compatibleTypes; }
@Override public Set<String> validateOntologically() { Set<String> errors = new HashSet<>(); SchemaConcept type = getSchemaConcept(); if (type != null && !type.isRelationshipType()){ errors.add(ErrorMessage.VALIDATION_RULE_INVALID_RELATION_TYPE.getMessage(type.label())); return errors; } //check role-type compatibility Map<Var, Type> varTypeMap = getParentQuery().getVarTypeMap(); for (Map.Entry<Role, Collection<Var>> e : getRoleVarMap().asMap().entrySet() ){ Role role = e.getKey(); if (!Schema.MetaSchema.isMetaLabel(role.label())) { //check whether this role can be played in this relation if (type != null && type.asRelationshipType().roles().noneMatch(r -> r.equals(role))) { errors.add(ErrorMessage.VALIDATION_RULE_ROLE_CANNOT_BE_PLAYED.getMessage(role.label(), type.label())); } //check whether the role player's type allows playing this role for (Var player : e.getValue()) { Type playerType = varTypeMap.get(player); if (playerType != null && playerType.playing().noneMatch(plays -> plays.equals(role))) { errors.add(ErrorMessage.VALIDATION_RULE_TYPE_CANNOT_PLAY_ROLE.getMessage(playerType.label(), role.label(), type == null? "" : type.label())); } } } } return errors; }
relType.roles().collect(toSet()) : inferPossibleTypes(sub).stream() .filter(Concept::isRelationshipType)