Lucene价格区间(范围)搜索两种方法分析

Lucene API可以有两种方法进行价格区间搜索。(我们采用第二种方法)

1.       采用RangeFilter过滤器进行价格区间搜索。

从名称上可以很容易地看出来,RangeFilter是用于过滤在一定范围内出现的文档

JavaDoc中,对它是这样解释的。

A Filter that restricts search results to a range of values in a given field.

中文翻译为:

Filter用于将检索结果限定在某个给定的Field值的范围内。

具体用法是:

RangeFilter filter = new RangeFilter(“DerivedPrice”,”200”,”500”,true,true);

searcher.search(query,filter);

上述代码表示搜索价格区间(DerivedPrice为索引中价格域)在200500的结果。

搜索结果为:

300.0350.0,400.0,4000.0

问题出来了,为什么4000.0也在搜索结果中?

这是因为,Lucene在按照范围搜索时,将字段值都作为字符串,并按照字典顺序对字符串进行排序,由于4排在25之间,所以4000.0也出现在搜索结果中。当然排序还会对小数点进行排序,这样将导致搜索结果不是我们想要的结果。

怎么解决?

修改索引补零,即对索引中的价格域DerivedPrice进行补零操作,首先要对所有价格有清晰的整体认识,我们索引中的价格都为200,或200.5200.00之类的数据,即小数点后最多2位,而且价格最大差不过10万,为保证万无一失我们先将索引原数据中的所有价格*100,这样就去掉了小数点,然后对所有价格不够10位的前面全部补零,这样可以接受的最大价格为100万。

例如:123变为   0000012300 

123.5 变为 0000012350  

123.55变为0000012355

然后重建索引,再采用我们上述的RangeFilter即可得到我们想要的范围搜索结果了。同时,搜出的结果价格要首先转换为Float,然后除于10才是真正的价格。

优点:可以采用Lucene的内部范围搜索机制,速度快,也省心。

缺点:需要对索引进行修改重建,而且搜出的结果需重新进行价格转换。

2.       借用价格排序方法Sort进行升降序排列,然后采用二分查找法查找价格区间的两个端点,最后取得两个端点之间的值即为落在价格区间的搜索结果。

下面是价格区间封装类PriceRange:

/**

* 价格区间封装类,包括价格区间最小值,最大值和搜索后的升降序排列标示 默认为false

*序排列,true标示降序排列

 *

 * @author 路卫杰

 * @version 1.0, 2010-7-30

 */

public class PriceRange {

    /** 价格区间最小值 */

    private float lower;

 

    /** 价格区间最大值 */

    private float upper;

 

    /** 价格区间排序方式,默认为false升序,true表示升序 */

    private boolean orderFlag = false;

 

    /** 价格区间排序方式,默认为false升序,true表示升序 */

    private Sort priceSort;

 

    /** 升序排列 */

    public final static boolean ASCENDING = false;

 

    /** 降序排列 */

    public final static boolean DESCENDING = true;

 

    /**

     * 构造方法

     *

     * @param lower

     *            价格区间最低值

     * @param upper

     *            价格区间最高值

     */

    public PriceRange(float lower, float upper) {

       if (lower <= upper) {

           this.lower = lower;

           this.upper = upper;

       } else {

           this.lower = upper;

           this.upper = lower;

       }

       this.priceSort = new Sort(new SortField("DerivedPrice", SortField.FLOAT,this.orderFlag));

//下面是本项目的价格Sort初始化方法,为方便交流,上面是Lucene的价格Sort生成方法

//SortBy.getSort(SortBy.PRICE, this.orderFlag);

 

    }

 

    /**

     * 构造方法

     *

     * @param lower

     *            价格区间最低值

     * @param upper

     *            价格区间最高值

     * @param orderFlag

     *            价格区间搜索排序方法,false为升序,true为降序

     */

    public PriceRange(float lower, float upper, boolean orderFlag) {

       if (lower <= upper) {

           this.lower = lower;

           this.upper = upper;

       } else {

           this.lower = upper;

           this.upper = lower;

       }

       this.orderFlag = orderFlag;

       this.priceSort = new Sort(new SortField("DerivedPrice", SortField.FLOAT,this.orderFlag));

//下面是本项目的价格Sort初始化方法,为方便交流,上面是Lucene的价格Sort生成方法

//SortBy.getSort(SortBy.PRICE, this.orderFlag);

 

 

    }

   

    /**

     * 生成价格比lower更高的PriceRange对象

     *

     * @param lower

     *            价格区间最低值

     *           

     * @return 价格比lower更高的PriceRange对象

     */

    public static PriceRange More(float lower) {

       return new PriceRange(lower, Float.MAX_VALUE);

    }

 

    /**

     * 生成价格比lower更高的PriceRange对象

     *

     * @param lower

     *            价格区间最低值

     * @param orderFlag

     *            价格区间搜索排序方法,false为升序,true为降序

     *           

     * @return 价格比lower更高的PriceRange对象

     */

