在我的应用程序中有2个线程:
>抓取网站并将数据插入MongoDB
>检索已爬网站点并执行业务逻辑
为了检索已爬网站点,我使用以下查询:
Document query = new Document("fetchStatus", new Document("$lte", fetchStatusParam));
FindIterable<Document> unfetchedEpisodes = dbC_Episodes.find(query);
结果我得到了所有剧集,其fetchStatusParam小于或等于特定值.
下一步,我将结果集的项目存储在HashMap< String,TrackedEpisode>中,这是一个对象属性,以便跟踪它们:
for (Document document : unfetchedEpisodes) {
this.trackedEpisodes.put(document.get("_id").toString(), new TrackedEpisode(document));
}
然后我做了一些业务逻辑,其中:
>不修改unfetchedEpisodes结果集.
>不会从trackedEpisodes中删除任何对象.
到目前为止一切都还可以.
最后一步,我传递所有检索到的文档并将其标记为已获取,以防止将来重复提取.
for (Document document : unfetchedEpisodes) {
if (this.trackedEpisodes.containsKey(document.get("_id").toString())) {
// prevent repeated fetching
document.put("fetchStatus", FetchStatus.IN_PROCESS.getID());
if (this.trackedEpisodes.get(document.get("_id").toString()).isExpired()) {
document.put("isExpired", true);
document.put("fetchStatus", FetchStatus.FETCHED.getID());
}
} else {
System.out.println("BOO! Strange new object detected");
}
dbC_Episodes.updateOne(new Document("_id", document.get("_id")), new Document("$set", document));
}
我运行这段代码几天,并注意到它有时会到达if(this.trackedEpisodes.containsKey())语句的else部分.这对我来说很奇怪,unfetchedEpisodes和trackedEpisodes如何不同步并且不包含相同的项目?
我开始调查这个案例,并注意到我到达“BOO!奇怪的新对象检测到”的时间文件迭代器包含数据库中的项目但不应该在unfetchedEpisodes中,因为我没有执行新的查询数据库.
我检查了几次将检索到的项目存储到trackedEpisodes的问题,并且始终将来自unfetchedEpisodes的所有元素添加到trackedEpisodes,但在此之后我仍然会到达“BOO!检测到奇怪的新对象”.
我的问题:
>为什么unfetchedEpisodes在执行查询后获取新项?
>执行Collection#query()后,MongoDB驱动程序是否可以修改unfetchedEpisodes?
>从MongoDB执行查询后,我可能应该使用.close()吗?
使用的版本:
> MongoDB:3.2.3,x64
> MongoDB Java驱动程序:mongodb-driver-3.2.2,mongodb-driver-core-3.2.2,bson-3.2.2
解决方法:
当你在这里打电话时:
FindIterable<Document> unfetchedEpisodes = dbC_Episodes.find(query);
你实际上并没有收到所有的剧集.您正在获取指向匹配文档的数据库游标.
然后当你打电话:
for (Document document : unfetchedEpisodes){}
在与查询匹配的所有文档上创建迭代器.
当您再次调用它时,将为同一查询返回一个新游标,并且迭代现在匹配的所有文档.
如果集合之间发生了变化,结果将会有所不同.
如果你想确保unfetchedEpisodes的内容没有改变,那么你可以将一个选项拉到内存中并在内存中而不是在数据库上迭代它,例如,
ArrayList<Document> unfetchedEpisodes = dbC_Episodes.find(query).into(new ArrayList<Document>());