我想使用indexedDB
管理典型电子邮件收件箱中的电子邮件列表。每封电子邮件只有三个属性:
Time
- 已收到时间电子邮件Sender
- 发件人的电子邮件地址Subject
- 电子邮件的标题我应该如何在indexedDB
中建模数据?如何制定查询以实现以下目标:
Sender
(时间,发件人,主题)的最新电子邮件,按每封最新电子邮件的Time
(降序排序)排序。此列表显示来自每个Sender
的最新电子邮件。Sender
,列出Sender
排序的Time
所有电子邮件。我对NoSql完全不熟悉。我想我可以通过为Sender
创建索引然后打开索引的游标来相对容易地实现第二个查询。
对于第一个查询,我考虑维护一个单独的商店Latest
,该商店会保留每个发件人的最新电子邮件。每次收到新电子邮件时,都必须Put
进入Latest
。显然,这充满了问题,我需要帮助以“正确”的方式去做。
答案 0 :(得分:1)
使用IndexedDB包装器JsStore,您可以创建一个表,用于存储带有自动增量列的邮件(例如Id),并使用此列(Id)对数据进行排序。
盯着案例#2:
用于检索发件人的所有电子邮件
var Connection = new JsStore.Instance("Database_name");
Connection.select({
From: "Inbox", // Inbox is your table name
Where:{
Sender: sender_email
},
Order: {
By: "Id",
Type: "desc"
},
OnSuccess:function (results){
console.log(results);
},
OnError:function (error) {
console.log(error);
}
});
案例#1:
如果我们可以在JsStore中使用GROUP BY子句,那么很容易为每个发件人检索最新的电子邮件但不幸的是,当前版本的JsStore没有提供它,但它的next release可能会支持它。
直到那时我们可以在完整记录中过滤邮件,或者可以使用发件人的电子邮件ID逐个获取。
过滤完整记录:
var Connection = new JsStore.Instance("Database_name");
Connection.select({
From: "Inbox", // Inbox is the table name
Order: {
By: 'Id',
Type: "desc"
},
OnSuccess:function (results){
var sendersName = [];
var filteredMails = results.filter(function(mail){
if(sendersName.indexOf(mail.sender) == -1){
sendersName.push(mail.sender);
return mail;
}
});
console.log(filteredMails);
},
OnError:function (error) {
console.log(error);
}
});
逐个获取每个发件人的最新邮件并推送到一个数组中:
var mails=[];
senders_email.forEach(function(value){ // keep the list of senders email in the array "sender_email"
Connection.select({
From:'Inbox',
Where:{
Sender:value
},
Limit:1,
Order: {
By: 'Id',
Type: "desc"
},
OnSuccess:function(result){
result.length > 0 && mails.push(result[0]);
},
OnError:function(err){
console.log(err);
}
})
});
您可以查看其SQL类型语法,以便在official site上使用IndexedDB进行查询。您可以轻松地解释上述代码。
答案 1 :(得分:0)
作为猜测,它将涉及使用基于多个属性的索引。这有点违反直觉,一开始很难做到,但基本上是:
function onupgradeneeded(event) {
// ...
// make sure to use explcit and exact property names in second
// param to createIndex
store.createIndex('sender-time', ['sender', 'time']);
}
function get_latest() {
// ...
var index = store.index('sender-time');
// use prev so that the sort order is descending instead of
// ascending
// first arg is the range, we are not limiting items in any way
// here, at least explictly. implicitly we are filtering out items
// without a sender or without a time since missing values cause
// the object to be excluded from the index.
var request = index.openCursor(null, 'prevunique');
request.onsuccess = function() {
// This iterates over all items. The trick then is to advance the
// cursor to the prev unique item. Since items in the index are
// ordered first by sender, then by time, jumping to prev unique
// sender skips over the other records for the same sender you
// would see if you normally just advanced the cursor.
}
}
最糟糕的情况是你加载所有项目,然后在内存中进行所有过滤和排序。与最坏的情况相比,上面的代码减少了反序列化的对象数量(从indexedDB加载到js land),并且这些项已经被排序,而nextUnique逻辑发生在C ++ land而不是Js land,所以它的评估速度要快得多。 / p>
对于案例#2,您使用range参数进行openCursor的技巧。如果您想要从最旧到最新的时间排序,则不再需要传递prev。
function get_sender() {
var index = store.index('sender-time');
var lowerBound = ['sender-value-here', smallest date possible in js];
var upperBound = ['the-same-sender-value-here', largest date possible in js];
var range = IDBKeyRange.bounds(lowerBound, upperBound);
// NOTE: above might be wrong I forget, you have to make the
// bounds inclusive, so that sender matches and the bounds do not
// result in excluding all records
// again here you don't need 'prev' 2nd param unless you want
// reverse order
var request = index.openCursor(range);
}
基本上,发件人时间索引有点像SELECT * ORDER BY sender ASC, time ASC
中间结果,然后您可以扫描。因此,在第一种情况下,您使用nextUnique只需跳转到下一个新发件人并获取他们的第一个项目,然后再次跳过。在第二种情况下,您使用像SELECT * FROM table WHERE sender >= sender && sender <= sender && time >= min-time && time <= max-time
这样的where子句进行限制。但请注意,这是一个严重的简化。实际上,我们正在利用这样一个事实:我们绕过了indexedDB中的伪where子句的短路评估,如果我们有更宽松的发送条件,这将导致一切搞砸。但在这种情况下,发送者要么没有条件(在我们只使用排序顺序的情况下),或者在情况2中,发送者总是相同的发送者,我们知道布尔子句的第一部分总是为真所以第二个子句总是评价。