    public static PriceRange More(float lower, boolean orderFlag) {

       return new PriceRange(lower, Float.MAX_VALUE, orderFlag);

    }

 

    /**

     * 生成价格比upper更低的PriceRange对象

     *

     * @param upper

     *            价格区间最高值

     *           

     * @return 价格比upper更低的PriceRange对象

     */

    public static PriceRange Less(float upper) {

       return new PriceRange(Float.MIN_VALUE, upper);

    }

   

    /**

     * 生成价格比upper更低的PriceRange对象

     *

     * @param upper

     *            价格区间最高值

     * @param orderFlag

     *            价格区间搜索排序方法,false为升序,true为降序

     *           

     * @return 价格比upper更低的PriceRange对象

     */

    public static PriceRange Less(float upper, boolean orderFlag) {

       return new PriceRange(Float.MIN_VALUE, upper, orderFlag);

    }

 

    public float getLower() {

       return lower;

    }

 

    public void setLower(float lower) {

       this.lower = lower;

    }

 

    public float getUpper() {

       return upper;

    }

 

    public void setUpper(float upper) {

       this.upper = upper;

    }

 

    public boolean isOrderFlag() {

       return orderFlag;

    }

 

    public void setOrderFlag(boolean orderFlag) {

       this.orderFlag = orderFlag;

    }

 

    public Sort getPriceSort() {

       return priceSort;

    }

 

    public void setPriceSort(Sort priceSort) {

       this.priceSort = priceSort;

    }

 

}

 

 

下面是对按照价格排序后的结果进行二分查找,查找价格区间两个端点在结果中的位置。

    /**

     * 二分查找算法,查找Hits对象中DerivedPrice值为valueDocumentHits中的位置

     *

     * @param hits

     *            Hits对象

     * @param value

     *            要查找的值

     * @param orderFlag

     *            原数组升降序标示,false表示为升序排列,true表示为降序排列

     * @param flag

     *            如果没有此值,orderFlagfalse前提下:true表示取比其大的值的位置,false表示取比其小的值的位置;

     *            orderFlagtrue前提下:true表示取比其小的值的位置,false表示取比其大的值的位置。

     *

     * @throws IOException

     * @throws CorruptIndexException

     * @throws NumberFormatException

     *

     * @return Hits对象中DerivedPrice值为valueDocumentHits中的位置,如果value值不在hits中,若flagtrue返回比value大的值的位置,若flagfalse返回比value小的值的位置

     */

    private int binarySearch(Hits hits, float value, boolean orderFlag,

           boolean flag) throws NumberFormatException, CorruptIndexException,

           IOException {

       int low = 0;

       int high = hits.length() - 1;

 

       // 根据orderFlag标示循环进行二分查找

       if (orderFlag) {// 原数组为降序情况

           while (low <= high) {

              int mid = (low + high) / 2;

              float midVal = Float.parseFloat(hits.doc(mid).get(

                     "DerivedPrice"));

              if (midVal > value)

                  low = mid + 1;

              else if (midVal < value)

                  high = mid - 1;

              else {

                  // 查找第一次出现的位置

                  if (!flag) {

                     int i = mid - 1;

                     for (; i > 0; i--) {

                         if (Float.parseFloat(hits.doc(i)

                                .get("DerivedPrice")) == value)

                            continue;

                         else

                            break;

                     }

                     return ++i;

                  } else {

                     int i = mid + 1;

                     for (; i < hits.length(); i++) {

                         if (Float.parseFloat(hits.doc(i)

                                .get("DerivedPrice")) == value)

                            continue;

                         else

                            break;

                     }

                     return --i;

                  }

              }

 

           }

           // 如果flagfalse返回比其大的值的位置,否则返回比其小的值的位置

           if (!flag)

              return (low < hits.length() ? low : hits.length() - 1);

           else

              return (high >= 0 ? high : 0);

       } else {// 原数组为升序情况

           while (low <= high) {

              int mid = (low + high) / 2;

              float midVal = Float.parseFloat(hits.doc(mid).get(

                     "DerivedPrice"));

 

              if (midVal < value)

                  low = mid + 1;

              else if (midVal > value)

                  high = mid - 1;

              else {

                  // 查找第一次出现的位置

                  if (flag) {

                     int i = mid - 1;

                     for (; i > 0; i--) {

                         if (Float.parseFloat(hits.doc(i)

                                .get("DerivedPrice")) == value)

                            continue;

                         else

                            break;

                     }

                     return ++i;

                  } else {

                     int i = mid + 1;

                     for (; i < hits.length(); i++) {

                         if (Float.parseFloat(hits.doc(i)

                                .get("DerivedPrice")) == value)

                            continue;

                         else

                            break;

                     }

                     return --i;

                  }

              }

 

           }

           // 如果flagtrue返回比其大的值的位置,否则返回比其小的值的位置

           if (flag)

              return (low < hits.length() ? low : hits.length() - 1);

           else

              return (high >= 0 ? high : 0);

       }

 

    }

 

 

