POC данных Neo4j Spring для социального уровня RESTful

Начинаем работу над новым проектом... Уровень RESTful, предоставляющий сервисы для платформы социальной сети.
Neo4j был моим очевидным выбором для основного хранилища данных, у меня была возможность работать с Neo раньше, но без использования возможностей Spring Data чтобы сопоставить POJO с узлом, что кажется очень удобным.

Цели:

  1. Уровень должен обеспечивать поддержку, аналогичную Facebook Graph API, которая определяет для каждой сущности/объекта связанные свойства и соединения, на которые можно ссылаться по URL-адресу.
    FB Graph API

  2. Если возможно, я хочу избежать передачи объектов, которые будут сериализованы в/из объектов домена, и использовать мой домен 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?


На этом пока все... буду рад любым комментариям относительно любого подхода, который я представил выше... спасибо.


person assaf_miz84    schedule 03.01.2013    source источник
comment
Вау, сколько вопросов.   -  person Michael Hunger    schedule 03.01.2013


Ответы (1)


Приступая к ответу, далеко не полному:

Возможно, перейти на версии .RELEASE?

Вопрос 1

Если вы хотите сериализовать объекты AspectJ в JSON, вы должны исключить внутренние поля, сгенерированные расширенным сопоставлением (см. этот обсуждение на форуме).

Когда вы используете Advanced Mapping, @Fetch не нужен, так как данные все равно считываются из базы данных.

Вопрос 2

Для пагинации полей можно попробовать использовать шифро-запрос с @Query и LIMIT 100 SKIP 10 в качестве фиксированного параметра. В противном случае вы могли бы использовать репозиторий/шаблон для фактического заполнения коллекции в поле вашей сущности выгруженной информацией.

Вопрос 3

Я не думаю, что возвращаемый тип @Query должен быть Iterable, он также должен работать с другими типами (коллекциями или конкретными типами). С какой проблемой вы столкнулись?

Для создания рекурсивных отношений - попробуйте сначала сохранить сами объекты отношений, а только потом узлы-сущности. Или используйте template.createRelationshipBetween(start, end, type, allowDuplicates) для создания отношений.

Вопрос 5

Поскольку вы используете SDN поверх REST, он может работать не очень хорошо, так как прямо сейчас базовая реализация использует базу данных RestGraphDatabase для мелких операций, а расширенное сопоставление использует очень мелкие вызовы. Есть ли причина, по которой вы не хотите использовать встроенный режим? Против сервера REST я бы наверняка использовал простое сопоставление и попытался обрабатывать операции чтения в основном с помощью шифра.

С REST APi есть только один tx на http-вызов, единственный вариант иметь более крупные транзакции — использовать rest-batch-api.

В базовой базе данных rest-graph-data есть поддержка псевдотранзакций, которые объединяют вызовы, выданные в рамках «транзакции», для выполнения в одном пакетном запросе остатка. Но эти вызовы не должны полагаться на результаты чтения во время передачи, они будут заполнены только после завершения передачи. Были также некоторые проблемы с использованием этого подхода с SDN, поэтому я отключил его для этого (это параметр конфигурации/системное свойство для rest-graphdb).

Вопрос 6

В настоящее время поддержка кросс-хранилищ для MongoDB и Neo4j используется только для JPA/реляционного хранилища. Однажды мы обсуждали наличие перекрестных ссылок между магазинами между проектами spring-data, но не продолжили это.

person Michael Hunger    schedule 03.01.2013
comment
Привет, Майкл, и спасибо за быстрый ответ..... относительно вопроса 1: у меня нет проблем с сериализацией в JSON, я просто хочу, чтобы несколько определенных полей загружались автоматически. относительно вопроса 3: я получаю это исключение java.lang.ClassCastException: org.springframework.data.neo4j.rest.SpringEndResult не может быть приведен к java.util.Collection... остальные ответы ясны... не могли бы вы обратиться меня к учебнику, где используются данные aspectJ + neo4j + spring .. последний вопрос, каковы основные варианты использования для работы с сервером REST? Спасибо - person assaf_miz84; 07.01.2013