Re: New Threading Mode
Urs Janßen <[email protected]>
| Newsgroups | gmane.network.tin.devel |
|---|---|
| Message-ID | <[email protected]> |
[note: fullquote as original article was rejected (non-member submission); updated patch at the end] > I have attached a patch which adds a new threading mode. I call this > threading mode "Percentage Match". It simply threads together all the > articles which have a subject which is similar enough to each other. The > level of similarity is decided based upon a character by character check > and is configurable. > This threading mode is useful in groups where the Multipart > threading mode doesn't work due to posters following a different naming > convention or where the articles obviously go together, as in image sets, > but have different filename embedded within the subject. > I find that this threading mode works quite well in large groups, > especially where the archive contains more than one file spread through > several multipart articles. The performance compared to Multipart > threading is significantly better. A realworld group containing 1.1 > million articles was threaded down to approximately 1400 in 40 seconds on > an AMD Sempron 2400+ while the same group took well over 40 minutes via > Multipart threading. > As you will notice I placed this threading mode ahead of Multipart > threading to keep the threading modes in approximately increasing > processing requirements. which is a bad idea as users have to adjust thier tinrc/attributes file to get back thier thread_arts preferece (if they used multipart/subject). I've placed the new threading method at the end so numbers don't change. > If there are any questions or problems regarding this patch please > feel free to contact me. as the threading method can be chosen on a per group basis via attributes it IMHO would make sense to allow a per grou basis percent limit for the new threading method. here is an updated version which fixes the method numbering, does some sanity checks and minor improvemts. urs -- "Only whimps use tape backup: _real_ men just upload their important stuff on ftp, and let the rest of the world mirror it ;)" - Linus
thread_patch.n
(text/plain, 11.6 KB)
diff -Nur tin-1.7.9/doc/tin.1 tin-1.7.9.new/doc/tin.1
--- tin-1.7.9/doc/tin.1 2005-06-12 09:48:07.908128000 +0200
+++ tin-1.7.9.new/doc/tin.1 2005-06-20 11:47:36.435347018 +0200
@@ -2196,12 +2196,19 @@
\&''Subject:'' (default).
.IP 4
\fBMultipart Subject\fP, thread multipart articles on ''Subject:''.
+.IP 5
+\fBPercentage Match\fP, thread base upon a partial character match on
+\&''Subject:''.
.RE
.TP
.B Catchup thread by using left key (thread_catchup_on_exit)
If ON catchup group/thread when leaving with the left arrow key. Default is
ON.
.TP
+.B Matchingness of a thread (thread_perc)
+How closely the subjects must match for two threads to be considered
+part of the same thread. This is a percentage and the default if 75%.
+.TP
.B Score of a thread (thread_score)
How the total score of a thread is computed. Default is 0, the maximum
score in this thread.
diff -Nur tin-1.7.9/doc/tin.5 tin-1.7.9.new/doc/tin.5
--- tin-1.7.9/doc/tin.5 2005-06-07 23:59:41.000000000 +0200
+++ tin-1.7.9.new/doc/tin.5 2005-06-20 11:48:46.362522633 +0200
@@ -1929,10 +1929,16 @@
0) Don't thread, 1) Thread on Subject only 2) Thread on References only,
3) Thread on References then Subject (default)
4) Thread multipart articles on Subject.
+5) Thread on Percentage Match of the Subjects
It's also possible to set the threading type on a per group basis by setting
-the group attribute variable \fBthread_arts\fP to 0 - 4 in the file
+the group attribute variable \fBthread_arts\fP to 0 - 5 in the file
\fI${TIN_HOMEDIR\-"$HOME"}/.tin/attributes\fR.
.TP
+.B thread_perc
+Defines how close the subjects must match while threading by Percentage
+Match for threads to be considered part of a single thread. This value
+is in the range 0 to 100. The default is 75.
+.TP
.B thread_catchup_on_exit
If ON catchup group/thread when leaving with the left arrow key. Default is
ON.
diff -Nur tin-1.7.9/include/extern.h tin-1.7.9.new/include/extern.h
--- tin-1.7.9/include/extern.h 2005-06-07 23:59:41.000000000 +0200
+++ tin-1.7.9.new/include/extern.h 2005-06-20 11:34:16.762014849 +0200
@@ -1567,6 +1567,7 @@
extern struct opttxt txt_tab_goto_next_unread;
extern struct opttxt txt_tex2iso_conv;
extern struct opttxt txt_thread_articles;
+extern struct opttxt txt_thread_perc;
extern struct opttxt txt_thread_catchup_on_exit;
extern struct opttxt txt_thread_score;
extern struct opttxt txt_underscores_regex;
diff -Nur tin-1.7.9/include/tin.h tin-1.7.9.new/include/tin.h
--- tin-1.7.9/include/tin.h 2005-06-07 23:59:41.000000000 +0200
+++ tin-1.7.9.new/include/tin.h 2005-06-20 12:00:51.505482545 +0200
@@ -1136,8 +1136,11 @@
#define THREAD_REFS 2
#define THREAD_BOTH 3
#define THREAD_MULTI 4
+#define THREAD_PERC 5
-#define THREAD_MAX THREAD_MULTI
+#define THREAD_MAX THREAD_PERC
+
+#define THREAD_PERC_DEFAULT 75
/*
* Values for show_author
diff -Nur tin-1.7.9/include/tinrc.h tin-1.7.9.new/include/tinrc.h
--- tin-1.7.9/include/tinrc.h 2005-06-07 23:59:41.000000000 +0200
+++ tin-1.7.9.new/include/tinrc.h 2005-06-20 11:35:28.071938650 +0200
@@ -141,6 +141,7 @@
int sort_threads_type; /* method used to sort base[] */
int strip_bogus;
int thread_articles; /* threading system for viewing articles */
+ int thread_perc; /* how close the match needs to be for THREAD_PERC to recognize two articles as the same thread */
int thread_score; /* how the score for threads is computed*/
int wildcard; /* 0=wildmat, 1=regex */
int score_limit_kill; /* score limit to kill articles */
diff -Nur tin-1.7.9/src/art.c tin-1.7.9.new/src/art.c
--- tin-1.7.9/src/art.c 2005-06-17 16:08:14.528851604 +0200
+++ tin-1.7.9.new/src/art.c 2005-06-20 12:15:15.474961229 +0200
@@ -85,8 +85,9 @@
static t_bool parse_headers(FILE *fp, struct t_article *h);
static t_compfunc eval_sort_arts_func(unsigned int sort_art_type);
static void sort_base(unsigned int sort_threads_type);
-static void thread_by_subject(void);
static void thread_by_multipart(void);
+static void thread_by_percentage(void);
+static void thread_by_subject(void);
/*
@@ -720,6 +721,96 @@
#endif /* 0 */
}
+/*
+ * This Threading algorithm threads articles into 'buckets' where each bucket
+ * contains all the articles which match the root article's subject line to
+ * the configured percentage. Eg, if the root article had the subject "asdf"
+ * and the match percentage was configured to be 75% then any article would
+ * match if its subject was no different in more than a single character.
+ */
+static void
+thread_by_percentage(
+ void)
+{
+ int i, j, k;
+ int root_num = 0; /* The index number of the root we are currently working on. */
+ int unmatched; /* This is the number of characters that don't match between the two strings */
+ unsigned int percentage = 100 - tinrc.thread_perc;
+ int length_diff;
+
+ /* First we need to sort art[] to simplify and speed up the matching. */
+ SortBy(subj_comp_asc);
+
+ /*
+ * Now we put all the articles which match enough into the thread. If
+ * an article doesn't match enough we create a new thread and then add
+ * to that and so on.
+ */
+ base[0] = 0;
+ arts[0].prev = ART_NORMAL;
+ for_each_art(i) {
+ if (i == 0)
+ continue;
+
+ /* Check each character to see if it matched enough */
+ k = 0;
+ unmatched = 0;
+ for (j = 0; arts[base[root_num]].subject[j] != '\0' && arts[i].subject[k] != '\0'; j++) {
+ if (arts[base[root_num]].subject[j] == arts[i].subject[k]) {
+ /* The characters match up. So we move onto the next*/
+ k++;
+ continue;
+ }
+
+ /*
+ * So the characters didn't match up and a character
+ * wasn't inserted. So we'll just ignore these two
+ * characters and see if they were just differing, but
+ * don't through the alignment out. We need to keep
+ * track of this character as a difference.
+ */
+ k++;
+ unmatched++;
+ }
+
+ /*
+ * By getting here we have a number of unmatched characters
+ * between the two strings. We also have the length of the
+ * strings available to us easily.
+ * All we need to do is see if the match is good enough, but
+ * we count differences in the length of the strings against
+ * them matching.
+ */
+
+ length_diff = strlen(arts[base[root_num]].subject) - strlen(arts[i].subject);
+ /* ensure that it's positive */
+ if (length_diff < 0)
+ length_diff = -1 * length_diff;
+
+ unmatched += length_diff;
+ if ((unmatched * 100) / strlen(arts[base[root_num]].subject) > percentage) {
+ /*
+ * If there is less greater than percentage% different
+ * start a new thread.
+ */
+ root_num++;
+ base[root_num] = i;
+ arts[i].prev = ART_NORMAL;
+ continue;
+ } else {
+ /*
+ * The subject lines match enough to consider them part
+ * of a single thread, so add the current article to
+ * the thread.
+ */
+ if (arts[base[root_num]].thread < 0)
+ arts[base[root_num]].thread = i;
+ arts[i].prev = i - 1;
+ arts[i - 1].thread = i;
+ continue;
+ }
+ }
+}
/*
* This was brought over from tags.c, however this version doesn't not
@@ -927,6 +1018,7 @@
* THREAD_REFS Threads are created using the References headers
* THREAD_BOTH Threads created using References and then Subject
* THREAD_MULTI Threads created using Subject to search for Multiparts
+ * THREAD_PERC Threads based upon a char for char match of greater than x%
*
* .thread and .prev are used to hold the threading information, see tin.h for
* more information
@@ -1021,6 +1113,10 @@
thread_by_multipart();
break;
+ case THREAD_PERC:
+ thread_by_percentage();
+ break;
+
default: /* not reached */
break;
}
diff -Nur tin-1.7.9/src/config.c tin-1.7.9.new/src/config.c
--- tin-1.7.9/src/config.c 2005-06-10 22:53:02.508897000 +0200
+++ tin-1.7.9.new/src/config.c 2005-06-20 12:00:48.345062658 +0200
@@ -704,6 +704,9 @@
if (match_integer(buf, "thread_articles=", &tinrc.thread_articles, THREAD_MAX))
break;
+ if (match_integer(buf, "thread_perc=", &tinrc.thread_perc, 100))
+ break;
+
if (match_integer(buf, "thread_score=", &tinrc.thread_score, THREAD_SCORE_WEIGHT))
break;
@@ -925,6 +928,9 @@
fprintf(fp, _(txt_thread_articles.tinrc));
fprintf(fp, "thread_articles=%d\n\n", tinrc.thread_articles);
+ fprintf(fp, _(txt_thread_perc.tinrc));
+ fprintf(fp, "thread_perc=%d\n\n", tinrc.thread_perc);
+
fprintf(fp, _(txt_show_description.tinrc));
fprintf(fp, "show_description=%s\n\n", print_boolean(tinrc.show_description));
@@ -1683,6 +1689,7 @@
t_bool show_lines = FALSE;
t_bool show_score = FALSE;
t_bool thread_articles = FALSE;
+ t_bool thread_perc = FALSE;
t_bool use_builtin_inews = FALSE;
t_bool use_getart_limit = FALSE;
t_bool use_mailreader_i = FALSE;
@@ -1785,6 +1792,9 @@
if (thread_articles)
tinrc.thread_articles = THREAD_BOTH;
+ if (thread_perc)
+ tinrc.thread_perc = THREAD_PERC_DEFAULT;
+
if (use_builtin_inews)
strncpy(tinrc.inews_prog, INTERNAL_CMD, sizeof(tinrc.inews_prog) - 1);
diff -Nur tin-1.7.9/src/init.c tin-1.7.9.new/src/init.c
--- tin-1.7.9/src/init.c 2005-06-17 16:05:08.489045345 +0200
+++ tin-1.7.9.new/src/init.c 2005-06-20 12:00:34.828543703 +0200
@@ -291,6 +291,7 @@
SORT_THREADS_BY_SCORE_DESCEND, /* sort_threads_type */
BOGUS_SHOW, /* strip_bogus */
THREAD_BOTH, /* thread_articles */
+ THREAD_PERC_DEFAULT, /* thread_perc */
THREAD_SCORE_MAX, /* thread_score */
0, /* Default to wildmat, not regex */
-50, /* score_limit_kill */
diff -Nur tin-1.7.9/src/lang.c tin-1.7.9.new/src/lang.c
--- tin-1.7.9/src/lang.c 2005-06-07 23:59:45.000000000 +0200
+++ tin-1.7.9.new/src/lang.c 2005-06-20 11:46:21.581075549 +0200
@@ -1095,7 +1095,8 @@
N_("Subject"),
N_("References"),
N_("Both Subject and References"),
- N_("Multipart Subject")
+ N_("Multipart Subject"),
+ N_("Percentage Match")
};
/*
@@ -1415,7 +1416,21 @@
# 1 = Subject\n\
# 2 = References\n\
# * 3 = Both (Subject and References)\n\
-# 4 = Multipart Subject\n")
+# 4 = Multipart Subject\n\
+# 5 = Percentage Match\n")
+};
+
+struct opttxt txt_thread_perc = {
+ N_("Enter percentage match required to thread together. <CR> sets."),
+ N_("Thread percentage match"),
+ N_("# Thread percentage match...\n\
+# the percentage of characters in the subject of an article that must match a\n\
+# base article for both those articles to be considered to belong to the same\n\
+# thread. This option is an integer percentage, eg. 80, no decimals may follow.\n\
+# If 80 is used here, then 80%% of the characters must match exactly, no\n\
+# insertion of a character, for the two articles to be put in the same thread.\n\
+# eg. 'happy' and 'harpy' would match, but 'harpie', 'happie' and 'harppy'\n\
+# would be threaded separately from 'happy'\n")
};
struct opttxt txt_thread_score = {
diff -Nur tin-1.7.9/src/options_menu.c tin-1.7.9.new/src/options_menu.c
--- tin-1.7.9/src/options_menu.c 2005-06-08 17:16:03.591669000 +0200
+++ tin-1.7.9.new/src/options_menu.c 2005-06-20 12:06:27.541808833 +0200
@@ -1439,6 +1439,12 @@
redraw_screen(option);
break;
+ case OPT_THREAD_PERC:
+ prompt_option_num(option);
+ if (tinrc.thread_perc < 0 || tinrc.thread_perc > 100)
+ tinrc.thread_perc = THREAD_PERC_DEFAULT;
+ break;
+
case OPT_WRAP_COLUMN:
prompt_option_num(option);
/* recook if in an article is open */
diff -Nur tin-1.7.9/src/tincfg.tbl tin-1.7.9.new/src/tincfg.tbl
--- tin-1.7.9/src/tincfg.tbl 2005-06-07 23:59:43.000000000 +0200
+++ tin-1.7.9.new/src/tincfg.tbl 2005-06-20 11:38:12.935709163 +0200
@@ -43,6 +43,7 @@
inverse_okay OPT_ON_OFF
strip_blanks OPT_ON_OFF
thread_articles txt_threading THREAD_MAX+1
+ thread_perc OPT_NUM
sort_article_type txt_sort_a_type SORT_ARTICLES_BY_LINES_ASCEND+1
sort_threads_type txt_sort_t_type SORT_THREADS_BY_SCORE_ASCEND+1
pos_first_unread OPT_ON_OFF