Начинаем работу над новым проектом... Уровень RESTful, предоставляющий сервисы для платформы социальной сети.
Neo4j был моим очевидным выбором для основного хранилища данных, у меня была возможность работать с Neo раньше, но без использования возможностей Spring Data чтобы сопоставить POJO с узлом, что кажется очень удобным.
Цели:
Уровень должен обеспечивать поддержку, аналогичную Facebook Graph API, которая определяет для каждой сущности/объекта связанные свойства и соединения, на которые можно ссылаться по URL-адресу.
FB Graph APIЕсли возможно, я хочу избежать передачи объектов, которые будут сериализованы в/из объектов домена, и использовать мой домен pojo в качестве JSON, переданного клиенту/от клиента.
Примеры:
HTTP GET /profile/{id}/?fields=...&connections=... ответом будет объект Profile, содержащий запрошенный в URL.
HTTP GET /profile/{id}/stories/?fields=..&connections=...&page=..&sort=... ответом будет список Объекты Story в соответствии с запрошенными.
Соответствующие версии:
- Spring Framework 3.1.2
- Весенние данные Neo4j 2.1.0.RC3
- Spring Данные MongoDB 1.1.0.RC1
- Аспект J 1.6.12
- Джексон 1.8.5
Для простоты у нас есть узлы Профиль,История и Роль. связь между ними.
public abstract class GraphEntity {
@GraphId
protected Long id;
}
Узел профиля
@NodeEntity
@Configurable
public class Profile extends GraphEntity {
// Profile fields
private String firstName;
private String lastName;
// Profile connections
@RelatedTo(type = "FOLLOW", direction = Direction.OUTGOING)
private Set<Profile> followThem;
@RelatedTo(type = "BOOKMARK", direction = Direction.OUTGOING)
private Set<Story> bookmarks;
@Query("START profile=node({self}) match profile-[r:ROLE]->story where r.role = FOUNDER and story.status = PUBLIC")
private Iterable<Story> published;
}
Узел истории
@NodeEntity
@Configurable
public class Story extends GraphEntity {
// Story fields
private String title;
private StoryStatusEnum status = StoryStatusEnum.PRIVATE;
// Story connections
@RelatedToVia(type = "ROLE", elementClass = Role.class, direction = Direction.INCOMING)
private Set<Role> roles;
}
Взаимоотношения ролей
@RelationshipEntity(type = "ROLE")
public class Role extends GraphEntity {
@StartNode
private Profile profile;
@EndNode
private Story story;
private StoryRoleEnum role;
}
Сначала я не использовал поддержку AspectJ, но я считаю ее очень полезной для моего варианта использования, потому что она создает разделитель между POJO и фактическим узлом, поэтому я могу легко запрашивать свойства/соединения в соответствии с к запросам и подходу к проектированию на основе предметной области выглядит очень мило.
Вопрос 1 – AspectJ:
Допустим, я хочу определить поля по умолчанию для объекта, эти поля будут возвращены клиенту независимо от того, запрашиваются ли они в URL-адресе или нет... поэтому я попробовал аннотацию @FETCH для этих полей, но это кажется, что это не работает при использовании AspectJ. На данный момент я так и делаю..
public Profile(Node n) {
setPersistentState(n);
this.id = getId();
this.firstName = getFirstName();
this.lastName = getLastName();
}
Это правильный подход для достижения этого? следует ли поддерживать аннотацию @FETCH даже при использовании AspectJ?
Буду рад получить примеры/блоги, рассказывающие об AspectJ + Neo4j, почти ничего не нашел....
Вопрос 2. Разбивка на страницы:
Я хотел бы поддерживать нумерацию страниц при запросе определенного соединения, например
/profile/{id}/stories/ , если истории связаны, как показано ниже
// inside profile node
@RelatedTo(type = "BOOKMARK", direction = Direction.OUTGOING)
private Set<Story> bookmarks;
/profile/{id}/stories/ , если истории связаны, как показано ниже
// inside profile node
@Query("START profile=node({self}) match profile-[r:ROLE]->story where r.role = FOUNDER and story.status = PUBLIC")
private Iterable<Story> published;
Поддерживается ли нумерация страниц по умолчанию с помощью @Query || @RelatedTo || @RelatedToVia использует интерфейс Pageable для получения Page вместо Set/List/Iterable? ограничение и сортировка должны быть динамическими в зависимости от запроса от клиента... Я могу добиться этого, используя Cypher Query DSL, но предпочитаю использовать базовый. Другие подходы будут приняты с радостью.
Вопрос 3. @Query with {self}:
Какой-то глупый вопрос, но я ничего не могу с собой поделать :), кажется, что при использовании @Query внутри сущности узла (с использованием параметра {self} } тип возвращаемого значения должен быть Iterable, что имеет смысл.. давайте возьмем пример.. .
// inside profile node
@Query("START profile=node({self}) match profile-[r:ROLE]->story where r.role = FOUNDER and story.status = PUBLIC")
private Iterable<Story> published;
При запросе опубликованного соединения:
// retrieving the context profile
Profile profile = profileRepo.findOne(id);
// getting the publishe stories using AspectJ - will redirect to the backed node
Iterable<Story> published = profile.getPublished();
// set the result into the domain object - will throw exception of read only because the type is Iterable
profile.setPublished(published);
Есть ли обходной путь для этого? который не создает другое свойство, которое будет @Transiant внутри профиля..
Вопрос 4. Рекурсивные отношения:
У меня возникают некоторые проблемы с транзитивными/рекурсивными отношениями, когда при назначении новой роли профиля в истории роль сущности отношения содержит историю @EndNode, которая содержит соединение ролей... и одну из них - это контекст role выше, и он никогда не заканчивается :)... Есть ли способ настроить механизм данных Spring, чтобы не создавать эти бесконечные отношения?
Вопрос 5 – Транзакции:
Может быть, я должен был упомянуть об этом раньше, но я использую сервер REST для БД Neo4j, из предыдущего чтения я понимаю, что в транзакциях нет встроенной поддержки? например, при использовании встроенного сервера у меня есть следующий код...
Profile newProfile = new Profile();
newProfile.getFollowThem().add(otherProfile);
newProfile.getBookmarks().add(otherStory);
newProfile.persist(); // or profileRepo.save(newProfile)
будет ли это выполняться в транзакции при использовании сервера REST? здесь мало операций, если одна провалится, все провалятся?
Вопрос 6 – Mongo + Neo4j:
Мне нужно хранить данные, которые не имеют реляционной природы, такие как каналы, комментарии, сообщения.
Я подумал об интеграции с MongoDB для хранения этих данных.. могу ли я разделить поля pojo домена / подключения к mongo / neo4j с поддержкой кросс-магазина? будет ли он поддерживать AspectJ?
На этом пока все... буду рад любым комментариям относительно любого подхода, который я представил выше... спасибо.