message_search.proto 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. syntax = "proto3";
  2. package chatto.api.v1;
  3. import "buf/validate/validate.proto";
  4. import "chatto/api/v1/message_types.proto";
  5. import "chatto/api/v1/room_timeline.proto";
  6. import "chatto/api/v1/rooms.proto";
  7. import "chatto/api/v1/threads.proto";
  8. import "google/protobuf/duration.proto";
  9. import "google/protobuf/timestamp.proto";
  10. // Ordering for message or grouped-thread search results.
  11. enum MessageSearchOrder {
  12. // Use relevance order.
  13. MESSAGE_SEARCH_ORDER_UNSPECIFIED = 0;
  14. // Highest message score first; grouped results use their best matching message.
  15. MESSAGE_SEARCH_ORDER_RELEVANCE = 1;
  16. // Newest matching message first, also when results are grouped.
  17. MESSAGE_SEARCH_ORDER_NEWEST = 2;
  18. // Return threads with the most recent activity first. Requires thread grouping.
  19. MESSAGE_SEARCH_ORDER_THREAD_ACTIVITY = 3;
  20. // Newest root-message creation first, with unknown dates last. Requires thread
  21. // grouping. Older servers can reject this order.
  22. MESSAGE_SEARCH_ORDER_THREAD_CREATED = 4;
  23. }
  24. // Candidate scope, intersected with the caller's current access and query filters.
  25. enum MessageSearchScope {
  26. // Search all accessible messages. This is the existing default.
  27. MESSAGE_SEARCH_SCOPE_UNSPECIFIED = 0;
  28. // Search roots and replies in followed threads, including direct messages.
  29. MESSAGE_SEARCH_SCOPE_FOLLOWED_THREADS = 1;
  30. }
  31. // Determines the result type and pagination unit.
  32. enum MessageSearchGroupBy {
  33. // Return individual matching messages.
  34. MESSAGE_SEARCH_GROUP_BY_UNSPECIFIED = 0;
  35. // Return distinct thread rows. A root without replies is also a thread.
  36. MESSAGE_SEARCH_GROUP_BY_THREAD = 1;
  37. }
  38. // Current availability of message search on this server.
  39. enum MessageSearchState {
  40. // The server did not report a meaningful state.
  41. MESSAGE_SEARCH_STATE_UNSPECIFIED = 0;
  42. // Search is disabled by the server operator.
  43. MESSAGE_SEARCH_STATE_DISABLED = 1;
  44. // Search is enabled but its provider has not established index state yet.
  45. MESSAGE_SEARCH_STATE_STARTING = 2;
  46. // The provider is rebuilding or catching up and cannot answer queries yet.
  47. MESSAGE_SEARCH_STATE_INDEXING = 3;
  48. // Search is ready to answer queries.
  49. MESSAGE_SEARCH_STATE_READY = 4;
  50. // Search can answer queries but has a known non-fatal limitation.
  51. MESSAGE_SEARCH_STATE_DEGRADED = 5;
  52. // Search is enabled but no provider can currently answer.
  53. MESSAGE_SEARCH_STATE_UNAVAILABLE = 6;
  54. }
  55. // Request to search current message bodies visible to the authenticated user.
  56. message SearchMessagesRequest {
  57. // Required query expression. Words are required terms, quoted text is an
  58. // exact phrase, and `AND` may separate terms. The query also accepts `in:`,
  59. // `from:`, `before:`, `after:`, and `has:attachment` filters; a recognized
  60. // filter may be used without a word or phrase.
  61. string query = 1 [(buf.validate.field).string = {
  62. min_len: 1
  63. max_bytes: 1024
  64. }];
  65. // Optional room-ID scope. The server intersects this room with rooms the
  66. // current user may read and with any `in:` filters in query.
  67. optional string room_id = 2 [(buf.validate.field).string = {
  68. min_len: 1
  69. max_bytes: 128
  70. }];
  71. // Optional author-ID scope. The server intersects this author with any
  72. // `from:` filters in query.
  73. optional string author_id = 3 [(buf.validate.field).string = {
  74. min_len: 1
  75. max_bytes: 128
  76. }];
  77. // Exclusive lower bound on message creation time. The stricter bound wins
  78. // when query also contains `after:`.
  79. google.protobuf.Timestamp created_after = 4;
  80. // Exclusive upper bound on message creation time. The stricter bound wins
  81. // when query also contains `before:`.
  82. google.protobuf.Timestamp created_before = 5;
  83. // When true, return only messages that currently have attachments.
  84. bool has_attachments = 6;
  85. // Defaults to relevance in both result modes. Thread activity order requires
  86. // thread grouping; newest order uses matching-message creation time.
  87. MessageSearchOrder order = 7 [(buf.validate.field).enum.defined_only = true];
  88. // Maximum results to return (messages or distinct threads). Zero uses 50; the
  89. // maximum is 100. Stale or no-longer-visible provider hits are omitted, so a
  90. // page may contain fewer messages than requested even when next_cursor is
  91. // present.
  92. uint32 page_size = 8 [(buf.validate.field).uint32.lte = 100];
  93. // Opaque cursor returned by the preceding response. It is bound to the
  94. // authenticated user and every other query field.
  95. string cursor = 9 [(buf.validate.field).string.max_bytes = 8192];
  96. // Candidate scope. Followed threads are selected before result pagination.
  97. MessageSearchScope scope = 10 [(buf.validate.field).enum.defined_only = true];
  98. // Result grouping. The server deduplicates threads before pagination.
  99. MessageSearchGroupBy group_by = 11 [(buf.validate.field).enum.defined_only = true];
  100. // Select root authors within FOLLOWED_THREADS before search and pagination.
  101. // Other scopes require UNSPECIFIED. This does not restrict reply authors;
  102. // author_id and from: still select the authors of matching messages.
  103. ThreadRootAuthorFilter root_author_filter = 12 [(buf.validate.field).enum.defined_only = true];
  104. }
  105. // One current, authorized match. With thread grouping, each result represents
  106. // one thread and message is its best match in the selected order.
  107. message MessageSearchResult {
  108. // Current renderable message that matched the query.
  109. Message message = 1;
  110. // Search-provider relevance score. Higher values are more relevant for the
  111. // same query. Clients may merge results from servers using compatible
  112. // search implementations by this value.
  113. double relevance_score = 2;
  114. // Present only with THREAD grouping. The matching message can be a reply;
  115. // the root and latest reply in this context need not match the query.
  116. ThreadSearchContext thread_context = 3;
  117. }
  118. // One ordered page of current, authorized message or thread results. Pagination reads a live
  119. // search index rather than a pinned snapshot, so results may move, repeat, or
  120. // disappear between page requests while the index advances.
  121. message SearchMessagesResponse {
  122. // Ordered matches. With THREAD grouping, one result per matching thread.
  123. // Otherwise each result is one message, without thread_context.
  124. repeated MessageSearchResult results = 1;
  125. // Opaque cursor for the next page in the selected result mode.
  126. // Empty means no more matches. Live activity can reorder thread pages.
  127. string next_cursor = 2;
  128. // Total distinct matching threads. Present only when group_by is THREAD.
  129. optional uint64 thread_total_count = 4;
  130. // Related entities for grouped results, avoiding per-row reads.
  131. RoomTimelineIncludes includes = 5;
  132. }
  133. // Current context for a grouped match. The viewer need not follow this thread.
  134. // Search evidence stays on the containing MessageSearchResult.
  135. message ThreadSearchContext {
  136. // Renderable root message, when still visible.
  137. Message root_message = 1;
  138. // Room containing this thread.
  139. RoomSummary room = 2;
  140. // Current thread metadata and viewer follow/read state.
  141. ThreadSummary thread = 3;
  142. // Most recent visible reply, when present; it need not match the query.
  143. Message latest_reply = 4;
  144. // Conversation-label participants for direct messages; empty for channels.
  145. repeated string direct_message_participant_user_ids = 5;
  146. }
  147. // Request for current message-search availability.
  148. message GetStatusRequest {}
  149. // Current message-search availability.
  150. message GetStatusResponse {
  151. reserved 2, 3;
  152. reserved "indexed_event_count", "target_event_count";
  153. // Current feature/provider state.
  154. MessageSearchState state = 1;
  155. // Suggested delay before checking again while search is not ready.
  156. google.protobuf.Duration retry_after = 4;
  157. }
  158. // Searches current message content visible to an authenticated user.
  159. service MessageSearchService {
  160. // Reports whether the operator enabled Search and whether its provider can
  161. // currently answer. This method remains available when Search is disabled.
  162. rpc GetStatus(GetStatusRequest) returns (GetStatusResponse);
  163. // Searches current message bodies in rooms where the caller is a member.
  164. // Scope and grouping are independent, including for direct messages. THREAD
  165. // grouping returns one row per matching root and its replies. Scope, grouping,
  166. // and ordering are part of the cursor scope. Unsupported combinations return
  167. // INVALID_ARGUMENT. Existing requests return individual messages by default.
  168. // Results also require message.read or a matching thread relationship with
  169. // message.read-interactions. Results are authorized and hydrated from current Chatto state
  170. // after provider matching. Returns FAILED_PRECONDITION when Search is
  171. // disabled and UNAVAILABLE when its provider is not ready.
  172. rpc SearchMessages(SearchMessagesRequest) returns (SearchMessagesResponse);
  173. }