Simple metadata queries. #222

Added support for retrieving metadata that matches all key/value pairs in a query.
This commit is contained in:
akwizgran
2016-05-12 17:40:11 +01:00
parent af1f267d4b
commit f2ab0eff53
8 changed files with 258 additions and 38 deletions

View File

@@ -85,29 +85,6 @@ class ClientHelperImpl implements ClientHelper {
return messageFactory.createMessage(g, timestamp, toByteArray(body));
}
@Override
public BdfDictionary getMessageAsDictionary(MessageId m) throws DbException,
FormatException {
BdfDictionary dictionary;
Transaction txn = db.startTransaction(true);
try {
dictionary = getMessageAsDictionary(txn, m);
txn.setComplete();
} finally {
db.endTransaction(txn);
}
return dictionary;
}
@Override
public BdfDictionary getMessageAsDictionary(Transaction txn, MessageId m)
throws DbException, FormatException {
byte[] raw = db.getRawMessage(txn, m);
if (raw == null) return null;
return toDictionary(raw, MESSAGE_HEADER_LENGTH,
raw.length - MESSAGE_HEADER_LENGTH);
}
@Override
public BdfList getMessageAsList(MessageId m) throws DbException,
FormatException {
@@ -174,7 +151,7 @@ class ClientHelperImpl implements ClientHelper {
}
@Override
public Map<MessageId, BdfDictionary> getMessageMetatataAsDictionary(
public Map<MessageId, BdfDictionary> getMessageMetadataAsDictionary(
GroupId g) throws DbException, FormatException {
Map<MessageId, BdfDictionary> map;
Transaction txn = db.startTransaction(true);
@@ -198,6 +175,34 @@ class ClientHelperImpl implements ClientHelper {
return Collections.unmodifiableMap(parsed);
}
@Override
public Map<MessageId, BdfDictionary> getMessageMetadataAsDictionary(
GroupId g, BdfDictionary query) throws DbException,
FormatException {
Map<MessageId, BdfDictionary> map;
Transaction txn = db.startTransaction(true);
try {
map = getMessageMetadataAsDictionary(txn, g, query);
txn.setComplete();
} finally {
db.endTransaction(txn);
}
return map;
}
@Override
public Map<MessageId, BdfDictionary> getMessageMetadataAsDictionary(
Transaction txn, GroupId g, BdfDictionary query) throws DbException,
FormatException {
Metadata metadata = metadataEncoder.encode(query);
Map<MessageId, Metadata> raw = db.getMessageMetadata(txn, g, metadata);
Map<MessageId, BdfDictionary> parsed =
new HashMap<MessageId, BdfDictionary>(raw.size());
for (Entry<MessageId, Metadata> e : raw.entrySet())
parsed.put(e.getKey(), metadataParser.parse(e.getValue()));
return Collections.unmodifiableMap(parsed);
}
@Override
public void mergeGroupMetadata(GroupId g, BdfDictionary metadata)
throws DbException, FormatException {

View File

@@ -273,6 +273,16 @@ interface Database<T> {
*/
Collection<MessageId> getMessageIds(T txn, GroupId g) throws DbException;
/**
* Returns the IDs of any messages in the given group with metadata
* matching all entries in the given query. If the query is empty, the IDs
* of all messages are returned.
* <p/>
* Read-only.
*/
Collection<MessageId> getMessageIds(T txn, GroupId g, Metadata query)
throws DbException;
/**
* Returns the metadata for all messages in the given group.
* <p/>
@@ -281,6 +291,16 @@ interface Database<T> {
Map<MessageId, Metadata> getMessageMetadata(T txn, GroupId g)
throws DbException;
/**
* Returns the metadata for any messages in the given group with metadata
* matching all entries in the given query. If the query is empty, the
* metadata for all messages is returned.
* <p/>
* Read-only.
*/
Map<MessageId, Metadata> getMessageMetadata(T txn, GroupId g,
Metadata query) throws DbException;
/**
* Returns the metadata for the given message.
* <p/>

View File

@@ -427,6 +427,14 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getMessageMetadata(txn, g);
}
public Map<MessageId, Metadata> getMessageMetadata(Transaction transaction,
GroupId g, Metadata query) throws DbException {
T txn = unbox(transaction);
if (!db.containsGroup(txn, g))
throw new NoSuchGroupException();
return db.getMessageMetadata(txn, g, query);
}
public Metadata getMessageMetadata(Transaction transaction, MessageId m)
throws DbException {
T txn = unbox(transaction);

View File

@@ -36,10 +36,12 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -1133,6 +1135,44 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public Collection<MessageId> getMessageIds(Connection txn, GroupId g,
Metadata query) throws DbException {
// If there are no query terms, return all messages
if (query.isEmpty()) return getMessageIds(txn, g);
PreparedStatement ps = null;
ResultSet rs = null;
try {
// Retrieve the message IDs for each query term and intersect
Set<MessageId> intersection = null;
String sql = "SELECT m.messageId"
+ " FROM messages AS m"
+ " JOIN messageMetadata AS md"
+ " ON m.messageId = md.messageId"
+ " WHERE groupId = ? AND key = ? AND value = ?";
for (Entry<String, byte[]> e : query.entrySet()) {
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes());
ps.setString(2, e.getKey());
ps.setBytes(3, e.getValue());
rs = ps.executeQuery();
Set<MessageId> ids = new HashSet<MessageId>();
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
rs.close();
ps.close();
if (intersection == null) intersection = ids;
else intersection.retainAll(ids);
// Return early if there are no matches
if (intersection.isEmpty()) return Collections.emptySet();
}
if (intersection == null) throw new IllegalStateException();
return Collections.unmodifiableSet(intersection);
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public Map<MessageId, Metadata> getMessageMetadata(Connection txn,
GroupId g) throws DbException {
PreparedStatement ps = null;
@@ -1169,6 +1209,18 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public Map<MessageId, Metadata> getMessageMetadata(Connection txn,
GroupId g, Metadata query) throws DbException {
// Retrieve the matching message IDs
Collection<MessageId> matches = getMessageIds(txn, g, query);
if (matches.isEmpty()) return Collections.emptyMap();
// Retrieve the metadata for each match
Map<MessageId, Metadata> all = new HashMap<MessageId, Metadata>(
matches.size());
for (MessageId m : matches) all.put(m, getMessageMetadata(txn, m));
return Collections.unmodifiableMap(all);
}
public Metadata getGroupMetadata(Connection txn, GroupId g)
throws DbException {
return getMetadata(txn, g.getBytes(), "groupMetadata", "groupId");