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

@@ -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");