send recsys powered-by #6875

Merged
jessopb merged 4 commits from recsys-sendPoweredBy into master 2021-08-17 16:03:25 +02:00
9 changed files with 72 additions and 29 deletions

View file

@ -28,7 +28,7 @@ declare type SearchOptions = {
declare type SearchState = {
options: SearchOptions,
urisByQuery: {},
resultsByQuery: {},
hasReachedMaxResultsLength: {},
searching: boolean,
};
@ -40,6 +40,7 @@ declare type SearchSuccess = {
from: number,
size: number,
uris: Array<string>,
recsys: string,
},
};

View file

@ -81,7 +81,7 @@ function ChannelContent(props: Props) {
!showMature ? '&nsfw=false&size=50&from=0' : ''
}`
)
.then((results) => {
.then(({ body: results }) => {
const urls = results.map(({ name, claimId }) => {
return `lbry://${name}#${claimId}`;
});

View file

@ -6,6 +6,7 @@ import {
makeSelectRecommendedClaimIds,
makeSelectRecommendationClicks,
} from 'redux/selectors/content';
import { makeSelectRecommendedRecsysIdForClaimId } from 'redux/selectors/search';
const VERSION = '0.0.1';
@ -36,7 +37,7 @@ function createRecsys(claimId, userId, events, loadedAt, isEmbed) {
claimId: claimId,
pageLoadedAt: pageLoadedAt,
pageExitedAt: pageExitedAt,
recsysId: recsysId,
recsysId: makeSelectRecommendedRecsysIdForClaimId(claimId)(state) || recsysId,
recClaimIds: makeSelectRecommendedClaimIds(claimId)(state),
recClickedVideoIdx: makeSelectRecommendationClicks(claimId)(state),
events: events,

View file

@ -25,7 +25,7 @@ export default function useLighthouse(
let isSubscribed = true;
lighthouse
.search(throttledQuery)
.then((results) => {
.then(({ body: results }) => {
if (isSubscribed) {
setResults(
results.map((result) => `lbry://${result.name}#${result.claimId}`).filter((uri) => isURIValid(uri))

View file

@ -3,7 +3,7 @@ import { withRouter } from 'react-router';
import { doSearch } from 'redux/actions/search';
import {
selectIsSearching,
makeSelectSearchUris,
makeSelectSearchUrisForQuery,
selectSearchOptions,
makeSelectHasReachedMaxResultsLength,
} from 'redux/selectors/search';
@ -28,7 +28,7 @@ const select = (state, props) => {
};
const query = getSearchQueryString(urlQuery, searchOptions);
const uris = makeSelectSearchUris(query)(state);
const uris = makeSelectSearchUrisForQuery(query)(state);
const hasReachedMaxResultsLength = makeSelectHasReachedMaxResultsLength(query)(state);
return {

View file

@ -2,7 +2,7 @@
import * as ACTIONS from 'constants/action_types';
import { SEARCH_OPTIONS } from 'constants/search';
import { buildURI, doResolveUris, batchActions, isURIValid, makeSelectClaimForUri } from 'lbry-redux';
import { makeSelectSearchUris, selectSearchValue } from 'redux/selectors/search';
import { makeSelectSearchUrisForQuery, selectSearchValue } from 'redux/selectors/search';
import handleFetchResponse from 'util/handle-fetch';
import { getSearchQueryString } from 'util/query-params';
import { SIMPLE_SITE, SEARCH_SERVER_API } from 'config';
@ -48,7 +48,7 @@ export const doSearch = (rawQuery: string, searchOptions: SearchOptions) => (
const from = searchOptions.from;
// If we have already searched for something, we don't need to do anything
const urisForQuery = makeSelectSearchUris(queryWithOptions)(state);
const urisForQuery = makeSelectSearchUrisForQuery(queryWithOptions)(state);
if (urisForQuery && !!urisForQuery.length) {
if (!size || !from || from + size < urisForQuery.length) {
return;
@ -61,13 +61,14 @@ export const doSearch = (rawQuery: string, searchOptions: SearchOptions) => (
lighthouse
.search(queryWithOptions)
.then((data: Array<{ name: string, claimId: string }>) => {
.then((data: { body: Array<{ name: string, claimId: string }>, poweredBy: string }) => {
const { body: result, poweredBy } = data;
const uris = [];
const actions = [];
data.forEach((result) => {
if (result) {
const { name, claimId } = result;
result.forEach((item) => {
if (item) {
const { name, claimId } = item;
const urlObj: LbryUrlObj = {};
if (name.startsWith('@')) {
@ -94,6 +95,7 @@ export const doSearch = (rawQuery: string, searchOptions: SearchOptions) => (
from: from,
size: size,
uris,
recsys: poweredBy,
},
});
dispatch(batchActions(...actions));

View file

@ -17,7 +17,7 @@ const defaultState: SearchState = {
[SEARCH_OPTIONS.MEDIA_IMAGE]: defaultSearchTypes.includes(SEARCH_OPTIONS.MEDIA_IMAGE),
[SEARCH_OPTIONS.MEDIA_APPLICATION]: defaultSearchTypes.includes(SEARCH_OPTIONS.MEDIA_APPLICATION),
},
urisByQuery: {},
resultsByQuery: {},
hasReachedMaxResultsLength: {},
searching: false,
};
@ -29,21 +29,23 @@ export default handleActions(
searching: true,
}),
[ACTIONS.SEARCH_SUCCESS]: (state: SearchState, action: SearchSuccess): SearchState => {
const { query, uris, from, size } = action.data;
const { query, uris, from, size, recsys } = action.data;
const normalizedQuery = createNormalizedSearchKey(query);
const urisForQuery = state.resultsByQuery[normalizedQuery] && state.resultsByQuery[normalizedQuery]['uris'];
let newUris = uris;
if (from !== 0 && state.urisByQuery[normalizedQuery]) {
newUris = Array.from(new Set(state.urisByQuery[normalizedQuery].concat(uris)));
if (from !== 0 && urisForQuery) {
newUris = Array.from(new Set(urisForQuery.concat(uris)));
}
// The returned number of urls is less than the page size, so we're on the last page
const noMoreResults = size && uris.length < size;
const results = { uris: newUris, recsys };
return {
...state,
searching: false,
urisByQuery: Object.assign({}, state.urisByQuery, { [normalizedQuery]: newUris }),
resultsByQuery: Object.assign({}, state.resultsByQuery, { [normalizedQuery]: results }),
hasReachedMaxResultsLength: Object.assign({}, state.hasReachedMaxResultsLength, {
[normalizedQuery]: noMoreResults,
}),

View file

@ -4,6 +4,7 @@ import { selectShowMatureContent } from 'redux/selectors/settings';
import {
parseURI,
makeSelectClaimForUri,
makeSelectClaimForClaimId,
makeSelectClaimIsNsfw,
buildURI,
isClaimNsfw,
@ -26,9 +27,9 @@ export const selectSearchOptions: (state: State) => SearchOptions = createSelect
export const selectIsSearching: (state: State) => boolean = createSelector(selectState, (state) => state.searching);
infinite-persistence commented 2021-08-13 04:33:45 +02:00 (Migrated from github.com)
Review

Maybe a rename would be good to reflect the actual returned object.

Maybe a rename would be good to reflect the actual returned object.
export const selectSearchUrisByQuery: (state: State) => { [string]: Array<string> } = createSelector(
export const selectSearchResultByQuery: (state: State) => { [string]: Array<string> } = createSelector(
selectState,
(state) => state.urisByQuery
(state) => state.resultsByQuery
);
export const selectHasReachedMaxResultsLength: (state: State) => { [boolean]: Array<boolean> } = createSelector(
@ -36,15 +37,15 @@ export const selectHasReachedMaxResultsLength: (state: State) => { [boolean]: Ar
(state) => state.hasReachedMaxResultsLength
);
export const makeSelectSearchUris = (query: string): ((state: State) => Array<string>) =>
export const makeSelectSearchUrisForQuery = (query: string): ((state: State) => Array<string>) =>
// replace statement below is kind of ugly, and repeated in doSearch action
createSelector(selectSearchUrisByQuery, (byQuery) => {
createSelector(selectSearchResultByQuery, (byQuery) => {
if (query) {
query = query.replace(/^lbry:\/\//i, '').replace(/\//, ' ');
const normalizedQuery = createNormalizedSearchKey(query);
return byQuery[normalizedQuery];
return byQuery[normalizedQuery] && byQuery[normalizedQuery]['uris'];
}
return byQuery[query];
return byQuery[query] && byQuery[query]['uris'];
});
export const makeSelectHasReachedMaxResultsLength = (query: string): ((state: State) => boolean) =>
@ -60,7 +61,7 @@ export const makeSelectHasReachedMaxResultsLength = (query: string): ((state: St
export const makeSelectRecommendedContentForUri = (uri: string) =>
createSelector(
makeSelectClaimForUri(uri),
selectSearchUrisByQuery,
selectSearchResultByQuery,
makeSelectClaimIsNsfw(uri),
(claim, searchUrisByQuery, isMature) => {
let recommendedContent;
@ -84,16 +85,47 @@ export const makeSelectRecommendedContentForUri = (uri: string) =>
const searchQuery = getSearchQueryString(title.replace(/\//, ' '), options);
const normalizedSearchQuery = createNormalizedSearchKey(searchQuery);
let searchUris = searchUrisByQuery[normalizedSearchQuery];
if (searchUris) {
searchUris = searchUris.filter((searchUri) => searchUri !== currentUri);
recommendedContent = searchUris;
let searchResult = searchUrisByQuery[normalizedSearchQuery];
if (searchResult) {
recommendedContent = searchResult['uris'].filter((searchUri) => searchUri !== currentUri);
}
}
return recommendedContent;
}
);
export const makeSelectRecommendedRecsysIdForClaimId = (claimId: string) =>
createSelector(makeSelectClaimForClaimId(claimId), selectSearchResultByQuery, (claim, searchUrisByQuery) => {
// TODO: DRY this out.
let poweredBy;
if (claim) {
const isMature = isClaimNsfw(claim);
const { title } = claim.value;
if (!title) {
return;
}
const options: {
related_to?: string,
nsfw?: boolean,
isBackgroundSearch?: boolean,
} = { related_to: claim.claim_id, isBackgroundSearch: true };
options['nsfw'] = isMature;
const searchQuery = getSearchQueryString(title.replace(/\//, ' '), options);
const normalizedSearchQuery = createNormalizedSearchKey(searchQuery);
let searchResult = searchUrisByQuery[normalizedSearchQuery];
if (searchResult) {
poweredBy = searchResult.recsys;
} else {
return normalizedSearchQuery;
}
}
return poweredBy;
});
export const makeSelectWinningUriForQuery = (query: string) => {
const uriFromQuery = `lbry://${query}`;

View file

@ -1,4 +1,9 @@
// @flow
export default function handleFetchResponse(response: Response): Promise<any> {
return response.status === 200 ? Promise.resolve(response.json()) : Promise.reject(new Error(response.statusText));
const headers = response.headers;
const poweredBy = headers.get('x-powered-by');
return response.status === 200
? response.json().then((body) => ({ body, poweredBy }))
: Promise.reject(new Error(response.statusText));
}