aboutsummaryrefslogtreecommitdiff
path: root/libdino
diff options
context:
space:
mode:
authorbobufa <bobufa@users.noreply.github.com>2018-08-01 15:20:56 +0200
committerbobufa <bobufa@users.noreply.github.com>2018-08-13 22:39:18 +0200
commitc0844bdea428c10949339960bd16ea5e2a335fb8 (patch)
treef2e1e3f0257a7d0ef41f052a31eb143f613fb8d6 /libdino
parentb0c94641fcf85ed55976e894fea970331ae22892 (diff)
downloaddino-c0844bdea428c10949339960bd16ea5e2a335fb8.tar.gz
dino-c0844bdea428c10949339960bd16ea5e2a335fb8.zip
add suggestions/auto-complete for search filters
Diffstat (limited to 'libdino')
-rw-r--r--libdino/src/service/search_processor.vala159
1 files changed, 155 insertions, 4 deletions
diff --git a/libdino/src/service/search_processor.vala b/libdino/src/service/search_processor.vala
index e56efa41..3f746981 100644
--- a/libdino/src/service/search_processor.vala
+++ b/libdino/src/service/search_processor.vala
@@ -31,19 +31,19 @@ public class SearchProcessor : StreamInteractionModule, Object {
foreach(string word in query.split(" ")) {
if (word.has_prefix("with:")) {
if (with == null) {
- with = word.substring(5) + "%";
+ with = word.substring(5);
} else {
return db.message.select().where("0");
}
} else if (word.has_prefix("in:")) {
if (in_ == null) {
- in_ = word.substring(3) + "%";
+ in_ = word.substring(3);
} else {
return db.message.select().where("0");
}
} else if (word.has_prefix("from:")) {
if (from == null) {
- from = word.substring(5) + "%";
+ from = word.substring(5);
} else {
return db.message.select().where("0");
}
@@ -90,9 +90,143 @@ public class SearchProcessor : StreamInteractionModule, Object {
return rows;
}
+ public Gee.List<SearchSuggestion> suggest_auto_complete(string query, int cursor_position, int limit = 5) {
+ int after_prev_space = query.substring(0, cursor_position).last_index_of(" ") + 1;
+ int next_space = query.index_of(" ", after_prev_space);
+ if (next_space < 0) next_space = query.length;
+ string current_query = query.substring(after_prev_space, next_space - after_prev_space);
+ Gee.List<SearchSuggestion> suggestions = new ArrayList<SearchSuggestion>();
+
+ if (current_query.has_prefix("from:")) {
+ if (cursor_position < after_prev_space + 5) return suggestions;
+ string current_from = current_query.substring(5);
+ string[] splitted = query.split(" ");
+ foreach(string s in splitted) {
+ if (s.has_prefix("from:") && s != "from:" + current_from) {
+ // Already have an from: filter -> no useful autocompletion possible
+ return suggestions;
+ }
+ }
+ string? current_in = null;
+ string? current_with = null;
+ foreach(string s in splitted) {
+ if (s.has_prefix("in:")) {
+ current_in = s.substring(3);
+ } else if (s.has_prefix("with:")) {
+ current_with = s.substring(5);
+ }
+ }
+ if (current_in != null && current_with != null) {
+ // in: and with: -> no useful autocompletion possible
+ return suggestions;
+ }
+ if (current_with != null) {
+ // Can only be the other one or us
+
+ // Normal chat
+ QueryBuilder chats = db.conversation.select()
+ .join_with(db.jid, db.jid.id, db.conversation.jid_id)
+ .join_with(db.account, db.account.id, db.conversation.account_id)
+ .with(db.jid.bare_jid, "=", current_with)
+ .with(db.account.enabled, "=", true)
+ .with(db.conversation.type_, "=", Conversation.Type.CHAT)
+ .order_by(db.conversation.last_active, "DESC");
+ foreach(Row chat in chats) {
+ if (suggestions.size == 0) {
+ suggestions.add(new SearchSuggestion(new Account.from_row(db, chat), new Jid(chat[db.jid.bare_jid]), "from:"+chat[db.jid.bare_jid], after_prev_space, next_space));
+ }
+ suggestions.add(new SearchSuggestion(new Account.from_row(db, chat), new Jid(chat[db.account.bare_jid]), "from:"+chat[db.account.bare_jid], after_prev_space, next_space));
+ }
+ return suggestions;
+ }
+ if (current_in != null) {
+ // All members of the MUC with history
+ QueryBuilder msgs = db.message.select()
+ .select_string(@"account.*, $(db.message.counterpart_resource)")
+ .join_with(db.jid, db.jid.id, db.message.counterpart_id)
+ .join_with(db.account, db.account.id, db.message.account_id)
+ .with(db.jid.bare_jid, "=", current_in)
+ .with(db.account.enabled, "=", true)
+ .with(db.message.type_, "=", Message.Type.GROUPCHAT)
+ .with(db.message.counterpart_resource, "LIKE", @"%$current_from%")
+ .group_by({db.message.counterpart_resource})
+ .order_by_name(@"MAX($(db.message.time))", "DESC")
+ .limit(5);
+ foreach(Row msg in msgs) {
+ suggestions.add(new SearchSuggestion(new Account.from_row(db, msg), new Jid(current_in).with_resource(msg[db.message.counterpart_resource]), "from:"+msg[db.message.counterpart_resource], after_prev_space, next_space));
+ }
+ }
+ // TODO: auto complete from
+ } else if (current_query.has_prefix("with:")) {
+ if (cursor_position < after_prev_space + 5) return suggestions;
+ string current_with = current_query.substring(5);
+ string[] splitted = query.split(" ");
+ foreach(string s in splitted) {
+ if ((s.has_prefix("with:") && s != "with:" + current_with) || s.has_prefix("in:")) {
+ // Already have an in: or with: filter -> no useful autocompletion possible
+ return suggestions;
+ }
+ }
+
+ // Normal chat
+ QueryBuilder chats = db.conversation.select()
+ .join_with(db.jid, db.jid.id, db.conversation.jid_id)
+ .join_with(db.account, db.account.id, db.conversation.account_id)
+ .outer_join_on(db.roster, @"$(db.jid.bare_jid) = $(db.roster.jid) AND $(db.account.id) = $(db.roster.account_id)")
+ .where(@"$(db.jid.bare_jid) LIKE ? OR $(db.roster.handle) LIKE ?", {@"%$current_with%", @"%$current_with%"})
+ .with(db.account.enabled, "=", true)
+ .with(db.conversation.type_, "=", Conversation.Type.CHAT)
+ .order_by(db.conversation.last_active, "DESC")
+ .limit(limit);
+ foreach(Row chat in chats) {
+ suggestions.add(new SearchSuggestion(new Account.from_row(db, chat), new Jid(chat[db.jid.bare_jid]), "with:"+chat[db.jid.bare_jid], after_prev_space, next_space) { order = chat[db.conversation.last_active]});
+ }
+
+ // Groupchat PM
+ if (suggestions.size < 5) {
+ chats = db.conversation.select()
+ .join_with(db.jid, db.jid.id, db.conversation.jid_id)
+ .join_with(db.account, db.account.id, db.conversation.account_id)
+ .where(@"$(db.jid.bare_jid) LIKE ? OR $(db.conversation.resource) LIKE ?", {@"%$current_with%", @"%$current_with%"})
+ .with(db.account.enabled, "=", true)
+ .with(db.conversation.type_, "=", Conversation.Type.GROUPCHAT_PM)
+ .order_by(db.conversation.last_active, "DESC")
+ .limit(limit - suggestions.size);
+ foreach(Row chat in chats) {
+ suggestions.add(new SearchSuggestion(new Account.from_row(db, chat), new Jid(chat[db.jid.bare_jid]).with_resource(chat[db.conversation.resource]), "with:"+chat[db.jid.bare_jid]+"/"+chat[db.conversation.resource], after_prev_space, next_space) { order = chat[db.conversation.last_active]});
+ }
+ suggestions.sort((a, b) => (int)(b.order - a.order));
+ }
+ } else if (current_query.has_prefix("in:")) {
+ if (cursor_position < after_prev_space + 3) return suggestions;
+ string current_in = current_query.substring(3);
+ string[] splitted = query.split(" ");
+ foreach(string s in splitted) {
+ if ((s.has_prefix("in:") && s != "in:" + current_in) || s.has_prefix("with:")) {
+ // Already have an in: or with: filter -> no useful autocompletion possible
+ return suggestions;
+ }
+ }
+ QueryBuilder groupchats = db.conversation.select()
+ .join_with(db.jid, db.jid.id, db.conversation.jid_id)
+ .join_with(db.account, db.account.id, db.conversation.account_id)
+ .with(db.jid.bare_jid, "LIKE", @"%$current_in%")
+ .with(db.account.enabled, "=", true)
+ .with(db.conversation.type_, "=", Conversation.Type.GROUPCHAT)
+ .order_by(db.conversation.last_active, "DESC")
+ .limit(limit);
+ foreach(Row chat in groupchats) {
+ suggestions.add(new SearchSuggestion(new Account.from_row(db, chat), new Jid(chat[db.jid.bare_jid]), "in:"+chat[db.jid.bare_jid], after_prev_space, next_space));
+ }
+ } else {
+ // Other auto complete?
+ }
+ return suggestions;
+ }
+
public Gee.List<MessageItem> match_messages(string query, int offset = -1) {
Gee.List<MessageItem> ret = new ArrayList<MessageItem>();
- var rows = prepare_search(query, true).limit(10);
+ QueryBuilder rows = prepare_search(query, false).limit(10);
if (offset > 0) {
rows.offset(offset);
}
@@ -109,4 +243,21 @@ public class SearchProcessor : StreamInteractionModule, Object {
}
}
+public class SearchSuggestion : Object {
+ public Account account { get; private set; }
+ public Jid? jid { get; private set; }
+ public string completion { get; private set; }
+ public int start_index { get; private set; }
+ public int end_index { get; private set; }
+ public long order { get; set; }
+
+ public SearchSuggestion(Account account, Jid? jid, string completion, int start_index, int end_index) {
+ this.account = account;
+ this.jid = jid;
+ this.completion = completion;
+ this.start_index = start_index;
+ this.end_index = end_index;
+ }
+}
+
}