Skip to content

Commit

Permalink
Support 2 types of expand - apoc & plain BFS
Browse files Browse the repository at this point in the history
  • Loading branch information
QubitPi committed Oct 30, 2024
1 parent 0d7d5ec commit 005a8a6
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 36 deletions.
33 changes: 10 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,14 @@ Aristotle
=========

![Java Version Badge][Java Version Badge]
![HashiCorp Packer Badge][HashiCorp Packer Badge]
![HashiCorp Terraform Badge][HashiCorp Terraform Badge]
[![Apache License Badge]][Apache License, Version 2.0]

Aristotle is a [JSR 370] [JAX-RS] webservice of CRUD operations against a graph database. It supports Neo4J now.

Test
----

```console
mvn clean verify
```

Start Locally in Jetty
----------------------

Make sure port 8080 is not occupied and the following environment variables are set:
Navigate to a dedicated directory; make sure port 8080 is not occupied and the following environment variables are set:

```console
export NEO4J_URI=
Expand All @@ -33,21 +24,20 @@ Then start webservice with:
./jetty-start.sh
```

Press `Ctr-C` to stop the webservice and delete generated directories if needed when done.


Deployment
----------

This is a one-person project. Agility outplays team scaling, so deployment is manual:

Test
----

```console
mvn clean package
mvn clean verify
```

```console
export JETTY_HOME=
```
Deployment
----------

This is a one-person project. Agility outplays team scaling, so deployment is manual and pretty much follows
[jetty-start.sh](./jetty-start.sh)

### Sending Logs to ELK Cloud

Expand Down Expand Up @@ -101,9 +91,6 @@ The use and distribution terms for [Aristotle]() are covered by the [Apache Lice
[Apache License Badge]: https://img.shields.io/badge/Apache%202.0-F25910.svg?style=for-the-badge&logo=Apache&logoColor=white
[Apache License, Version 2.0]: https://www.apache.org/licenses/LICENSE-2.0

[HashiCorp Packer Badge]: https://img.shields.io/badge/Packer-02A8EF?style=for-the-badge&logo=Packer&logoColor=white
[HashiCorp Terraform Badge]: https://img.shields.io/badge/Terraform-7B42BC?style=for-the-badge&logo=terraform&logoColor=white

[Java Version Badge]: https://img.shields.io/badge/Java-17-brightgreen?style=for-the-badge&logo=OpenJDK&logoColor=white
[JAX-RS]: https://jcp.org/en/jsr/detail?id=370
[JSR 370]: https://jcp.org/en/jsr/detail?id=370
88 changes: 75 additions & 13 deletions src/main/java/org/qubitpi/wilhelm/Graph.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,35 @@
package org.qubitpi.wilhelm;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIncludeProperties;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jakarta.validation.constraints.NotNull;
import net.jcip.annotations.Immutable;
import net.jcip.annotations.ThreadSafe;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* A JSON-serializable object representation of a knowledge graph in wilhelm-ws.
*/
@Immutable
@ThreadSafe
@SuppressWarnings("ClassCanBeRecord")
@JsonIncludeProperties({ "nodes", "links" })
public class Graph {

private static final Logger LOG = LoggerFactory.getLogger(Graph.class);
private static final ObjectMapper JSON_MAPPER = new ObjectMapper();

private final Set<Node> nodes;
Expand All @@ -38,24 +53,43 @@ public class Graph {
/**
* All-args constructor.
*
* @param nodes
* @param links
* @param nodes The set of all nodes contained in this Graph
* @param links The set of all links contained in this Graph
*/
public Graph(final Set<Node> nodes, final Set<Link> links) {
this.nodes = nodes;
this.links = links;
public Graph(@NotNull final Set<Node> nodes, @NotNull final Set<Link> links) {
this.nodes = new HashSet<>(nodes);
this.links = new HashSet<>(links);
}

/**
* Creates a new {@link Graph} instance with no initial nodes or links in it.
*
* @return a new instance
*/
public static Graph emptyGraph() {
return new Graph(Collections.emptySet(), Collections.emptySet());
}

@JsonIgnore
/**
* Returns whether or not this {@link Graph} has neither nodes noe links.
*
* @return {@code true} if no nodes or links exist in this {@link Graph}, or {@code false} otherwise.
*/
public boolean isEmpty() {
return getNodes().isEmpty() && getLinks().isEmpty();
}

public List<Node> getUndirectedNeighborsOf(Node node) {
/**
* Returns all weakly connected neighbors of a specified node.
* <p>
* If the node has no such neighrbors, this method returns an empty list
*
* @param node a node from this {@link Graph}
*
* @return all nodes each of which has a link between it and the provided node.
*/
@NotNull
public Set<Node> getUndirectedNeighborsOf(Node node) {
final Set<String> neighborIds = getLinks().stream()
.filter(link -> node.getId().equals(link.getSourceNodeId()) || node.getId().equals(link.getTargetNodeId()))
.flatMap(link -> Stream.of(link.getSourceNodeId(), link.getTargetNodeId()))
Expand All @@ -64,30 +98,58 @@ public List<Node> getUndirectedNeighborsOf(Node node) {

return getNodes().stream()
.filter(it -> neighborIds.contains(it.getId()))
.collect(Collectors.toUnmodifiableList());
.collect(Collectors.toUnmodifiableSet());
}

public Graph merge(final Graph that) {
/**
* Combines the nodes and links from this {@link Graph} instance and the other one and returns a new {@link Graph}.
*
* @param that the other {@link Graph} instance to be merged with this {@link Graph}
*
* @return a new instance
*/
public Graph merge(@NotNull final Graph that) {
return new Graph(
Stream.of(this.getNodes(), that.getNodes()).flatMap(Set::stream).collect(Collectors.toSet()),
Stream.of(this.getLinks(), that.getLinks()).flatMap(Set::stream).collect(Collectors.toSet())
);
}

/**
* Returns an unmodifiable view of all the nodes in this Graph instance.
*
* @return an immutable list of nodes
*/
@NotNull
public Set<Node> getNodes() {
return nodes;
return Collections.unmodifiableSet(nodes);
}

/**
* Returns an unmodifiable view of all the links in this Graph instance.
*
* @return an immutable list of links
*/
@NotNull
public Set<Link> getLinks() {
return links;
return Collections.unmodifiableSet(links);
}

/**
* Returns a JSON serialization of this Graph instance. It contains 2 fields: nodes and links, each of which is a
* list of nodes and links respectively. Each list element is itself a JSON object whose structure are defined by
* Jackson's serialization on {@link Node} and {@link Link}.
*
* @return a JSON string
*/
@NotNull
@Override
public String toString() {
try {
return JSON_MAPPER.writeValueAsString(this);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
} catch (JsonProcessingException exception) {
LOG.error(exception.getMessage());
throw new IllegalStateException(exception);
}
}
}
32 changes: 32 additions & 0 deletions src/test/groovy/org/qubitpi/wilhelm/GraphSpec.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright Jiaqi Liu
*
* 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 org.qubitpi.wilhelm

import spock.lang.Specification

class GraphSpec extends Specification {

def "Graph is immutable"() {
given: "an empty graph"
Graph graph = Graph.emptyGraph()

when: "a new node is added to the graph"
graph.nodes.add(Mock(Node))

then: ""
noExceptionThrown()
}
}

0 comments on commit 005a8a6

Please sign in to comment.