【问题标题】:Spring Data mongodb date range querySpring Data mongodb日期范围查询
【发布时间】:2014-11-28 08:53:23
【问题描述】:

我正在使用带有 mongodb 的 spring 数据来创建应用程序。

我有这个对象:

public class Room {
    private String name;
    private List<Date> occupied;
}

我想最好使用mongodbTemplate 来获取某个日期范围内未被占用的房间列表。

例如,如果我的开始日期为 2014 年 10 月 10 日,结束日期为 2014 年 10 月 15 日,我想获取列表中没有占据日期 10、11、12、13 的房间列表,14,15 为 2014 年 10 月。

有人对此有任何想法吗?

更新:

我已经找到了一种使用此查询的方法:

query.addCriteria(Criteria.where("occupiedDates")
   .ne(from).andOperator(
       Criteria.where("occupiedDates").ne(to))
);

问题是我不能动态添加andOperator。

如果可能,我希望在条件内添加日期列表。

一个示例文档是(mongo这个只有一条记录):

房间(床数=1,床型1=1,床型2=0,床型3=0,床型4=0,
filetype=null, noofrooms=0, occupancy=0, photo=null, rateid=1,
房间名=null,房间号=888,状态=null,
roomTypeID=26060747427845848211948325568,占用日期=[8 月 10 日星期日
2014 年东部标准时间 00:00:00,8 月 11 日星期一 2014 年东部标准时间 00:00:00,8 月 12 日星期二 00:00:00
2014 年欧洲东部标准时间,2014 年 8 月 13 日星期三 00:00:00 EEST],属性={})

这是构建 wyeru 的代码:

