Add artist details, members endpoints

This commit is contained in:
simon 2019-05-21 12:38:19 -04:00
parent 99cd73db07
commit 6a59ffdeef
4 changed files with 138 additions and 33 deletions

View File

@ -11,6 +11,6 @@ public class ArtistDetails {
} }
public String name; public String name;
public List<String> releases; public List<Release> releases;
public List<Tag> tags; public List<Tag> tags;
} }

View File

@ -0,0 +1,14 @@
package net.simon987.musicgraph.entities;
public class Release {
public String mbid;
public String name;
public long year;
public Release(String mbid, String name, long year) {
this.mbid = mbid;
this.name = name;
this.year = year;
}
}

View File

@ -9,7 +9,9 @@ import org.neo4j.driver.v1.types.Node;
import org.neo4j.driver.v1.types.Relationship; import org.neo4j.driver.v1.types.Relationship;
import javax.inject.Singleton; import javax.inject.Singleton;
import java.util.*; import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -37,11 +39,37 @@ public class MusicDatabase extends AbstractBinder {
long end = System.nanoTime(); long end = System.nanoTime();
long took = (end - start) / 1000000; long took = (end - start) / 1000000;
logger.info(String.format("Query %s (Took %dms)", query, took)); logger.info(String.format("Query %s (Took %dms)", query.replace('\n', ' '), took));
return result; return result;
} }
public SearchResult getArtistMembers(String mbid) {
try (Session session = driver.session()) {
Map<String, Object> params = new HashMap<>();
params.put("mbid", mbid);
StatementResult result = query(session,
"MATCH (a:Artist)-[r]-(b)\n" +
"WHERE a.id = $mbid AND NOT type(r) = \"IS_RELATED_TO\"\n" +
"RETURN a as artist, a {rels: collect(DISTINCT r), nodes: collect(DISTINCT b)} as rank1\n" +
"LIMIT 1",
params);
SearchResult out = new SearchResult();
parseRelatedResult(result, out);
return out;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public ArtistDetails getArtistDetails(String mbid) { public ArtistDetails getArtistDetails(String mbid) {
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
@ -51,7 +79,7 @@ public class MusicDatabase extends AbstractBinder {
StatementResult result = query(session, StatementResult result = query(session,
"MATCH (a:Artist {id: $mbid})-[:CREDITED_FOR]->(r:Release)\n" + "MATCH (a:Artist {id: $mbid})-[:CREDITED_FOR]->(r:Release)\n" +
"WITH collect(r.id) as releases, a\n" + "WITH collect({id:r.id, name:r.name, year:r.year}) as releases, a\n" +
"OPTIONAL MATCH (a)-[r:IS_TAGGED]->(t:Tag)\n" + "OPTIONAL MATCH (a)-[r:IS_TAGGED]->(t:Tag)\n" +
"RETURN a {name:a.name, releases:releases, tags:collect({weight: r.weight, name: t.name})}\n" + "RETURN a {name:a.name, releases:releases, tags:collect({weight: r.weight, name: t.name})}\n" +
"LIMIT 1", "LIMIT 1",
@ -65,7 +93,16 @@ public class MusicDatabase extends AbstractBinder {
Map<String, Object> map = result.next().get("a").asMap(); Map<String, Object> map = result.next().get("a").asMap();
details.name = (String) map.get("name"); details.name = (String) map.get("name");
details.releases.addAll((Collection<String>) map.get("releases")); details.releases.addAll(
((List<Map>) map.get("releases"))
.stream()
.map(x -> new Release(
(String) x.get("id"),
(String) x.get("name"),
(Long) x.get("year")
)).collect(Collectors.toList())
);
details.tags.addAll( details.tags.addAll(
((List<Map>) map.get("tags")) ((List<Map>) map.get("tags"))
.stream() .stream()
@ -85,7 +122,7 @@ public class MusicDatabase extends AbstractBinder {
} }
} }
public SearchResult getRelated(String mbid) { public SearchResult getRelatedById(String mbid) {
try (Session session = driver.session()) { try (Session session = driver.session()) {
@ -104,28 +141,7 @@ public class MusicDatabase extends AbstractBinder {
SearchResult out = new SearchResult(); SearchResult out = new SearchResult();
long start = System.nanoTime(); parseRelatedResult(result, out);
while (result.hasNext()) {
Record row = result.next();
out.artists.add(makeArtist(row.get(0).asNode()));
var rank1 = row.get(1).asMap();
out.relations.addAll(((List<Relationship>) rank1.get("rels"))
.stream()
.map(MusicDatabase::makeRelation)
.collect(Collectors.toList()
));
out.artists.addAll(((List<Node>) rank1.get("nodes"))
.stream()
.map(MusicDatabase::makeArtist)
.collect(Collectors.toList()
));
}
long end = System.nanoTime();
long took = (end - start) / 1000000;
logger.info(String.format("Fetched search result (Took %dms)", took));
return out; return out;
} catch (Exception e) { } catch (Exception e) {
@ -134,14 +150,69 @@ public class MusicDatabase extends AbstractBinder {
} }
} }
public SearchResult getRelatedByName(String name) {
try (Session session = driver.session()) {
Map<String, Object> params = new HashMap<>();
params.put("name", name);
StatementResult result = query(session,
"MATCH (a:Artist)-[r:IS_RELATED_TO]-(b)\n" +
"WHERE a.name = $name\n" +
// Only match artists with > 0 releases
"MATCH (b)-[:CREDITED_FOR]->(:Release)\n" +
"WHERE r.weight > 0.25\n" +
"RETURN a as artist, a {rels: collect(DISTINCT r), nodes: collect(DISTINCT b)} as rank1\n" +
"LIMIT 1",
params);
SearchResult out = new SearchResult();
parseRelatedResult(result, out);
return out;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private void parseRelatedResult(StatementResult result, SearchResult out) {
long start = System.nanoTime();
while (result.hasNext()) {
Record row = result.next();
out.artists.add(makeArtist(row.get(0).asNode()));
var rank1 = row.get(1).asMap();
out.relations.addAll(((List<Relationship>) rank1.get("rels"))
.stream()
.map(MusicDatabase::makeRelation)
.collect(Collectors.toList()
));
out.artists.addAll(((List<Node>) rank1.get("nodes"))
.stream()
.map(MusicDatabase::makeArtist)
.collect(Collectors.toList()
));
}
long end = System.nanoTime();
long took = (end - start) / 1000000;
logger.info(String.format("Fetched search result (Took %dms)", took));
}
private static Artist makeArtist(Node node) { private static Artist makeArtist(Node node) {
Artist artist = new Artist(); Artist artist = new Artist();
artist.id = node.id(); artist.id = node.id();
artist.mbid = node.get("id").asString(); artist.mbid = node.get("id").asString();
artist.name = node.get("name").asString(); artist.name = node.get("name").asString();
artist.labels = ImmutableList.copyOf(node.labels()); artist.labels = ImmutableList.copyOf(node.labels());
artist.listeners = node.get("listeners").asInt(); if (node.containsKey("listeners")) {
artist.playCount = node.get("playcount").asInt(); artist.listeners = node.get("listeners").asInt();
artist.playCount = node.get("playcount").asInt();
}
return artist; return artist;
} }
@ -150,7 +221,9 @@ public class MusicDatabase extends AbstractBinder {
relation.source = rel.startNodeId(); relation.source = rel.startNodeId();
relation.target = rel.endNodeId(); relation.target = rel.endNodeId();
relation.weight = rel.get("weight").asFloat(); if (rel.containsKey("weight")) {
relation.weight = rel.get("weight").asFloat();
}
return relation; return relation;
} }

View File

@ -26,8 +26,26 @@ public class ArtistController {
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public SearchResult getRelated(@PathParam("mbid") String mbid) { public SearchResult getRelated(@PathParam("mbid") String mbid) {
logger.info(String.format("Searching for %s", mbid)); logger.info(String.format("Related for %s", mbid));
return db.getRelated(mbid); return db.getRelatedById(mbid);
}
@GET
@Path("members/{mbid}")
@Produces(MediaType.APPLICATION_JSON)
public SearchResult getMembers(@PathParam("mbid") String mbid) {
logger.info(String.format("Members for %s", mbid));
return db.getArtistMembers(mbid);
}
@GET
@Path("related_by_name/{name}")
@Produces(MediaType.APPLICATION_JSON)
public SearchResult getRelatedByName(@PathParam("name") String name) {
logger.info(String.format("Related for %s", name));
return db.getRelatedByName(name);
} }
@GET @GET