Java tutorial
/** * QuiltPlayer v1.0 Copyright (C) 2008-2009 Vlado Palczynski * vlado.palczynski@quiltplayer.com http://www.quiltplayer.com This program is * free software; you can redistribute it and/or modify it under the terms of * the GNU General Public License as published by the Free Software Foundation; * either version 2 of the License, or (at your option) any later version. This * program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ package com.quiltplayer.core.storage.neo; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Locale; import java.util.Stack; import javax.annotation.PostConstruct; import org.apache.commons.lang.Validate; import org.apache.log4j.Logger; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.ReturnableEvaluator; import org.neo4j.graphdb.StopEvaluator; import org.neo4j.graphdb.Traverser; import org.neo4j.index.IndexService; import org.neo4j.index.lucene.LuceneIndexService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import com.quiltplayer.core.storage.Storage; import com.quiltplayer.external.covers.model.LocalImage; import com.quiltplayer.model.Album; import com.quiltplayer.model.Artist; import com.quiltplayer.model.Song; import com.quiltplayer.model.StringId; import com.quiltplayer.model.neo.NeoAlbum; import com.quiltplayer.model.neo.NeoLocalImage; import com.quiltplayer.model.neo.NeoSong; /** * Implementation of Storage against Neo4j. * * @author Vlado Palczynski */ @Repository public class NeoStorage implements Storage { /** * The logger. */ private static final Logger log = Logger.getLogger(NeoStorage.class); /** * Album property for album id. */ private static final String ALBUM_ID_INDEX = "albumId"; /** * Song property for song id. */ private static final String SONG_ID_INDEX = "songId"; /** * Index property for file name. */ private static final String FILE_NAME_INDEX = "fileName"; /** * Property for word. */ private static final String WORD_PROPERTY = "word"; /** * Property for count. */ private static final String COUNT_PROPERTY = "count_uses"; /** * Property for part. */ private static final String PART_POSTFIX = ".part"; /** * Neo storage. */ @Autowired private GraphDatabaseService neoService; /** * Index service. */ private IndexService indexService; @PostConstruct public void init() { indexService = new LuceneIndexService(neoService); } /* * (non-Javadoc) * * @see com.quiltplayer.core.storage.Storage#getAlbums(java.util.Collection) */ @Override @Transactional public List<Album> getAlbums(final Collection<Artist> artists) { final List<Album> albums = new ArrayList<Album>(); for (final Artist artist : artists) { for (Album album : artist.getAlbums()) { albums.add(album); } } return albums; } @Override @Transactional public Stack<Album> getAlbumsAsStack(final Collection<Artist> artists) { final Stack<Album> albums = new Stack<Album>(); for (final Artist artist : artists) { for (Album album : artist.getAlbums()) { albums.push(album); } } return albums; } /* * @see com.quiltplayer.core.storage.Storage#getAlbum(java.lang.String) */ @Override @Transactional public Album getAlbum(final StringId albumId) { Node albumNode = searchSingle(albumId.getId(), ALBUM_ID_INDEX, QuiltPlayerRelationshipTypes.ALBUM_ID); Album album = null; if (albumNode != null) { album = new NeoAlbum(albumNode); } return album; } @Override @Transactional public Song getSong(final String albumTitle, final StringId songId) { Song song = null; Node songNode = searchSingle(songId.getId(), SONG_ID_INDEX, QuiltPlayerRelationshipTypes.SONG_ID); if (songNode != null) { song = new NeoSong(songNode); } return song; } /* * @see com.quiltplayer.core.storage.Storage#getLocalImage(java.lang.String) */ @Override @Transactional public LocalImage getLocalImage(final String path) { LocalImage image = null; Node imageNode = searchSingle(path, FILE_NAME_INDEX, QuiltPlayerRelationshipTypes.PART_OF_FILE_NAME); if (imageNode != null) { image = new NeoLocalImage(imageNode); } return image; } /* * (non-Javadoc) * * @see com.quiltplayer.core.storage.Storage#createAlbum(com.quiltplayer.model .StringId) */ @Override @Transactional public Album createAlbum(final StringId albumId) { Album album = null; final Node albumNode = neoService.createNode(); album = new NeoAlbum(albumNode); album.setId(albumId); index(albumId.getId(), albumNode, ALBUM_ID_INDEX, QuiltPlayerRelationshipTypes.ALBUM_ID); log.debug("Album with title '" + albumId.getId() + "' created."); return album; } /* * @see com.quiltplayer.core.storage.Storage#createLocalImage(com.quiltplayer .model.Album, * java.lang.String) */ @Override @Transactional public LocalImage createLocalImage(final Album album, final String fileName, final LocalImage image) { Validate.notNull(album); final Node imageNode = neoService.createNode(); final NeoLocalImage neoImage = new NeoLocalImage(imageNode); final Node albumNode = ((NeoAlbum) album).getNode(); if (image.getType().equals(LocalImage.TYPE_PRIMARY)) { log.debug("Adding primary image to album: " + album.getTitle()); albumNode.createRelationshipTo(imageNode, QuiltPlayerRelationshipTypes.HAS_FRONT_IMAGE); Traverser nodes = ((NeoAlbum) album).getNode().traverse(Traverser.Order.DEPTH_FIRST, StopEvaluator.DEPTH_ONE, ReturnableEvaluator.ALL_BUT_START_NODE, QuiltPlayerRelationshipTypes.HAS_FRONT_IMAGE, Direction.OUTGOING); int counter = nodes.getAllNodes().size(); neoImage.setCounter(counter); } else { log.debug("Adding secondary image to album: " + album.getTitle()); albumNode.createRelationshipTo(imageNode, QuiltPlayerRelationshipTypes.HAS_IMAGE); Traverser nodes = ((NeoAlbum) album).getNode().traverse(Traverser.Order.DEPTH_FIRST, StopEvaluator.DEPTH_ONE, ReturnableEvaluator.ALL_BUT_START_NODE, QuiltPlayerRelationshipTypes.HAS_IMAGE, Direction.OUTGOING); int counter = nodes.getAllNodes().size(); neoImage.setCounter(counter); } index(fileName, imageNode, FILE_NAME_INDEX, QuiltPlayerRelationshipTypes.PART_OF_FILE_NAME); neoImage.setType(image.getType()); neoImage.setSmallImage(image.getSmallImage()); neoImage.setMediumImage(image.getMediumImage()); neoImage.setLargeImage(image.getLargeImage()); return image; } /* * (non-Javadoc) * * @see com.quiltplayer.core.storage.Storage#createSong(com.quiltplayer.model .StringId) */ @Override @Transactional public Song createSong(final StringId songId) { Song song = null; final Node songNode = neoService.createNode(); song = new NeoSong(songNode); song.setId(songId); index(songId.getId(), songNode, SONG_ID_INDEX, QuiltPlayerRelationshipTypes.SONG_ID); return song; } /* * (non-Javadoc) * * @see com.quiltplayer.core.storage.Storage#deleteArtist(com.quiltplayer.model .Artist) */ @Override @Transactional public void delete(final Album album) { Node albumNode = ((NeoAlbum) album).getNode(); indexService.removeIndex(albumNode, album.getId().getId(), ""); for (Relationship rel : albumNode.getRelationships()) { if (rel.isType(QuiltPlayerRelationshipTypes.HAS_SONG)) { Node songNode = rel.getEndNode(); deleteSong(songNode); } else if (rel.isType(QuiltPlayerRelationshipTypes.HAS_FRONT_IMAGE) || rel.isType(QuiltPlayerRelationshipTypes.HAS_IMAGE)) { Node imageNode = rel.getEndNode(); deleteImage(imageNode); } else rel.delete(); } System.out.println("------------------------------------------"); albumNode.delete(); } @Transactional public void deleteSong(final Node songNode) { indexService.removeIndex(songNode, (String) songNode.getProperty(SONG_ID_INDEX), ""); for (Relationship rel : songNode.getRelationships()) { rel.delete(); } songNode.delete(); } @Transactional public void deleteImage(final Node imageNode) { for (Relationship rel : imageNode.getRelationships()) { rel.delete(); } imageNode.delete(); } private void index(final String value, final Node node, final String indexName, final QuiltPlayerRelationshipTypes relType) { indexService.index(node, indexName, value); final String partIndexName = indexName + PART_POSTFIX; for (String part : splitSearchString(value)) { Node wordNode = indexService.getSingleNode(partIndexName, part); if (wordNode == null) { wordNode = neoService.createNode(); indexService.index(wordNode, partIndexName, part); wordNode.setProperty(WORD_PROPERTY, part); } wordNode.createRelationshipTo(node, relType); wordNode.setProperty(COUNT_PROPERTY, ((Integer) wordNode.getProperty(COUNT_PROPERTY, 0)) + 1); } } private String[] splitSearchString(final String value) { return value.toLowerCase(Locale.ENGLISH).split("[^\\w]+"); } private List<Node> findSearchWords(final String userInput, final String indexName) { final String partIndexName = indexName + PART_POSTFIX; final List<Node> wordList = new ArrayList<Node>(); for (String part : splitSearchString(userInput)) { Node wordNode = indexService.getSingleNode(partIndexName, part); if (wordNode == null || !wordNode.hasRelationship() || wordList.contains(wordNode)) { continue; } wordList.add(wordNode); } if (wordList.isEmpty()) { return Collections.emptyList(); } Collections.sort(wordList, new Comparator<Node>() { public int compare(final Node left, final Node right) { int leftCount = (Integer) left.getProperty(COUNT_PROPERTY, 0); int rightCount = (Integer) right.getProperty(COUNT_PROPERTY, 0); return leftCount - rightCount; } }); return wordList; } private Node searchSingle(final String value, final String indexName, final QuiltPlayerRelationshipTypes wordRelType) { Node match = indexService.getSingleNode(indexName, value); if (match != null) { return match; } final List<Node> wordList = findSearchWords(value, indexName); if (wordList.isEmpty()) { return null; } final Node startNode = wordList.remove(0); match = startNode.getRelationships(wordRelType).iterator().next().getEndNode(); if (wordList.isEmpty()) { return match; } int bestCount = 0; final int listSize = wordList.size(); for (Relationship targetRel : startNode.getRelationships(wordRelType)) { Node targetNode = targetRel.getEndNode(); int hitCount = 0; for (Relationship wordRel : targetNode.getRelationships(wordRelType)) { if (wordList.contains(wordRel.getStartNode())) { if (++hitCount == listSize) { return targetNode; } } } if (hitCount > bestCount) { match = targetNode; bestCount = hitCount; } } return match; } }