SimpleDateFormat dateFormat = new SimpleDateFormat("dd-M-yyyy hh:mm:ss");
Date to = null;
Date from = null;
try {
    to = dateFormat.parse("12-08-2014 00:00:00");
    from = dateFormat.parse("10-08-2014 00:00:00");
} catch (ParseException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
DBObject c1 = new BasicDBObject("occupied", null);
DBObject c2 = BasicDBObjectBuilder.start()
    .push("occupied").push("$not")
    .push("$elemMatch").add("$gte", from).add("$lte", to).get();

Criteria c = Criteria.where("$or").is(Arrays.asList(c1, c2));
Query query = new Query().addCriteria(c);
List<Room> rooms = mongoTemplate.find(query, Room.class);

这个查询被发送到mongodb

{ "$or" : [
  { "occupied" :  null } ,
  { "occupied" :
    { "$not" :
      { "$elemMatch" :
        { "$gte" : { "$date" : "2014-08-09T21:00:00.000Z"} ,
          "$lte" : { "$date" : "2014-08-11T21:00:00.000Z"}
        }
      }
    } 
  }
]}

由此我们了解到查询不应该返回任何内容。但它返回我 1 行。

【问题讨论】:

标签: java mongodb spring-data


【解决方案1】:

根据我最终理解的要求,您想要获取其中的所有文档 occupied 的所有元素都不属于指定的日期范围。

在 mongo shell 上完成:

db.b.find({
    $or : [{
        occupied : null
    }, {
        occupied : {
            $not : {
                $elemMatch : {
                    $gte : start,
                    $lte : end
                }
            }
        }
    }]
}).pretty();

然后翻译成Java代码如下:

// Because "Criteria" has a bug when invoking its method "elemMatch", 
// so I build the criteria by the driver directly, almost.  

DBObject c1 = new BasicDBObject("occupied", null);
DBObject c2 = BasicDBObjectBuilder.start().push("occupied").push("$not").
                        push("$elemMatch").add("$gte", start).add("$lte", end).get();
Criteria c = where("$or").is(Arrays.asList(c1, c2));
Query query = new Query().addCriteria(c);
List<Room> rooms = mongoTemplate.find(query, Room.class);

【讨论】:

  • 您好,很遗憾,这个查询不起作用,它总是返回给我所有的房间。我所做的更改是我将 c2 转换为 BasicDBObject,因为 .get 返回 DBObject。在你有开始和结束变量的地方,我放了 java.util.Date 变量。 :(
  • @giannisapi,很抱歉 C2 应该是 DBObject 类型。我已经在答案中更正了它们。修改后对你有用吗?
  • 还是一样,它返回所有的值。另一个变化是 Criteria c = where 应该是 Criteria c = Criteria.where。我说的对吗?
  • 当我获得对象列表时,日期元素的格式为:Sun Aug 10 00:00:00 EEST 2014,当我执行查询时,发送的格式为:2014-08- 11T21:00:00.000Z 也许这就是查询失败的原因?请注意,我以相同的格式和方式创建两个日期。
  • @giannisapi,您能否发布一些不应按您预期列出的文件。以及您的开始和结束值。顺便说一句,我导入了“where”方法,这样我就可以用那种风格来写了。 Criteria.where是原来的格式,没错。
【解决方案2】:

分析路由

根据您的问题,假设有以下资源:

  • 占用 = [d1, d2, d3, d4, d5]; // 为了更容易表示,假设元素是升序的
  • 范围 = [开始,结束];

因此,如果文档的数据在升序排序后满足以下条件之一,则您希望返回每个文档:

  • start, end, d1, d2, d3, d4, d5 // 等价于:min(occupied) > end
  • d1, d2, d3, d4, d5, start, end // 等价于:max(occupied)

如果元素按顺序存储在占用中,则很容易获取最小值和最大值。 但是你没有提到它,所以我想基本上它们不是按顺序排列的。

不幸的是,在标准查询中没有运算符可以从数组中获取最小值或最大值。 但是根据数组字段的特点对比标准查询, 如果至少一个元素或其自身满足条件,则匹配将返回 true; 仅返回 false 所有不符合条件的情况
幸运地发现min(occupied) &gt; end等价于NOT (at least one element &lt;= end),这是通过以下方法实现的关键点。

在 mongo shell 上实现:

db.b.find({
    $or: [{
        occupied: {
            $not: {
                $lte: end
            }
        }
    }, {
        occupied: {
            $not: {
                $gte: start
            }
        }
    }]
}). pretty();

然后像这样翻译成 Java 代码:

Criteria c = new Criteria().orOperator(where("occupied").not().lte(end),
        where("occupied").not().gte(start));
Query query = new Query().addCriteria(c);
List<Room> rooms = mongoTemplate.find(query, Room.class);

【讨论】:

  • 嗨,我想你误解了我的问题。或者我误解了你的回答:)。你读过这个吗:例如,如果我的开始日期为 2014 年 10 月 10 日,结束日期为 2014 年 10 月 15 日,我想获取列表中没有占据日期 10、11、12 的房间列表,13,14,15 表示 2014 年 10 月。占用的列表存储日期不是范围,因此它可能有 10/10/1985,11/10/1985 但不会有 10/10/1985 - 15/10/1985,而对于 5 天的预订,它将有 5 条记录,例如:10/10/1985、11/10/1985、12/10/1985、13/10/1985、14/10/1985。我解释清楚了吗?感谢您的帮助
  • @giannisapi,我理解你的意图。例如,如果有一个文档,其occupied 的值为:10/10/1985,11/10/1985,12/10/1985,13/10/1985,14/10/1985,那么应该是如果 s=02/10/1985, e=09/10/1985 则选择,如果 s=02/10/1985, e=11/10/1985 则不应选择,因为 10/10/1985 和1985 年 11 月 10 日。这样对吗? :)
  • 你是绝对正确的:) 我需要更仔细地看,更好地理解你的答案。谢谢你:)
  • 你好,我想现在我明白你的答案了,不幸的是我不认为这是正确的。因为占用的可能有 10/10/1985,11/10/1985,12/10/1985,15/10/1985,16/10/1985 等值。然后,如果查询的开始日期为 1985 年 13 月 10 日,结束日期为 1985 年 10 月 14 日,那么您的解决方案将不返回任何内容(说房间在这些日期被占用)。我说的对吗?
  • @giannisapi,你是对的。现在我知道您真正想要做的是获取所有occupied 的元素都不属于指定日期范围的文档。正确的?你原来的解释太酷了,我没能抓住它。 :) 我为你添加另一个答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-07-09
  • 2018-09-18
  • 1970-01-01
  • 1970-01-01
  • 2014-09-29
  • 2014-05-17
  • 2021-01-03
相关资源
最近更新 更多