对二分查找的调用如下:

/** 价格区间最小值在hits中的位置 */

           int low = binarySearch(hits, lower, orderFlag, true);

 

           /** 价格区间最大值在hits中的位置 */

           int up = binarySearch(hits, upper, orderFlag, false);

 

           // 价格区间是否有结果标志,默认isFlagtrue

           boolean isFlag = true;

          

           // 如果orderFlagfalse,即hits中价格为升序且low>up

           // 如果orderFlagtrue,即原hits中价格为降序且low<up

           // 则表明价格区间中没有结果,将isFlagfalse

           if ((!orderFlag && low > up) || (orderFlag && low < up))

              isFlag = false;

           int totalRecords = 0;

           if (low >= 0 && up >= 0  && isFlag) {

              // 如果up小于low,则进行交换

              if (up < low) {

                  int mid = up;

                  up = low;

                  low = mid;

              }

 

              // 在价格区间的总数目

              totalRecords = up - low + 1;

              }

 

             // 根据总数目生成Page对象

              page = PageUtil.createPage(page, totalRecords);

 

              // 此页的开始记录和结束记录

              int start = page.getBeginIndex();

              int end = start + page.getEveryPage();

 

              // 如果end大于totalRecords则取totalRecords

              end = (end < totalRecords ? end : totalRecords);

 

              // 获取搜索结果

              for (int i = low + start; i < low + end; i++) {

                  Document doc = hits.doc(i);

                  OutShow outShow = new OutShow();

                  outShow.setProduct(doc.get("Product"));

                  outShow.setUrl(doc.get("Page_url"));

                  outShow.setDescription(doc.get("Description"));

                  outShow.setOnHand(doc.get("OnHand"));

                  outShow.setCharSet(doc.get("Charset"));

                  outShow.setCrawlertime(doc.get("Crawlertime"));

                  outShow.setAddress(doc.get("Address"));

                  outShow.setProvider(doc.get("Provider"));

                  outShow.setSite(doc.get("Site"));

                  float credit = Float

                         .valueOf(doc.get("DerivedCreditPoint") == null ? "0.0"

                                : doc.get("DerivedCreditPoint"));

                  outShow.setCreditPoint(credit);

                  float price1 = Float

                         .valueOf(doc.get("DerivedPrice") == null ? "0.0"

                                : doc.get("DerivedPrice"));

                  outShow.setPrice(price1);

                  int itemSold = Integer

                         .valueOf(doc.get("DerivedItemSold") == null ? "0"

                                : doc.get("DerivedItemSold"));

                  outShow.setItemSold(itemSold);

                  outShow.setScore(doc.getBoost());

                  outShow.setDerivedCatNum(doc.get("DerivedCatNum"));

                  outShow.setPictureUrl(doc.get("PictureUrl"));

 

                  list.add(outShow);

 

              }

 

           }

           System.out.println("共搜索到 " + totalRecords + " 个结果!");

 

       }

 

 

PriceRange的调用如下:

PriceRange pRange=new PriceRange(Float.parseFloat("2324"),Float.parseFloat("2340"));

search.search(page,pRange);

 

优点:不用修改索引,搜出的结果也无需对价格进行转换

缺点:无法在价格区间的搜索的同时对其它字段进行排序。由于本方法使用的是价格排序,然后对结果进行二分查找,所以无法同时对其它字段进行排序。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
Lucene suggest的实现代码可以分为以下几个部分: 1. 构建索引 Lucene suggest中的索引是使用Lucene库中的IndexWriter和Document对象来创建的。首先,需要创建一个IndexWriter对象,并指定索引的存储位置和一些基本配置。然后,需要遍历数据源中的每个文档,将其转换为Lucene的Document对象,并将其添加到IndexWriter中。在构建Document对象时,需要将文档中的每个单词分解为三元组,并将其添加到Document的一个或多个字段中。 2. 构建前缀数据结构 在索引构建完成后,需要构建一个前缀数据结构,用于搜索建议的查询。这个前缀数据结构是通过遍历索引中的所有文档和字段来创建的。对于每个字段,需要提取其中的所有三元组,并将它们添加到前缀数据结构中。这个前缀数据结构是一个基于Trie树的数据结构,可以有效地支持前缀匹配查询。 3. 提供搜索建议 在前缀数据结构构建完成后,就可以使用它来提供搜索建议了。对于每个用户查询Lucene suggest会遍历前缀数据结构,查找所有以查询词为前缀的三元组,并将它们按照频率和相关性进行排序。然后,Lucene suggest会返回前N个排名最高的三元组,作为搜索建议的结果。 以上就是Lucene suggest的主要实现代码和流程。需要注意的是,这只是一个概述,具体的实现细节和代码会因应用场景和需求而有所不同。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值