Skip to content

Commit a70cf76

Browse files
committed
feature: add average search rating graph
1 parent e649a7d commit a70cf76

11 files changed

Lines changed: 235 additions & 15 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { useQuery } from "@tanstack/react-query";
2+
import { useTrieve } from "app/context/trieveContext";
3+
import { RAGAnalyticsFilter } from "trieve-ts-sdk";
4+
import { Granularity } from "trieve-ts-sdk";
5+
import { GraphComponent } from "../GraphComponent";
6+
import { searchAverageRatingQuery } from "app/queries/analytics/search";
7+
8+
export const SearchAverageRating = ({
9+
filters,
10+
granularity,
11+
}: {
12+
filters: RAGAnalyticsFilter;
13+
granularity: Granularity;
14+
}) => {
15+
const { trieve } = useTrieve();
16+
const { data, isLoading } = useQuery(
17+
searchAverageRatingQuery(trieve, filters, granularity),
18+
);
19+
20+
return (
21+
<GraphComponent
22+
loading={isLoading}
23+
topLevelMetric={data?.avg_search_rating}
24+
graphData={data?.points}
25+
granularity={granularity}
26+
date_range={filters.date_range}
27+
xAxis={"time_stamp"}
28+
yAxis={"point"}
29+
label="Average Search Rating"
30+
tooltipContent="The average rating that users give to the search."
31+
/>
32+
);
33+
};

clients/trieve-shopify-extension/app/queries/analytics/search.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
CTRMetricsOverTimeResponse,
1212
SearchConversionRateResponse,
1313
SearchesPerUserResponse,
14+
SearchAverageRatingResponse,
1415
} from "trieve-ts-sdk";
1516
import { subDays } from "date-fns";
1617
import { formatDateForApi } from "../../utils/formatting";
@@ -152,3 +153,21 @@ export const searchesPerUserQuery = (
152153
},
153154
} satisfies QueryOptions;
154155
};
156+
157+
export const searchAverageRatingQuery = (
158+
trieve: TrieveSDK,
159+
filters: SearchAnalyticsFilter,
160+
granularity: Granularity,
161+
) => {
162+
return {
163+
queryKey: ["search_average_rating", filters, granularity],
164+
queryFn: async () => {
165+
const result = await trieve.getSearchAnalytics({
166+
filter: filters,
167+
type: "search_average_rating",
168+
granularity,
169+
});
170+
return result as SearchAverageRatingResponse;
171+
},
172+
} satisfies QueryOptions;
173+
};

