mirror of https://github.com/vector-im/riot-web
Merge pull request #71 from matrix-org/rav/new_search_api
Use new searchRoomEvents and backPaginateRoomEventsSearch methodspull/21833/head
commit
49c1d39f93
|
@ -364,10 +364,11 @@ module.exports = React.createClass({
|
|||
if (!backwards)
|
||||
return q(false);
|
||||
|
||||
if (this.nextSearchBatch) {
|
||||
if (this.state.searchResults.next_batch) {
|
||||
debuglog("requesting more search results");
|
||||
return this._getSearchBatch(this.state.searchTerm,
|
||||
this.state.searchScope).then(true);
|
||||
var searchPromise = MatrixClientPeg.get().backPaginateRoomEventsSearch(
|
||||
this.state.searchResults);
|
||||
return this._handleSearchResult(searchPromise);
|
||||
} else {
|
||||
debuglog("no more search results");
|
||||
return q(false);
|
||||
|
@ -486,10 +487,8 @@ module.exports = React.createClass({
|
|||
this.setState({
|
||||
searchTerm: term,
|
||||
searchScope: scope,
|
||||
searchResults: [],
|
||||
searchResults: {},
|
||||
searchHighlights: [],
|
||||
searchCount: null,
|
||||
searchCanPaginate: null,
|
||||
});
|
||||
|
||||
// if we already have a search panel, we need to tell it to forget
|
||||
|
@ -498,64 +497,68 @@ module.exports = React.createClass({
|
|||
this.refs.searchResultsPanel.resetScrollState();
|
||||
}
|
||||
|
||||
this.nextSearchBatch = null;
|
||||
this._getSearchBatch(term, scope).done();
|
||||
// make sure that we don't end up showing results from
|
||||
// an aborted search by keeping a unique id.
|
||||
//
|
||||
// todo: should cancel any previous search requests.
|
||||
this.searchId = new Date().getTime();
|
||||
|
||||
var filter;
|
||||
if (scope === "Room") {
|
||||
filter = {
|
||||
// XXX: it's unintuitive that the filter for searching doesn't have the same shape as the v2 filter API :(
|
||||
rooms: [
|
||||
this.props.roomId
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
debuglog("sending search request");
|
||||
|
||||
var searchPromise = MatrixClientPeg.get().searchRoomEvents({
|
||||
filter: filter,
|
||||
term: term,
|
||||
});
|
||||
this._handleSearchResult(searchPromise).done();
|
||||
},
|
||||
|
||||
// fire off a request for a batch of search results
|
||||
_getSearchBatch: function(term, scope) {
|
||||
_handleSearchResult: function(searchPromise) {
|
||||
var self = this;
|
||||
|
||||
// keep a record of the current search id, so that if the search terms
|
||||
// change before we get a response, we can ignore the results.
|
||||
var localSearchId = this.searchId;
|
||||
|
||||
this.setState({
|
||||
searchInProgress: true,
|
||||
});
|
||||
|
||||
// make sure that we don't end up merging results from
|
||||
// different searches by keeping a unique id.
|
||||
//
|
||||
// todo: should cancel any previous search requests.
|
||||
var searchId = this.searchId = new Date().getTime();
|
||||
|
||||
var self = this;
|
||||
|
||||
debuglog("sending search request");
|
||||
return MatrixClientPeg.get().search({ body: this._getSearchCondition(term, scope),
|
||||
next_batch: this.nextSearchBatch })
|
||||
.then(function(data) {
|
||||
return searchPromise.then(function(results) {
|
||||
debuglog("search complete");
|
||||
if (!self.state.searching || self.searchId != searchId) {
|
||||
if (!self.state.searching || self.searchId != localSearchId) {
|
||||
console.error("Discarding stale search results");
|
||||
return;
|
||||
}
|
||||
|
||||
var results = data.search_categories.room_events;
|
||||
// postgres on synapse returns us precise details of the strings
|
||||
// which actually got matched for highlighting.
|
||||
//
|
||||
// In either case, we want to highlight the literal search term
|
||||
// whether it was used by the search engine or not.
|
||||
|
||||
// postgres on synapse returns us precise details of the
|
||||
// strings which actually got matched for highlighting.
|
||||
|
||||
// combine the highlight list with our existing list; build an object
|
||||
// to avoid O(N^2) fail
|
||||
var highlights = {};
|
||||
results.highlights.forEach(function(hl) { highlights[hl] = 1; });
|
||||
self.state.searchHighlights.forEach(function(hl) { highlights[hl] = 1; });
|
||||
|
||||
// turn it back into an ordered list. For overlapping highlights,
|
||||
// favour longer (more specific) terms first
|
||||
highlights = Object.keys(highlights).sort(function(a, b) { b.length - a.length });
|
||||
|
||||
// sqlite doesn't give us any highlights, so just try to highlight the literal search term
|
||||
if (highlights.length == 0) {
|
||||
highlights = [ term ];
|
||||
var highlights = results.highlights;
|
||||
if (highlights.indexOf(self.state.searchTerm) < 0) {
|
||||
highlights = highlights.concat(self.state.searchTerm);
|
||||
}
|
||||
|
||||
// append the new results to our existing results
|
||||
var events = self.state.searchResults.concat(results.results);
|
||||
// For overlapping highlights,
|
||||
// favour longer (more specific) terms first
|
||||
highlights = highlights.sort(function(a, b) { b.length - a.length });
|
||||
|
||||
self.setState({
|
||||
searchHighlights: highlights,
|
||||
searchResults: events,
|
||||
searchCount: results.count,
|
||||
searchCanPaginate: !!(results.next_batch),
|
||||
searchResults: results,
|
||||
});
|
||||
self.nextSearchBatch = results.next_batch;
|
||||
}, function(error) {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
|
@ -569,48 +572,24 @@ module.exports = React.createClass({
|
|||
});
|
||||
},
|
||||
|
||||
_getSearchCondition: function(term, scope) {
|
||||
var filter;
|
||||
|
||||
if (scope === "Room") {
|
||||
filter = {
|
||||
// XXX: it's unintuitive that the filter for searching doesn't have the same shape as the v2 filter API :(
|
||||
rooms: [
|
||||
this.props.roomId
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
search_categories: {
|
||||
room_events: {
|
||||
search_term: term,
|
||||
filter: filter,
|
||||
order_by: "recent",
|
||||
event_context: {
|
||||
before_limit: 1,
|
||||
after_limit: 1,
|
||||
include_profile: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getSearchResultTiles: function() {
|
||||
var DateSeparator = sdk.getComponent('messages.DateSeparator');
|
||||
var cli = MatrixClientPeg.get();
|
||||
|
||||
var ret = [];
|
||||
|
||||
var EventTile = sdk.getComponent('rooms.EventTile');
|
||||
var cli = MatrixClientPeg.get();
|
||||
|
||||
// XXX: todo: merge overlapping results somehow?
|
||||
// XXX: why doesn't searching on name work?
|
||||
|
||||
if (this.state.searchResults.results === undefined) {
|
||||
// awaiting results
|
||||
return [];
|
||||
}
|
||||
|
||||
if (this.state.searchCanPaginate === false) {
|
||||
if (this.state.searchResults.length == 0) {
|
||||
var ret = [];
|
||||
|
||||
if (!this.state.searchResults.next_batch) {
|
||||
if (this.state.searchResults.results.length == 0) {
|
||||
ret.push(<li key="search-top-marker">
|
||||
<h2 className="mx_RoomView_topMarker">No results</h2>
|
||||
</li>
|
||||
|
@ -625,9 +604,10 @@ module.exports = React.createClass({
|
|||
|
||||
var lastRoomId;
|
||||
|
||||
for (var i = this.state.searchResults.length - 1; i >= 0; i--) {
|
||||
var result = this.state.searchResults[i];
|
||||
var mxEv = new Matrix.MatrixEvent(result.result);
|
||||
for (var i = this.state.searchResults.results.length - 1; i >= 0; i--) {
|
||||
var result = this.state.searchResults.results[i];
|
||||
|
||||
var mxEv = result.context.getEvent();
|
||||
|
||||
if (!EventTile.haveTileForEvent(mxEv)) {
|
||||
// XXX: can this ever happen? It will make the result count
|
||||
|
@ -638,29 +618,28 @@ module.exports = React.createClass({
|
|||
var eventId = mxEv.getId();
|
||||
|
||||
if (this.state.searchScope === 'All') {
|
||||
var roomId = result.result.room_id;
|
||||
var roomId = mxEv.getRoomId();
|
||||
if(roomId != lastRoomId) {
|
||||
ret.push(<li key={eventId + "-room"}><h1>Room: { cli.getRoom(roomId).name }</h1></li>);
|
||||
lastRoomId = roomId;
|
||||
}
|
||||
}
|
||||
|
||||
var ts1 = result.result.origin_server_ts;
|
||||
var ts1 = mxEv.getTs();
|
||||
ret.push(<li key={ts1 + "-search"}><DateSeparator ts={ts1}/></li>); // Rank: {resultList[i].rank}
|
||||
|
||||
if (result.context.events_before[0]) {
|
||||
var mxEv2 = new Matrix.MatrixEvent(result.context.events_before[0]);
|
||||
if (EventTile.haveTileForEvent(mxEv2)) {
|
||||
ret.push(<li key={eventId+"-1"} data-scroll-token={eventId+"-1"}><EventTile mxEvent={mxEv2} contextual={true} /></li>);
|
||||
var timeline = result.context.getTimeline();
|
||||
for (var j = 0; j < timeline.length; j++) {
|
||||
var ev = timeline[j];
|
||||
var highlights;
|
||||
var contextual = (j != result.context.getOurEventIndex());
|
||||
if (!contextual) {
|
||||
highlights = this.state.searchHighlights;
|
||||
}
|
||||
}
|
||||
|
||||
ret.push(<li key={eventId+"+0"} data-scroll-token={eventId+"+0"}><EventTile mxEvent={mxEv} highlights={this.state.searchHighlights}/></li>);
|
||||
|
||||
if (result.context.events_after[0]) {
|
||||
var mxEv2 = new Matrix.MatrixEvent(result.context.events_after[0]);
|
||||
if (EventTile.haveTileForEvent(mxEv2)) {
|
||||
ret.push(<li key={eventId+"+1"} data-scroll-token={eventId+"+1"}><EventTile mxEvent={mxEv2} contextual={true} /></li>);
|
||||
if (EventTile.haveTileForEvent(ev)) {
|
||||
ret.push(<li key={eventId+"+"+j} data-scroll-token={eventId+"+"+j}>
|
||||
<EventTile mxEvent={ev} contextual={contextual} highlights={highlights} />
|
||||
</li>);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1292,7 +1271,7 @@ module.exports = React.createClass({
|
|||
searchInfo = {
|
||||
searchTerm : this.state.searchTerm,
|
||||
searchScope : this.state.searchScope,
|
||||
searchCount : this.state.searchCount,
|
||||
searchCount : this.state.searchResults.count,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -109,8 +109,8 @@ module.exports = React.createClass({
|
|||
|
||||
var searchStatus;
|
||||
// don't display the search count until the search completes and
|
||||
// gives us a non-null searchCount.
|
||||
if (this.props.searchInfo && this.props.searchInfo.searchCount !== null) {
|
||||
// gives us a valid (possibly zero) searchCount.
|
||||
if (this.props.searchInfo && this.props.searchInfo.searchCount !== undefined && this.props.searchInfo.searchCount !== null) {
|
||||
searchStatus = <div className="mx_RoomHeader_searchStatus"> (~{ this.props.searchInfo.searchCount } results)</div>;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue