Announcement Announcement Module
Collapse
No announcement yet.
Need examples or documentation on relationships on @Query, derived and Pageable Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Need examples or documentation on relationships on @Query, derived and Pageable

    So I am working on my Repositories on the slightly more complex queries beyond query by property. And I am can't seem to find in the documentation anything beyond the basic trivial queries. Which is fine, we need Spring Data to cover those trivial queries automatically for us. which is what makes it great.

    But when you start to get to those just beyond the simple, it is difficult to figure out if Spring Data can automatically help you out, or what is it that you need to do to just go beyond.

    So, I don't think what I am doing is complex strange corner case.

    I have an Event node and I have an Item Node with a RelationshipEntity called ItemBringingToEvent. I want to write a query that returns a List of Items and the value from the quantityNeeded in the relationship. So I started with

    Code:
    public interface ItemRepository extends GraphRepository<Item> {
    
        Page<Item> findByDescriptionLike(String description, Pageable page);
        Item findByDescription(String description);
        Item findByBarcode(String barcode);
    
        //ITEM_NEEDED_FOR_EVENT
        @Query("start event=node({0}) " +
                " match event-[i:"+ Event.ITEMS_NEEDED_FOR_EVENT +
                "]->item" +
                " return item, i.quantityNeeded")
        Page<Item> findByEvent(Long eventId, Pageable page);
    OK, now of all my possible examples this is the oddest relationship I have. Which is weird since this is one of the only ones with direct relationships and not vertexes.

    But while I am still debating how to do this relationship, I still think it can be used to see if I am on the correct thinking.

    I think I cannot used a derived query by method name because the Item domain object does not hold a reference to any Event, it is only the Event with a List of ItemsNeededForEvent with @RelatedToVia.

    Anyway, the question I have is not about the query itself (It is wrong), but how Spring Data derives stuff. So in the query, because I have Pageable as a parameter, then the query will get a limit added to it? Can Pageable be any position parameter in that method, meaning if I have another like it with a "Where", where there are parameters to be filled in by the values passed in the method parameters I can put those anywhere, or I think I saw an @Param annotation to add them in the method signature.

    Q2) Because Item does not have a reference to any events, then I can't use a derived query by methodName that would return a List of Items? Or would that have to be a query in the EventRepository<Event>

    Q3) The query above is returning a Page<Item> but my Relationship object has a property that I also need called quantityNeeded. How do I get that returned with the query, I can't see the ItemRepository method returning a List of the RelationshipEntity type, it would have to be a node type of Item. Does this mean I need a Repository for the RelationshipEntity?

    Thanks

    some more code if needed

    Item class
    Code:
    @NodeEntity
    public class Item implements Serializable {
    
        private static final String BARCODE_INDEX = "barcode";
        private static final String DESCRIPTION_INDEX = "description";
    
        @GraphId
        private Long id;
    
        @Indexed(indexName = BARCODE_INDEX)
        private String barcode;
    
        @Indexed(indexName = DESCRIPTION_INDEX, indexType = IndexType.FULLTEXT)
        private String description;
    }
    And the EntityRelationship

    Code:
    @RelationshipEntity(type = Event.ITEMS_NEEDED_FOR_EVENT)
    public class ItemNeededForEvent implements Serializable {
    
        private BigDecimal quantityNeeded;
    
        @StartNode
        private Event event;
    
        @EndNode
        private Item item;
    }
    Mark
    Last edited by bytor99999; Feb 2nd, 2012, 01:09 AM. Reason: adding code

  • #2
    I'm going to risk the 2 posts in a thread before someone else posts jinx.

    Here is a simpler example that all of you would know about and make my first question seem simpler.

    Finding possible friends via Friend of a friend query

    This is in my UserRepository.

    Code:
    @Query("start user=node({0}) " + 
        	"match user-[f1:" + User.FRIEND +"]->friend-[f2:"+ User.FRIEND+"]-friendOfFriend " +
        	"return friendOfFriend " +
        	"order by count(*) desc")
        public Page<User> findFriendsOfFriends(User user, Pageable page);
    Thanks

    Mark

    Comment


    • #3
      Mark,

      first of all, when you want to return more than column from the query you should either expect an Iterable|List|Set<Map<String,Object>> or an Iterable|List|Set<YouMappedInterface>.

      Derived queries use the metadata of the repository object, so you could perhaps do it the other way round, put the method into the Event repository.

      I also discussed with Oliver (who works on Spring Data Commons from the SpringSource side) to support providing a different starting point (Entity) for derived queries but it not there yet.

      Yes for pageable it adds "start", "limit" and perhaps "order by", Pageable can be at any position in the parameter list.

      HTH

      Michael

      Comment


      • #4
        Mark,

        first of all, when you want to return more than column from the query you should either expect an Iterable|List|Set<Map<String,Object>> or an Iterable|List|Set<YouMappedInterface>.

        Derived queries use the metadata of the repository object, so you could perhaps do it the other way round, put the method into the Event repository.

        I also discussed with Oliver (who works on Spring Data Commons from the SpringSource side) to support providing a different starting point (Entity) for derived queries but it not there yet.

        Yes for pageable it adds "start", "limit" and perhaps "order by", Pageable can be at any position in the parameter list.

        HTH

        Michael

        Comment


        • #5
          Derived queries use the metadata of the repository object, so you could perhaps do it the other way round, put the method into the Event repository.
          I was trying to group it based on the return domain from the method. So if it returns an Event or List<Event> then that would go into the EventRepository, but if I am going through Event and it is say returning List<User> then I would put that query in the UserRepository.

          However, I did have one caveat, and that was based on the direction of the relationship. I would put the relationship name as a constant in the class that was the outgoing direction class. So based on returning type, it wouldn't work if the returning type has the relationship as incoming. (I am proud of myself finally understanding direction and what it means)

          I guess I could turn it around to where the startnode determines which Repository it belongs to. I just want to be consistent in my approach.

          Thanks again Michael

          Mark
          Last edited by bytor99999; Feb 3rd, 2012, 03:52 PM. Reason: add more stuff

          Comment


          • #6
            Actually another question.

            Code:
            @Query("start event=node({0}) " +
                           "match event<-[:" + User.EVENTS_I_AM_HOSTING + "]-hosts " +
                           "return hosts " +
                           "order by hosts.lastName asc")    
                public Page<User> findHosts(Event event, Pageable page);
                
                @Query("start event=node({0}) " +
                           "match event-[:" + Event.INVITED_GUESTS + "]->guests " +
                           "return guests " +
                           "order by guests.lastName asc")
                public Page<User> findUsersInvitedToEvent(Event event, Pageable page);
            So I have methods like this which take the domain object. Do you think I should also have equivalent methods that take the nodeId as the parameter instead. That way if I have the domain object use is, other wise I might only have the nodeId. These calls will be coming from a website or device where they might pass in just the ID in the URL request. So that would require my Controller calling the method to findById to get the domain object that I can then pass to the Repository method.

            Thanks, I'll also look at Cineasts to see what approach was taken there.

            UPDATE, the samples don't have anything that goes this far. Only Cineasts had a Repo with 1 @Query method, and it to a User and the User was looked up in the Controller, but it would have anyway since it was part of the request of what they needed and didn't already have.
            I do think I will add code in the Service to call a lookup by id first then the corresponding Repo method that takes the domain object. I wouldn't want to do that with an SQL database, but with Neo4J that type of consideration is a waste of time. Since Neo4j does that id lookup so fast anyway.

            Mark
            Last edited by bytor99999; Feb 3rd, 2012, 04:12 PM. Reason: update

            Comment


            • #7
              You can also pass in the node-id. If you want to be evil you could change the Event parameter to object and name it eventOrEventId but I would not recommend that, a second method would probably more sensible.

              Michael

              Comment


              • #8
                Yeah, the tough part is the interface apis to create. You want to write that up front, but sometimes it is later decisions or needs that dictate what is the best api signature to have.

                For instance, the pagination. I am cringing that I am putting as parameters to service interface methods things like (Event event, int page, int size) all over the place. But I guess if you want pagination, it has to be everywhere.

                I wonder if there is anyway we could do a ThreadLocal kind of thing. Where there is a util static method, maybe in the Template, where you call it from say a MVC Controller and pass in the page and size which still has to come from say RequestParams. Then it creates a PageRequest object and puts it into ThreadLocal, then in the repository, when you have a PageRequest as the parameter to the Repo interface method, that it automatically retrieves it from ThreadLocal, or maybe you don't even have to declare your Repo interface method to take a PageRequest as a parameter anymore and just when you parse/read the @Query, you go check in ThreadLocal to see if there is a PageRequest object in ThreadLocal, then automatically use it.

                Hmmmm.

                Thanks

                Mark

                Comment


                • #9
                  We want to change this anyhow to put paging as a separate concert in the result handling dsl. Which will then only start pulling data when you pull data at the end.

                  Comment

                  Working...
                  X