summaryrefslogtreecommitdiff
path: root/src/domain
diff options
context:
space:
mode:
Diffstat (limited to 'src/domain')
-rw-r--r--src/domain/search.c194
-rw-r--r--src/domain/search.h14
2 files changed, 137 insertions, 71 deletions
diff --git a/src/domain/search.c b/src/domain/search.c
index 10109a7..faf16ff 100644
--- a/src/domain/search.c
+++ b/src/domain/search.c
@@ -556,104 +556,158 @@ tiny_search_index_candidates(const tiny_search_index_t *index,
return true;
}
-double
-tiny_search_score(const char *query, const tiny_message_t *message)
+static bool
+points_contain(const uint32_t *haystack,
+ size_t haystack_count,
+ const uint32_t *needle,
+ size_t width)
{
- uint32_t *query_points = NULL, *message_points = NULL;
- size_t query_count = 0, message_count = 0, width, query_offset, message_offset, matches = 0,
- total, tag;
- double score = 0.0;
- if (query == NULL || message == NULL || message->content == NULL
- || (message->metadata.tag_count != 0 && message->metadata.tags == NULL))
- {
- return 0.0;
- }
- if (!decode_codepoints(query, &query_points, &query_count) || query_count == 0)
+ size_t offset;
+ for (offset = 0; offset + width <= haystack_count; ++offset)
{
- free(query_points);
- return 0.0;
- }
- if (!decode_codepoints(message->content, &message_points, &message_count))
- {
- free(query_points);
- return 0.0;
+ if (memcmp(haystack + offset, needle, width * sizeof(uint32_t)) == 0)
+ {
+ return true;
+ }
}
+ return false;
+}
+
+static bool
+score_points_breakdown(const uint32_t *query_points,
+ size_t query_count,
+ const uint32_t *content_points,
+ size_t content_count,
+ const tiny_message_t *message,
+ tiny_search_breakdown_t *breakdown)
+{
+ size_t width, query_offset, total;
+ bool *content_hits = NULL, *summary_hits = NULL, *tag_hits = NULL;
+ double exact_bonus = 0.0;
width = query_count >= 3 ? 3 : (query_count >= 2 ? 2 : 1);
total = query_count - width + 1;
+ content_hits = calloc(total, sizeof(bool));
+ summary_hits = calloc(total, sizeof(bool));
+ tag_hits = calloc(total, sizeof(bool));
+ if (!content_hits || !summary_hits || !tag_hits)
+ {
+ free(content_hits);
+ free(summary_hits);
+ free(tag_hits);
+ return false;
+ }
for (query_offset = 0; query_offset < total; ++query_offset)
{
- bool found = false;
- for (message_offset = 0; message_offset + width <= message_count; ++message_offset)
+ if (points_contain(content_points, content_count, query_points + query_offset, width))
{
- if (memcmp(query_points + query_offset,
- message_points + message_offset,
- width * sizeof(uint32_t))
- == 0)
- {
- found = true;
- break;
- }
+ content_hits[query_offset] = true;
}
- for (tag = 0; tag < message->metadata.tag_count && !found; ++tag)
+ else
{
- uint32_t *tag_points = NULL;
- size_t tag_count = 0;
- if (!decode_codepoints(message->metadata.tags[tag], &tag_points, &tag_count))
+ size_t tag;
+ for (tag = 0; tag < message->metadata.tag_count; ++tag)
{
- continue;
- }
- for (message_offset = 0; message_offset + width <= tag_count; ++message_offset)
- {
- if (memcmp(query_points + query_offset,
- tag_points + message_offset,
- width * sizeof(uint32_t))
- == 0)
+ uint32_t *tag_points = NULL;
+ size_t tag_count = 0;
+ if (!decode_codepoints(message->metadata.tags[tag], &tag_points, &tag_count))
+ {
+ continue;
+ }
+ if (points_contain(tag_points, tag_count, query_points + query_offset, width))
{
- found = true;
+ tag_hits[query_offset] = true;
+ free(tag_points);
break;
}
+ free(tag_points);
}
- free(tag_points);
- }
- if (!found && message->metadata.summary)
- {
- uint32_t *summary_points = NULL;
- size_t summary_count = 0;
- if (decode_codepoints(message->metadata.summary, &summary_points, &summary_count))
+ if (!tag_hits[query_offset] && message->metadata.summary)
{
- for (message_offset = 0; message_offset + width <= summary_count; ++message_offset)
+ uint32_t *summary_points = NULL;
+ size_t summary_count = 0;
+ if (decode_codepoints(message->metadata.summary, &summary_points, &summary_count)
+ && points_contain(
+ summary_points, summary_count, query_points + query_offset, width))
{
- if (memcmp(query_points + query_offset,
- summary_points + message_offset,
- width * sizeof(uint32_t))
- == 0)
- {
- found = true;
- break;
- }
+ summary_hits[query_offset] = true;
}
+ free(summary_points);
}
- free(summary_points);
- }
- if (found)
- {
- matches++;
}
}
- score = (double)matches / (double)total;
- if (query_count <= message_count)
+ if (query_count <= content_count)
{
- for (message_offset = 0; message_offset + query_count <= message_count; ++message_offset)
+ for (query_offset = 0; query_offset + query_count <= content_count; ++query_offset)
{
- if (memcmp(query_points, message_points + message_offset, query_count * sizeof(uint32_t))
+ if (memcmp(query_points, content_points + query_offset, query_count * sizeof(uint32_t))
== 0)
{
- score += 1.0;
+ exact_bonus = 1.0;
break;
}
}
}
+ {
+ size_t total_matches = 0, content_matches = 0, summary_matches = 0, tag_matches = 0;
+ for (query_offset = 0; query_offset < total; ++query_offset)
+ {
+ bool any
+ = content_hits[query_offset] || summary_hits[query_offset] || tag_hits[query_offset];
+ total_matches += any ? 1 : 0;
+ content_matches += content_hits[query_offset] ? 1 : 0;
+ summary_matches += summary_hits[query_offset] ? 1 : 0;
+ tag_matches += tag_hits[query_offset] ? 1 : 0;
+ }
+ breakdown->total = total == 0 ? 0.0 : ((double)total_matches / (double)total) + exact_bonus;
+ breakdown->content = total == 0 ? 0.0 : ((double)content_matches / (double)total) + exact_bonus;
+ breakdown->summary = total == 0 ? 0.0 : (double)summary_matches / (double)total;
+ breakdown->tags = total == 0 ? 0.0 : (double)tag_matches / (double)total;
+ }
+ free(content_hits);
+ free(summary_hits);
+ free(tag_hits);
+ return true;
+}
+
+bool
+tiny_search_score_breakdown(const char *query,
+ const tiny_message_t *message,
+ tiny_search_breakdown_t *breakdown)
+{
+ uint32_t *query_points = NULL, *content_points = NULL;
+ size_t query_count = 0, content_count = 0;
+ bool result;
+ if (!breakdown)
+ {
+ return false;
+ }
+ memset(breakdown, 0, sizeof(*breakdown));
+ if (query == NULL || message == NULL || message->content == NULL
+ || (message->metadata.tag_count != 0 && message->metadata.tags == NULL))
+ {
+ return true;
+ }
+ if (!decode_codepoints(query, &query_points, &query_count) || query_count == 0
+ || !decode_codepoints(message->content, &content_points, &content_count))
+ {
+ free(query_points);
+ free(content_points);
+ return false;
+ }
+ result = score_points_breakdown(
+ query_points, query_count, content_points, content_count, message, breakdown);
free(query_points);
- free(message_points);
- return score;
+ free(content_points);
+ return result;
}
+
+double
+tiny_search_score(const char *query, const tiny_message_t *message)
+{
+ tiny_search_breakdown_t breakdown;
+ if (!tiny_search_score_breakdown(query, message, &breakdown))
+ {
+ return 0.0;
+ }
+ return breakdown.total;
+} \ No newline at end of file
diff --git a/src/domain/search.h b/src/domain/search.h
index 3408998..a891a27 100644
--- a/src/domain/search.h
+++ b/src/domain/search.h
@@ -15,8 +15,20 @@ bool tiny_search_index_add(tiny_search_index_t *index,
size_t message_index);
bool tiny_search_index_candidates(const tiny_search_index_t *index,
const char *query,
- size_t **message_indices,
+ size_t **output,
size_t *count);
+
+typedef struct tiny_search_breakdown
+{
+ double total;
+ double content;
+ double summary;
+ double tags;
+} tiny_search_breakdown_t;
+
double tiny_search_score(const char *query, const tiny_message_t *message);
+bool tiny_search_score_breakdown(const char *query,
+ const tiny_message_t *message,
+ tiny_search_breakdown_t *breakdown);
#endif