Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update label property from 'name' to 'label' #9

Merged
merged 1 commit into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ We should see `HTTP/1.1 201 Created` as signs of success.
- vocabulary count: https://api.paion-data.dev/wilhelm/languages/german?perPage=100&page=1
- query vocabulary paged: https://api.paion-data.dev/wilhelm/languages/german/count
- expand: https://api.paion-data.dev/wilhelm/expand/nämlich
- search: https://api.paion-data.dev/wilhelm/search/das

License
-------
Expand Down
16 changes: 11 additions & 5 deletions src/main/java/org/qubitpi/wilhelm/Link.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@
@JsonIncludeProperties({ "label", "sourceNodeId", "targetNodeId", "attributes" })
public class Link {

/**
* The database node attribute name whose value is used for displaying the relationship caption on UI.
* <p>
* For example, for Neo4J database, this would correspond to a relationship property.
*/
public static final String LABEL_ATTRIBUTE = "label";

private static final Logger LOG = LoggerFactory.getLogger(Link.class);

private final String label;
Expand Down Expand Up @@ -93,18 +100,17 @@ private Link(
* @throws IllegalStateException if {@code relationship} is missing a "name" property
*/
public static Link valueOf(final Relationship relationship) {
final String labelKey = "name";
if (!Objects.requireNonNull(relationship).asMap().containsKey(labelKey)) {
LOG.error("Neo4J relationship does not contain '{}' attribute: {}", labelKey, relationship.asMap());
if (!Objects.requireNonNull(relationship).asMap().containsKey(LABEL_ATTRIBUTE)) {
LOG.error("Neo4J relationship does not contain '{}' attribute: {}", LABEL_ATTRIBUTE, relationship.asMap());
throw new IllegalStateException(
"There seems to be a data format mismatch between Wilhelm webservice and Neo4J database. " +
"Please file an issue at https://github.com/QubitPi/wilhelm-ws/issues for a fix"
);
}

final String label = relationship.asMap().get(labelKey).toString();
final String label = relationship.asMap().get(LABEL_ATTRIBUTE).toString();
final Map<String, Object> attributes = relationship.asMap().entrySet().stream()
.filter(entry -> !labelKey.equals(entry.getKey()))
.filter(entry -> !LABEL_ATTRIBUTE.equals(entry.getKey()))
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));

return new Link(label, relationship.startNodeElementId(), relationship.endNodeElementId(), attributes);
Expand Down
16 changes: 11 additions & 5 deletions src/main/java/org/qubitpi/wilhelm/Node.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@
@JsonIncludeProperties({ "id", "label", "attributes" })
public class Node {

/**
* The database node attribute name whose value is used for displaying the node caption on UI.
* <p>
* For example, for Neo4J database, this would correspond to a node property.
*/
public static final String LABEL_ATTRIBUTE = "label";

private static final Logger LOG = LoggerFactory.getLogger(Node.class);

private final String id;
Expand Down Expand Up @@ -82,18 +89,17 @@ private Node(@NotNull final String id, @NotNull final String label, @NotNull fin
* @throws IllegalStateException if {@code node} is missing a "name" property
*/
public static Node valueOf(@NotNull final org.neo4j.driver.types.Node node) {
final String labelKey = "name";
if (!Objects.requireNonNull(node).asMap().containsKey(labelKey)) {
LOG.error("Neo4J node does not contain '{}' attribute: {}", labelKey, node.asMap());
if (!Objects.requireNonNull(node).asMap().containsKey(LABEL_ATTRIBUTE)) {
LOG.error("Neo4J node does not contain '{}' attribute: {}", LABEL_ATTRIBUTE, node.asMap());
throw new IllegalStateException(
"There seems to be a data format mismatch between Wilhelm webservice and Neo4J database. " +
"Please file an issue at https://github.com/QubitPi/wilhelm-ws/issues for a fix"
);
}

final String label = node.asMap().get(labelKey).toString();
final String label = node.asMap().get(LABEL_ATTRIBUTE).toString();
final Map<String, Object> attributes = node.asMap().entrySet().stream()
.filter(entry -> !labelKey.equals(entry.getKey()))
.filter(entry -> !LABEL_ATTRIBUTE.equals(entry.getKey()))
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));

return new Node(node.elementId(), label, attributes);
Expand Down
17 changes: 12 additions & 5 deletions src/main/java/org/qubitpi/wilhelm/web/endpoints/Neo4JServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,12 @@ public Response getVocabularyByLanguagePaged(

final String query = String.format(
"MATCH (t:Term WHERE t.language = '%s')-[r]->(d:Definition) " +
"RETURN t.name AS term, d.name AS definition " +
"RETURN t.%s AS term, d.%s AS definition " +
"SKIP %s LIMIT %s",
requestedLanguage.getDatabaseName(), (Integer.parseInt(page) - 1) * Integer.parseInt(perPage), perPage
requestedLanguage.getDatabaseName(),
Node.LABEL_ATTRIBUTE,
Node.LABEL_ATTRIBUTE,
(Integer.parseInt(page) - 1) * Integer.parseInt(perPage), perPage
);

return Response
Expand All @@ -142,14 +145,17 @@ public Response getVocabularyByLanguagePaged(
*
* @param keyword The provided keyword
*
* @return all nodes whose "name" attribute contains the search keyword
* @return all nodes whose {@link Node#LABEL_ATTRIBUTE "label"} attribute contains the search keyword
*/
@GET
@Path("/search/{keyword}")
@Produces(MediaType.APPLICATION_JSON)
@SuppressWarnings("MultipleStringLiterals")
public Response search(@NotNull @PathParam("keyword") final String keyword) {
final String query = String.format("MATCH (node) WHERE node.name =~ '.*%s.*' RETURN node", keyword);
final String query = String.format(
"MATCH (node) WHERE node.%s =~ '.*%s.*' RETURN node",
Node.LABEL_ATTRIBUTE, keyword
);

return Response
.status(Response.Status.OK)
Expand Down Expand Up @@ -313,12 +319,13 @@ public Response expandApoc(

final String query = String.format(
"""
MATCH (node{name:'%s'})
MATCH (node{%s:'%s'})
CALL apoc.path.expand(node, "LINK", null, 1, %s)
YIELD path
RETURN path, length(path) AS hops
ORDER BY hops;
""",
Node.LABEL_ATTRIBUTE,
word.replace("'", "\\'"), maxHops
);

Expand Down
4 changes: 2 additions & 2 deletions src/test/groovy/org/qubitpi/wilhelm/LinkSpec.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class LinkSpec extends Specification {
_ | "attributes"
}

def "when a Neo4J relationship does not contain 'name' property, an error is thrown"() {
def "when a Neo4J relationship does not contain 'label' property, an error is thrown"() {
when: "a Neo4J node has no properties"
Link.valueOf(Mock(Relationship) {asMap() >> [:]})

Expand All @@ -62,7 +62,7 @@ class LinkSpec extends Specification {
when: "a happy path Neo4J relationship is being converted to a transparent link"
Link actual = Link.valueOf(Mock(Relationship) {
asMap() >> [
name: "my node",
label: "my node",
type: "follows"
]
startNodeElementId() >> "node1"
Expand Down
4 changes: 2 additions & 2 deletions src/test/groovy/org/qubitpi/wilhelm/NodeSpec.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class NodeSpec extends Specification {
_ | "attributes"
}

def "when a Neo4J node does not contain 'name' property, an error is thrown"() {
def "when a Neo4J node does not contain 'label' property, an error is thrown"() {
when: "a Neo4J node has no properties"
Node.valueOf(Mock(org.neo4j.driver.types.Node) {asMap() >> [:]})

Expand All @@ -59,7 +59,7 @@ class NodeSpec extends Specification {
when: "a happy path Neo4J node is being converted to a transparent node"
Node actual = Node.valueOf(Mock(org.neo4j.driver.types.Node) {
asMap() >> [
name: "my node",
label: "my node",
color: "blue",
size: "medium"
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ class Neo4JServletITSpec extends Specification {
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.when()
.get("/neo4j/expand/nämlich")
.get("/neo4j/expand/dreißig")
.then()
.statusCode(200)
.body("", hasKey("nodes"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ class Neo4JServletSpec extends Specification {
keys() >> ["term", "definition"]
get("term") >> Mock(Value) {
type() >> InternalTypeSystem.TYPE_SYSTEM.NODE()
keys() >> ["name", "language"]
get("name") >> Mock(Value) {
keys() >> ["label", "language"]
get("label") >> Mock(Value) {
type() >> InternalTypeSystem.TYPE_SYSTEM.STRING()
asString() >> "Hallo"
}
Expand All @@ -44,8 +44,8 @@ class Neo4JServletSpec extends Specification {
}
get("definition") >> Mock(Value) {
type() >> InternalTypeSystem.TYPE_SYSTEM.NODE()
keys() >> ["name"]
get("name") >> Mock(Value) {
keys() >> ["label"]
get("label") >> Mock(Value) {
type() >> InternalTypeSystem.TYPE_SYSTEM.STRING()
asString() >> "Hello"
}
Expand All @@ -55,11 +55,11 @@ class Neo4JServletSpec extends Specification {
expect:
Neo4JServlet.expand(value) == [
term: [
name: "Hallo",
label: "Hallo",
language: "German"
],
definition: [
name: "Hello"
label: "Hello"
]
]
}
Expand Down
Loading