日期范围内按集合计数的Hibernate搜索顺序

时间:2014-12-02 16:24:27

标签: java hibernate lucene hibernate-search

我有两个对象 - 一个Document对象和一个Hit对象:

Document
    id
    description
    hits (Collection of type Hits)
    ...

Hit
    documentId (Type Document)
    date
    ...

我试图创建一个搜索文档描述的Hibernate Search查询,然后按给定日期范围内的点击次数对结果进行排序。

我在如何做到这一点时遇到了一些麻烦。我已经查看了this,但它考虑了点击总数,并且不允许动态日期范围查询,因为桥接字段的更新与日期无关。

我目前有一个Document对象的索引,搜索description正常工作。我想我可能需要为Hit对象创建索引,针对日期范围运行查询,按documentId分组,然后使用搜索的术语运行第二个查询反对Document索引。

鉴于这种策略,我仍然不确定如何:

  1. documentId
  2. 对点击进行分组
  3. 维护点击查询结果的顺序
  4. 处理分页。
  5. 我的猜测是我需要从Hits索引中获取所有结果(使用某种类型的分面逻辑进行分组/排序?),然后匹配documentId / term并处理分页在第二个查询中。我只是不确定在使用QueryBuilder界面构建查询时所有这些看起来如何,或者如果有一种完全不同的方法来解决这个问题我还没有想到了。

    更新

    public class DateRangeDownloadsFieldComparator extends FieldComparator<Integer> {
    
        class DownloadsParser implements FieldCache.IntParser {
    
            @Override
            public int parseInt(String string) {
               /* 
                * What do I pass here and what do I do with it? 
                * Ideally, I would pass the downloads collection and return the size of the
                * collection where the download appears within the provided date range, but 
                * passing a collection here does nothing, as it's silently ignored.
                */
    
                return 0;
            }
        }
    
        private Calendar startDate;
    
        private Calendar endDate;
    
        private final int[] fieldValues;
    
        private int bottom;
    
        private int[] currentReaderValues;
    
        public DateRangeDownloadsFieldComparator(int numHits, Calendar startDate, Calendar endDate) {
    
            super();
            this.startDate = startDate;
            this.endDate = endDate;
    
            fieldValues = new int[numHits];
        }
    
        @Override
        public int compare(int slot1, int slot2) {
    
            return compareValues(fieldValues[slot1], fieldValues[slot2]);
        }
    
        @Override
        public int compareBottom(int doc) throws IOException {
    
            int currentDoc = currentReaderValues[doc];
    
            return compareValues(bottom, currentDoc);
        }
    
        @Override
        public int compareValues(Integer v1, Integer v2) {
    
            if (v1 > v2) {
                return 1;
            }
            else if (v1 < v2) {
                return -1;
            }
            else {
                return 0;
            }
        }
    
        @Override
        public void copy(int slot, int doc) throws IOException {
    
            int v1 = currentReaderValues[doc];
            fieldValues[slot] = v1;
    
        }
    
        @Override
        public void setBottom(int slot) {
    
            bottom = fieldValues[slot];
    
        }
    
        @Override
        public void setNextReader(IndexReader reader, int docBase) throws IOException {
    
            currentReaderValues = FieldCache.DEFAULT
                    .getInts(reader, "downloads", new DownloadsParser());
        }
    
        @Override
        public Integer value(int slot) {
    
            return fieldValues[slot];
        }
    
    }
    

    更新2

    文件实体

    @Entity                                                                                                                                
    @Table(name = "documents")                                                                                                             
    @Indexed(index = "documents")                                                                                                          
    public class EDocument {                                                                                                               
    
        public static final String FIELD_NAME = "name";                                                                                    
    
        public static final String FIELD_CREATED = "created";                                                                              
    
        public static final String FIELD_DESCRIPTION = "description";                                                                      
    
        @Id                                                                                                                                
        @GeneratedValue                                                                                                                    
        @Column(name = "id")                                                                                                               
        private Long id;                                                                                                                   
    
        @Temporal(TemporalType.TIMESTAMP)                                                                                                  
        @Column(name = "created")                                                                                                          
        @Field(name = FIELD_CREATED)                                                                                                       
        private Calendar created;                                                                                                          
    
        @Column(name = "name")                                                                                                             
        @Field(name = FIELD_NAME)                                                                                                          
        private String name;                                                                                                               
    
        @Column(name = "description")                                                                                                      
        @Field(name = FIELD_DESCRIPTION)                                                                                                   
        private String description;                                                                                                        
    
        @ManyToOne(fetch = FetchType.LAZY)                                                                                                 
        @JoinColumn(name = "user_id", nullable = false)                                                                                    
        @IndexedEmbedded(depth = 1)                                                                                                        
        private EUser user;                                                                                                                
    
        @OneToMany(                                                                                                                        
                fetch = FetchType.LAZY,                                                                                                    
                targetEntity = EDownload.class,                                                                                            
                mappedBy = "document",                                                                                                     
                orphanRemoval = true,                                                                                                      
                cascade = CascadeType.ALL)                                                                                                 
        @IndexedEmbedded(depth = 1)                                                                             
        private Set<EDownload> downloads;                                                                                                  
    
        public EDocument() {                                                                                                               
    
            created = Calendar.getInstance();                                                                                              
        }                                                                                                                                  
    
        // getters and setters                                                                                                             
    
    }        
    

    下载实体

    @Entity
    @Table(name = "downloads")
    public class EDownload {
    
        public static final String FIELD_REQUESTED = "requested";
    
        @Id
        @GeneratedValue
        @Column(name = "id")
        private Long id;
    
        @Column(name = "requested")
        @Field(name = FIELD_REQUESTED)
        private Calendar requested;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "document_id", nullable = false)
        private EDocument document;
    
        public EDownload() {
    
            requested = Calendar.getInstance();
        }
    
        // getters and setters
    }                                                                                                                              
    

2 个答案:

答案 0 :(得分:0)

我可能会使用@IndexedEmbedded将匹配作为文档索引的一部分进行索引。然后,您针对description字段运行查询,提供自定义Sort实施。自定义排序将需要数据范围作为参数,然后计算此日期范围内的匹配并相应地进行排序。

答案 1 :(得分:0)

我最终得到的东西类似于@gmansoor建议的东西 - 第一个查询条款,返回ID,然后第二个查询基于这些ID。在Hibernate Search的单个查询中,没有一种方法可以做到这一点,所以这似乎是目前最好的解决方案。如果有人知道更好的方法,请告诉我。