clients/trieve-shopify-extension/app/routes/app._dashboard.search.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { Granularity } from "trieve-ts-sdk";
1010
import { AllSearchesTable } from "app/components/analytics/search/AllSearchesTable";
1111
import { SearchCTRChart } from "app/components/analytics/search/SearchCTR";
1212
import { SearchesPerUser } from "app/components/analytics/search/SearchesPerUser";
13+
import { SearchAverageRating } from "app/components/analytics/search/SearchAverageRating";
14+
1315
export default function SearchAnalyticsPage() {
1416
const [filters, setFilters] = useState(defaultSearchAnalyticsFilter);
1517
const [granularity, setGranularity] = useState<Granularity>("day");
@@ -46,8 +48,12 @@ export default function SearchAnalyticsPage() {
4648
<div className="flex flex-col gap-4">
4749
<SearchUsageChart filters={filters} granularity={granularity} />
4850
<SearchConversionRate
49-
filters={filters}
50-
granularity={granularity}
51+
filters={filters}
52+
granularity={granularity}
53+
/>
54+
<SearchAverageRating
55+
filters={filters}
56+
granularity={granularity}
5157
/>
5258
<NoResultQueriesTable filters={filters} />
5359
</div>
@@ -56,8 +62,8 @@ export default function SearchAnalyticsPage() {
5662
<div className="flex flex-col gap-4">
5763
<SearchCTRChart filters={filters} granularity={granularity} />
5864
<SearchesPerUser
59-
filters={filters}
60-
granularity={granularity}
65+
filters={filters}
66+
granularity={granularity}
6167
/>
6268
<HeadQueriesTable filters={filters} />
6369
</div>

clients/trieve-shopify-extension/yarn.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8198,7 +8198,7 @@ tr46@~0.0.3:
81988198
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
81998199

82008200
trieve-ts-sdk@../ts-sdk:
8201-
version "0.0.64"
8201+
version "0.0.67"
82028202

82038203
trim-lines@^3.0.0:
82048204
version "3.0.1"

clients/ts-sdk/openapi.json

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16519,6 +16519,37 @@
1651916519
]
1652016520
}
1652116521
}
16522+
},
16523+
{
16524+
"type": "object",
16525+
"title": "SearchAverageRating",
16526+
"required": [
16527+
"type"
16528+
],
16529+
"properties": {
16530+
"filter": {
16531+
"allOf": [
16532+
{
16533+
"$ref": "#/components/schemas/SearchAnalyticsFilter"
16534+
}
16535+
],
16536+
"nullable": true
16537+
},
16538+
"granularity": {
16539+
"allOf": [
16540+
{
16541+
"$ref": "#/components/schemas/Granularity"
16542+
}
16543+
],
16544+
"nullable": true
16545+
},
16546+
"type": {
16547+
"type": "string",
16548+
"enum": [
16549+
"search_average_rating"
16550+
]
16551+
}
16552+
}
1652216553
}
1652316554
],
1652416555
"discriminator": {
@@ -16618,9 +16649,31 @@
1661816649
},
1661916650
{
1662016651
"$ref": "#/components/schemas/SearchesPerUserResponse"
16652+
},
16653+
{
16654+
"$ref": "#/components/schemas/SearchAverageRatingResponse"
1662116655
}
1662216656
]
1662316657
},
16658+
"SearchAverageRatingResponse": {
16659+
"type": "object",
16660+
"required": [
16661+
"avg_search_rating",
16662+
"points"
16663+
],
16664+
"properties": {
16665+
"avg_search_rating": {
16666+
"type": "number",
16667+
"format": "double"
16668+
},
16669+
"points": {
16670+
"type": "array",
16671+
"items": {
16672+
"$ref": "#/components/schemas/FloatTimePoint"
16673+
}
16674+
}
16675+
}
16676+
},
1662416677
"SearchCTRMetrics": {
1662516678
"type": "object",
1662616679
"title": "Search CTR Metrics",

clients/ts-sdk/src/types.gen.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3068,6 +3068,10 @@ export type SearchAnalytics = {
30683068
filter?: ((SearchAnalyticsFilter) | null);
30693069
granularity?: ((Granularity) | null);
30703070
type: 'searches_per_user';
3071+
} | {
3072+
filter?: ((SearchAnalyticsFilter) | null);
3073+
granularity?: ((Granularity) | null);
3074+
type: 'search_average_rating';
30713075
};
30723076

30733077
export type type7 = 'latency_graph';
@@ -3082,7 +3086,12 @@ export type SearchAnalyticsFilter = {
30823086
top_score?: ((FloatRange) | null);
30833087
};
30843088

3085-
export type SearchAnalyticsResponse = LatencyGraphResponse | SearchUsageGraphResponse | DatasetAnalytics | HeadQueryResponse | SearchQueryResponse | QueryCountResponse | SearchQueryEvent | PopularFiltersResponse | CTRMetricsOverTimeResponse | SearchConversionRateResponse | SearchesPerUserResponse;
3089+
export type SearchAnalyticsResponse = LatencyGraphResponse | SearchUsageGraphResponse | DatasetAnalytics | HeadQueryResponse | SearchQueryResponse | QueryCountResponse | SearchQueryEvent | PopularFiltersResponse | CTRMetricsOverTimeResponse | SearchConversionRateResponse | SearchesPerUserResponse | SearchAverageRatingResponse;
3090+
3091+
export type SearchAverageRatingResponse = {
3092+
avg_search_rating: number;
3093+
points: Array<FloatTimePoint>;
3094+
};
30863095

30873096
export type SearchCTRMetrics = {
30883097
avg_position_of_click: number;

server/src/data/models.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7250,6 +7250,11 @@ pub enum SearchAnalytics {
72507250
filter: Option<SearchAnalyticsFilter>,
72517251
granularity: Option<Granularity>,
72527252
},
7253+
#[schema(title = "SearchAverageRating")]
7254+
SearchAverageRating {
7255+
filter: Option<SearchAnalyticsFilter>,
7256+
granularity: Option<Granularity>,
7257+
},
72537258
}
72547259

72557260
#[derive(Debug, Serialize, Deserialize, ToSchema)]
@@ -7511,6 +7516,12 @@ pub struct SearchesPerUserResponse {
75117516
pub points: Vec<FloatTimePoint>,
75127517
}
75137518

7519+
#[derive(Debug, Row, Serialize, Deserialize, ToSchema)]
7520+
pub struct SearchAverageRatingResponse {
7521+
pub avg_search_rating: f64,
7522+
pub points: Vec<FloatTimePoint>,
7523+
}
7524+
75147525
#[derive(Debug, Row, Serialize, Deserialize, ToSchema)]
75157526
pub struct TopicQueriesResponse {
75167527
pub topics: Vec<TopicAnalyticsSummary>,
@@ -7605,6 +7616,8 @@ pub enum SearchAnalyticsResponse {
76057616
SearchConversionRate(SearchConversionRateResponse),
76067617
#[schema(title = "SearchesPerUser")]
76077618
SearchesPerUser(SearchesPerUserResponse),
7619+
#[schema(title = "SearchAverageRating")]
7620+
SearchAverageRating(SearchAverageRatingResponse)
76087621
}
76097622

76107623
#[derive(Debug, Serialize, Deserialize, ToSchema)]
@@ -7616,6 +7629,7 @@ pub enum ClusterAnalyticsResponse {
76167629
ClusterQueries(SearchQueryResponse),
76177630
}
76187631

7632+
76197633
#[derive(Debug, Serialize, Deserialize, ToSchema)]
76207634
#[serde(untagged)]
76217635
pub enum RecommendationAnalyticsResponse {

server/src/handlers/analytics_handler.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,19 @@ pub async fn get_search_analytics(
344344
.await?;
345345
SearchAnalyticsResponse::SearchesPerUser(searches_per_user)
346346
}
347+
SearchAnalytics::SearchAverageRating {
348+
filter,
349+
granularity,
350+
} => {
351+
let search_average_rating = get_search_average_rating_query(
352+
dataset_org_plan_sub.dataset.id,
353+
filter,
354+
granularity,
355+
clickhouse_client.get_ref(),
356+
)
357+
.await?;
358+
SearchAnalyticsResponse::SearchAverageRating(search_average_rating)
359+
}
347360
};
348361

349362
Ok(HttpResponse::Ok().json(response))

server/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,7 @@ impl Modify for SecurityAddon {
596596
data::models::MessagesPerUserResponse,
597597
data::models::SearchesPerUserResponse,
598598
data::models::ChatAverageRatingResponse,
599+
data::models::SearchAverageRatingResponse,
599600
errors::ErrorResponseBody,
600601
middleware::api_version::APIVersion,
601602
)

server/src/operators/analytics_operator.rs

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,16 @@ use crate::{
1313
RecommendationsConversionRateResponse, RecommendationsPerUserResponse,
1414
RecommendationsWithClicksCTRResponse, RecommendationsWithClicksCTRResponseClickhouse,
1515
RecommendationsWithoutClicksCTRResponse, RecommendationsWithoutClicksCTRResponseClickhouse,
16-
SearchAnalyticsFilter, SearchCTRMetrics, SearchCTRMetricsClickhouse, SearchClusterTopics,
17-
SearchConversionRateResponse, SearchQueriesWithClicksCTRResponse,
18-
SearchQueriesWithClicksCTRResponseClickhouse, SearchQueriesWithoutClicksCTRResponse,
19-
SearchQueriesWithoutClicksCTRResponseClickhouse, SearchQueryEvent,
20-
SearchQueryEventClickhouse, SearchSortBy, SearchTypeCount, SearchesPerUserResponse,
21-
SortOrder, TopComponents, TopComponentsResponse, TopDatasetsResponse,
22-
TopDatasetsResponseClickhouse, TopPages, TopPagesResponse, TopicAnalyticsFilter,
23-
TopicAnalyticsSummaryClickhouse, TopicDetailsResponse, TopicQueriesResponse,
24-
TopicQueryClickhouse, TopicsOverTimeResponse, TotalUniqueUsersResponse,
16+
SearchAnalyticsFilter, SearchAverageRatingResponse, SearchCTRMetrics,
17+
SearchCTRMetricsClickhouse, SearchClusterTopics, SearchConversionRateResponse,
18+
SearchQueriesWithClicksCTRResponse, SearchQueriesWithClicksCTRResponseClickhouse,
19+
SearchQueriesWithoutClicksCTRResponse, SearchQueriesWithoutClicksCTRResponseClickhouse,
20+
SearchQueryEvent, SearchQueryEventClickhouse, SearchSortBy, SearchTypeCount,
21+
SearchesPerUserResponse, SortOrder, TopComponents, TopComponentsResponse,
22+
TopDatasetsResponse, TopDatasetsResponseClickhouse, TopPages, TopPagesResponse,
23+
TopicAnalyticsFilter, TopicAnalyticsSummaryClickhouse, TopicDetailsResponse,
24+
TopicQueriesResponse, TopicQueryClickhouse, TopicsOverTimeResponse,
25+
TotalUniqueUsersResponse,
2526
},
2627
errors::ServiceError,
2728
handlers::analytics_handler::GetTopDatasetsRequestBody,
@@ -2697,3 +2698,67 @@ pub async fn get_chat_average_rating_query(
26972698
points: chat_average_rating.into_iter().map(|x| x.into()).collect(),
26982699
})
26992700
}
2701+
2702+
pub async fn get_search_average_rating_query(
2703+
dataset_id: uuid::Uuid,
2704+
filter: Option<SearchAnalyticsFilter>,
2705+
granularity: Option<Granularity>,
2706+
clickhouse_client: &clickhouse::Client,
2707+
) -> Result<SearchAverageRatingResponse, ServiceError> {
2708+
let interval = match granularity {
2709+
Some(Granularity::Second) => "1 SECOND",
2710+
Some(Granularity::Minute) => "1 MINUTE",
2711+
Some(Granularity::Hour) => "1 HOUR",
2712+
Some(Granularity::Day) => "1 DAY",
2713+
Some(Granularity::Month) => "1 MONTH",
2714+
None => "1 HOUR",
2715+
};
2716+
2717+
let mut query_string = format!(
2718+
"SELECT
2719+
CAST(toStartOfInterval(created_at, INTERVAL {}) AS DateTime) AS time_stamp,
2720+
avg(JSONExtract(search_queries.query_rating, 'rating', 'Float64')) as avg_rating
2721+
FROM search_queries
2722+
WHERE dataset_id = ? AND search_queries.query_rating != ''
2723+
",
2724+
interval,
2725+
);
2726+
2727+
if let Some(filter_params) = &filter {
2728+
query_string = filter_params.add_to_query(query_string);
2729+
}
2730+
2731+
query_string.push_str(
2732+
"
2733+
GROUP BY
2734+
time_stamp
2735+
ORDER BY
2736+
time_stamp
2737+
LIMIT 1000",
2738+
);
2739+
2740+
let search_average_rating = clickhouse_client
2741+
.query(query_string.as_str())
2742+
.bind(dataset_id)
2743+
.fetch_all::<FloatTimePointClickhouse>()
2744+
.await
2745+
.map_err(|e| {
2746+
log::error!("Error fetching search average rating: {:?}", e);
2747+
ServiceError::InternalServerError("Error fetching search average rating".to_string())
2748+
})?;
2749+
2750+
let avg_search_rating = if !search_average_rating.is_empty() {
2751+
search_average_rating.iter().map(|x| x.point).sum::<f64>()
2752+
/ search_average_rating.len() as f64
2753+
} else {
2754+
0.0
2755+
};
2756+
2757+
Ok(SearchAverageRatingResponse {
2758+
avg_search_rating,
2759+
points: search_average_rating
2760+
.into_iter()
2761+
.map(|x| x.into())
2762+
.collect(),
2763+
})
2764+
}

0 commit comments

Comments
 (0)