<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.mozilla.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Mindboggler</id>
	<title>MozillaWiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.mozilla.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Mindboggler"/>
	<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/Special:Contributions/Mindboggler"/>
	<updated>2026-04-07T19:42:05Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.10</generator>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=65444</id>
		<title>Places:Full Text Indexing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=65444"/>
		<updated>2007-08-15T10:38:39Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: /* Detailed Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
Full Text Indexing feature will allow user to search for a word/phrase from the pages that he has visited. The search query will be tightly integrated with Places&#039;s nsNavHistoryService. The tighter integration will allow queries like &amp;quot;search for pages visited between 01/05/07(dd/mm/yy) to 20/05/07(dd/mm/yy) containing the word &#039;places&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Design Descision ==&lt;br /&gt;
&lt;br /&gt;
A number of options were looked into before proposing this design. The options included implementing using CLucene(like flock), SQLite&#039;s FTS1 and FTS2 module, implementation using B+ Trees, using relational database etc.. The following text will briefly describe the advantage and disadvantage of all the implementation methods.&lt;br /&gt;
&lt;br /&gt;
CLucene is a full-text indexing engine that stores the index as B+ Trees in files. It uses a very efficient method for storage and retrieval. It has an excellent support for CJK languages. The Places system is a new incorporation into firefox. Hence, it is important that during its initial stages all the code that is written or used is flexible, small and tightly integrated with it. Tighter integration would allow future enhancements specific to firefox. Hence this approach was dropped.&lt;br /&gt;
&lt;br /&gt;
A custom implementation using B+ Tree is a very good option but however, it would require additional B+ Tree engine. In light of availability of an efficient algorithm for implementing full-text indexing using relational database, this method is used.&lt;br /&gt;
&lt;br /&gt;
A naive implementation of full-text indexing is very costly in terms of storage. I&#039;ll briefly explain how it is so. Let us define term. A term is any word that appear in a page. So a relational database contains a table with two columns, term and id. Another table contains two columsn term id and doc id(id of the document the term appeard in). The GNU manuals were analyzed [1]. It is 5.15 Mb of text containing 958,774 occurrences of word out of which 27,554 are unique. But table 2 will require that every occurrence has a corresponding doc id. If term id were stored as int, the amount of space required to store the first column alone, would be 958,774 * 4 bytes, which is about 3 Mb. A B+ Tree implementation is atleast 3Mb more efficient. However a nice encoding scheme and storage model proposed by [2] is almost as efficient as a B+ Tree implementation. This algorithm also leverages the capabilites of relational database system while not losing too much in terms of storage and performance. &lt;br /&gt;
&lt;br /&gt;
SQLite&#039;s FTS1 and FTS2 module are open source implementation of full-text indexing integrated with SQLite. According Scott Hess, FTS developer, &amp;quot;It sounds like a lot of what you&#039;re discussing here matches what we did for fts1 in SQLite. fts2 was a great improvement in terms of performance, and has no breaking changes expected&amp;quot; Hence Fts2 is a great option. I have built mozilla with sqlite and fts2 and it was easy. Moreover, FTS2 is integrated nicely with SQLite requiring no change in sqlite3file.h and sqlite.def files which are essentially exports. One can give queries like&lt;br /&gt;
&amp;lt;pre&amp;gt; Create virtual table history_index using fts2(title, meta, content) &amp;lt;/pre&amp;gt;&lt;br /&gt;
The index gets created. Further to insert,&lt;br /&gt;
&amp;lt;pre&amp;gt; insert into history_index(title, meta, content) values(&#039;some value&#039;, &#039;some value&#039;, &#039;some value&#039;) &amp;lt;/pre&amp;gt;&lt;br /&gt;
And to search,&lt;br /&gt;
&amp;lt;pre&amp;gt; select * from history_index where content matches &#039;value&#039;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Use Case ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: User&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Visit Page&amp;lt;br&amp;gt;&lt;br /&gt;
- Search&amp;lt;br&amp;gt;&lt;br /&gt;
- Clear History&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: Browser&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Expire Page&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The use cases above will be used to validate the design.&lt;br /&gt;
&lt;br /&gt;
== Detailed Design ==&lt;br /&gt;
&lt;br /&gt;
Classes&lt;br /&gt;
&lt;br /&gt;
There are essentially four classes for the back-end:&lt;br /&gt;
# nsNavHistoryQuery&lt;br /&gt;
# nsNavFullTextIndexHelper&lt;br /&gt;
# nsNavFullTextIndex&lt;br /&gt;
# nsNavFullTextAnalyzer&lt;br /&gt;
&lt;br /&gt;
===nsNavHistoryQuery===&lt;br /&gt;
&lt;br /&gt;
This class is already implemented with all features except searching text. The mechanism of searching is no different from what described in http://developer.mozilla.org/en/docs/Places:Query_System. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var historyService = Components.classes[&amp;quot;@mozilla.org/browser/nav-history-service;1&amp;quot;].getService(Components.interfaces.nsINavHistoryService);&lt;br /&gt;
&lt;br /&gt;
var options = historyService.getNewQueryOptions();&lt;br /&gt;
var query = historyService.getNewQuery();&lt;br /&gt;
query.searchTerms = &amp;quot;Mozilla Firefox&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// execute the query&lt;br /&gt;
var result = historyService.executeQuery(query, options);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result will contain a list of URI. A number of options can be specified in query and options making it very powerful. Conjunctive queries can be also be executed with historyService.executeQueries and a list of query as parameter.&lt;br /&gt;
&lt;br /&gt;
Internally the function calls nsNavFullTextIndex::searchDocument(searchTerms)  which returns a list of URI ranked according to algorithm described in  SearchDocument(terms) function that will be described later in this document. The list of URI is further filtered by the other parameters set in query and options variable. In case of executeQueries method, the list is aggregated with results from multiple queries.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndex===&lt;br /&gt;
&lt;br /&gt;
This class interacts with FTS2. It is responsible for executing queries that will insert content into the index and generate  URI of matching documents on a search request. This class will be private and will not be exposed outside. A search request will also generate text snippets to be displayed for UI to display&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndexHelper===&lt;br /&gt;
&lt;br /&gt;
This class implements nsParserDataListener and is registered to listen to data. The class is called every time there is data available upon page request. After aggregating the data, nsFullTextIndex::indexDocument(documentData) is called. This function will execute FTS2 queries.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextTokenizer===&lt;br /&gt;
&lt;br /&gt;
These are bunch of classes that will be registered to FTS2 module allowing custom tokenizers and different languages. Before the standarad tokenizer, the stream has to pass through a html tag stripper.&lt;br /&gt;
&lt;br /&gt;
== Front-End ==&lt;br /&gt;
===nsNavQueryParser===&lt;br /&gt;
The function of this is to break the query given in human language into a graph of TermQuery and BooleanQuery. BooleanQuery is a struct with two operand(each a TermQuery or BooleanQuery) and an operator(AND, OR, NOT, XOR). Although the idea here is to implement all kind of queries as in: http://lucene.apache.org/java/docs/queryparsersyntax.html eventually. nsNavHistoryQuery is used to query using the query struct. The results of which is url_list which is displayed using view.&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
# [http://www.vldb.org/conf/1992/P353.PDF An Efficient Indexing Technique for full-text database systems] Justin Jobel, Alistair Moffet, Ron Sacks Davis&lt;br /&gt;
# [http://www.n3labs.com/pdf/putz91using-inverted-DBMS.pdf Using a relational database for an Inverted Text index] Steve Putz, Xerox PARC&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=65429</id>
		<title>Places:Full Text Indexing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=65429"/>
		<updated>2007-08-15T06:45:07Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: /* nsNavFullTextIndex */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
Full Text Indexing feature will allow user to search for a word/phrase from the pages that he has visited. The search query will be tightly integrated with Places&#039;s nsNavHistoryService. The tighter integration will allow queries like &amp;quot;search for pages visited between 01/05/07(dd/mm/yy) to 20/05/07(dd/mm/yy) containing the word &#039;places&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Design Descision ==&lt;br /&gt;
&lt;br /&gt;
A number of options were looked into before proposing this design. The options included implementing using CLucene(like flock), SQLite&#039;s FTS1 and FTS2 module, implementation using B+ Trees, using relational database etc.. The following text will briefly describe the advantage and disadvantage of all the implementation methods.&lt;br /&gt;
&lt;br /&gt;
CLucene is a full-text indexing engine that stores the index as B+ Trees in files. It uses a very efficient method for storage and retrieval. It has an excellent support for CJK languages. The Places system is a new incorporation into firefox. Hence, it is important that during its initial stages all the code that is written or used is flexible, small and tightly integrated with it. Tighter integration would allow future enhancements specific to firefox. Hence this approach was dropped.&lt;br /&gt;
&lt;br /&gt;
A custom implementation using B+ Tree is a very good option but however, it would require additional B+ Tree engine. In light of availability of an efficient algorithm for implementing full-text indexing using relational database, this method is used.&lt;br /&gt;
&lt;br /&gt;
A naive implementation of full-text indexing is very costly in terms of storage. I&#039;ll briefly explain how it is so. Let us define term. A term is any word that appear in a page. So a relational database contains a table with two columns, term and id. Another table contains two columsn term id and doc id(id of the document the term appeard in). The GNU manuals were analyzed [1]. It is 5.15 Mb of text containing 958,774 occurrences of word out of which 27,554 are unique. But table 2 will require that every occurrence has a corresponding doc id. If term id were stored as int, the amount of space required to store the first column alone, would be 958,774 * 4 bytes, which is about 3 Mb. A B+ Tree implementation is atleast 3Mb more efficient. However a nice encoding scheme and storage model proposed by [2] is almost as efficient as a B+ Tree implementation. This algorithm also leverages the capabilites of relational database system while not losing too much in terms of storage and performance. &lt;br /&gt;
&lt;br /&gt;
SQLite&#039;s FTS1 and FTS2 module are open source implementation of full-text indexing integrated with SQLite. According Scott Hess, FTS developer, &amp;quot;It sounds like a lot of what you&#039;re discussing here matches what we did for fts1 in SQLite. fts2 was a great improvement in terms of performance, and has no breaking changes expected&amp;quot; Hence Fts2 is a great option. I have built mozilla with sqlite and fts2 and it was easy. Moreover, FTS2 is integrated nicely with SQLite requiring no change in sqlite3file.h and sqlite.def files which are essentially exports. One can give queries like&lt;br /&gt;
&amp;lt;pre&amp;gt; Create virtual table history_index using fts2(title, meta, content) &amp;lt;/pre&amp;gt;&lt;br /&gt;
The index gets created. Further to insert,&lt;br /&gt;
&amp;lt;pre&amp;gt; insert into history_index(title, meta, content) values(&#039;some value&#039;, &#039;some value&#039;, &#039;some value&#039;) &amp;lt;/pre&amp;gt;&lt;br /&gt;
And to search,&lt;br /&gt;
&amp;lt;pre&amp;gt; select * from history_index where content matches &#039;value&#039;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Use Case ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: User&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Visit Page&amp;lt;br&amp;gt;&lt;br /&gt;
- Search&amp;lt;br&amp;gt;&lt;br /&gt;
- Clear History&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: Browser&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Expire Page&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The use cases above will be used to validate the design.&lt;br /&gt;
&lt;br /&gt;
== Detailed Design ==&lt;br /&gt;
&lt;br /&gt;
Classes&lt;br /&gt;
&lt;br /&gt;
There are essentially four classes for the back-end:&lt;br /&gt;
# nsNavHistoryQuery&lt;br /&gt;
# nsNavFullTextIndexHelper&lt;br /&gt;
# nsNavFullTextIndex&lt;br /&gt;
# nsNavFullTextAnalyzer&lt;br /&gt;
&lt;br /&gt;
===nsNavHistoryQuery===&lt;br /&gt;
&lt;br /&gt;
This class is already implemented with all features except searching text. The mechanism of searching is no different from what described in http://developer.mozilla.org/en/docs/Places:Query_System. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var historyService = Components.classes[&amp;quot;@mozilla.org/browser/nav-history-service;1&amp;quot;].getService(Components.interfaces.nsINavHistoryService);&lt;br /&gt;
&lt;br /&gt;
var options = historyService.getNewQueryOptions();&lt;br /&gt;
var query = historyService.getNewQuery();&lt;br /&gt;
query.searchTerms = &amp;quot;Mozilla Firefox&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// execute the query&lt;br /&gt;
var result = historyService.executeQuery(query, options);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result will contain a list of URI. A number of options can be specified in query and options making it very powerful. Conjunctive queries can be also be executed with historyService.executeQueries and a list of query as parameter.&lt;br /&gt;
&lt;br /&gt;
Internally the function calls nsNavFullTextIndex::searchDocument(searchTerms)  which returns a list of URI ranked according to algorithm described in  SearchDocument(terms) function that will be described later in this document. The list of URI is further filtered by the other parameters set in query and options variable. In case of executeQueries method, the list is aggregated with results from multiple queries.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndex===&lt;br /&gt;
&lt;br /&gt;
This class interacts with FTS2. It is responsible for executing queries that will generate  URI of matching documents. It will also generate text snippets to be displayed for UI to display&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndexHelper===&lt;br /&gt;
&lt;br /&gt;
This class contains mechanism for scheduling documents for indexing. It works pretty much like nsNavHistoryExpire. The scheduling balances indexing with overall firefox performance. It has a timer which when expires picks an unindexed document from the history and calls nsNavFullTextIndex::addDocument to index. When and how the timer is set, dictates the overall efficiency. Whenever a user visits a page, the timer is triggered. &lt;br /&gt;
&lt;br /&gt;
When user visits a page, the history service calls OnAddURI function of this class. This function starts the timer. When timer expires the call back function is called. The function checks If there are any more document to be indexed and it has not been a long while since the user called addURI. If so, then it resets the timer and waits to expire again.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextAnalyzer===&lt;br /&gt;
&lt;br /&gt;
The analyzer design is similar to the way Lucene works. The Lucene design enables support for multiple languages.&lt;br /&gt;
The Analyzer takes the tokens generated from the tokenizer and discards those that are not required for indexing. E.g. is, a, an the can be discarded. This is enabled by by tokenStream classes. Refer to nsNavFullTextTokenStream and Lucene API for more detail.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextTokenStream===&lt;br /&gt;
&lt;br /&gt;
TokenStream is an abstract class with three methods next, reset and close. The next method returns a token. Token is a class representing startoffset, endoffset and termtext. TokenStream needs input to tokenize. There are two concrete class for TokenStream:&lt;br /&gt;
# Tokenizer: The input is an inputstream.&lt;br /&gt;
# TokenFilter: The input is another TokenStream. This works like pipes.&lt;br /&gt;
&lt;br /&gt;
== Front-End ==&lt;br /&gt;
===nsNavQueryParser===&lt;br /&gt;
The function of this is to break the query given in human language into a graph of TermQuery and BooleanQuery. BooleanQuery is a struct with two operand(each a TermQuery or BooleanQuery) and an operator(AND, OR, NOT, XOR). Although the idea here is to implement all kind of queries as in: http://lucene.apache.org/java/docs/queryparsersyntax.html eventually. nsNavHistoryQuery is used to query using the query struct. The results of which is url_list which is displayed using view.&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
# [http://www.vldb.org/conf/1992/P353.PDF An Efficient Indexing Technique for full-text database systems] Justin Jobel, Alistair Moffet, Ron Sacks Davis&lt;br /&gt;
# [http://www.n3labs.com/pdf/putz91using-inverted-DBMS.pdf Using a relational database for an Inverted Text index] Steve Putz, Xerox PARC&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=65428</id>
		<title>Places:Full Text Indexing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=65428"/>
		<updated>2007-08-15T06:37:32Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
Full Text Indexing feature will allow user to search for a word/phrase from the pages that he has visited. The search query will be tightly integrated with Places&#039;s nsNavHistoryService. The tighter integration will allow queries like &amp;quot;search for pages visited between 01/05/07(dd/mm/yy) to 20/05/07(dd/mm/yy) containing the word &#039;places&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Design Descision ==&lt;br /&gt;
&lt;br /&gt;
A number of options were looked into before proposing this design. The options included implementing using CLucene(like flock), SQLite&#039;s FTS1 and FTS2 module, implementation using B+ Trees, using relational database etc.. The following text will briefly describe the advantage and disadvantage of all the implementation methods.&lt;br /&gt;
&lt;br /&gt;
CLucene is a full-text indexing engine that stores the index as B+ Trees in files. It uses a very efficient method for storage and retrieval. It has an excellent support for CJK languages. The Places system is a new incorporation into firefox. Hence, it is important that during its initial stages all the code that is written or used is flexible, small and tightly integrated with it. Tighter integration would allow future enhancements specific to firefox. Hence this approach was dropped.&lt;br /&gt;
&lt;br /&gt;
A custom implementation using B+ Tree is a very good option but however, it would require additional B+ Tree engine. In light of availability of an efficient algorithm for implementing full-text indexing using relational database, this method is used.&lt;br /&gt;
&lt;br /&gt;
A naive implementation of full-text indexing is very costly in terms of storage. I&#039;ll briefly explain how it is so. Let us define term. A term is any word that appear in a page. So a relational database contains a table with two columns, term and id. Another table contains two columsn term id and doc id(id of the document the term appeard in). The GNU manuals were analyzed [1]. It is 5.15 Mb of text containing 958,774 occurrences of word out of which 27,554 are unique. But table 2 will require that every occurrence has a corresponding doc id. If term id were stored as int, the amount of space required to store the first column alone, would be 958,774 * 4 bytes, which is about 3 Mb. A B+ Tree implementation is atleast 3Mb more efficient. However a nice encoding scheme and storage model proposed by [2] is almost as efficient as a B+ Tree implementation. This algorithm also leverages the capabilites of relational database system while not losing too much in terms of storage and performance. &lt;br /&gt;
&lt;br /&gt;
SQLite&#039;s FTS1 and FTS2 module are open source implementation of full-text indexing integrated with SQLite. According Scott Hess, FTS developer, &amp;quot;It sounds like a lot of what you&#039;re discussing here matches what we did for fts1 in SQLite. fts2 was a great improvement in terms of performance, and has no breaking changes expected&amp;quot; Hence Fts2 is a great option. I have built mozilla with sqlite and fts2 and it was easy. Moreover, FTS2 is integrated nicely with SQLite requiring no change in sqlite3file.h and sqlite.def files which are essentially exports. One can give queries like&lt;br /&gt;
&amp;lt;pre&amp;gt; Create virtual table history_index using fts2(title, meta, content) &amp;lt;/pre&amp;gt;&lt;br /&gt;
The index gets created. Further to insert,&lt;br /&gt;
&amp;lt;pre&amp;gt; insert into history_index(title, meta, content) values(&#039;some value&#039;, &#039;some value&#039;, &#039;some value&#039;) &amp;lt;/pre&amp;gt;&lt;br /&gt;
And to search,&lt;br /&gt;
&amp;lt;pre&amp;gt; select * from history_index where content matches &#039;value&#039;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Use Case ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: User&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Visit Page&amp;lt;br&amp;gt;&lt;br /&gt;
- Search&amp;lt;br&amp;gt;&lt;br /&gt;
- Clear History&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: Browser&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Expire Page&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The use cases above will be used to validate the design.&lt;br /&gt;
&lt;br /&gt;
== Detailed Design ==&lt;br /&gt;
&lt;br /&gt;
Classes&lt;br /&gt;
&lt;br /&gt;
There are essentially four classes for the back-end:&lt;br /&gt;
# nsNavHistoryQuery&lt;br /&gt;
# nsNavFullTextIndexHelper&lt;br /&gt;
# nsNavFullTextIndex&lt;br /&gt;
# nsNavFullTextAnalyzer&lt;br /&gt;
&lt;br /&gt;
===nsNavHistoryQuery===&lt;br /&gt;
&lt;br /&gt;
This class is already implemented with all features except searching text. The mechanism of searching is no different from what described in http://developer.mozilla.org/en/docs/Places:Query_System. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var historyService = Components.classes[&amp;quot;@mozilla.org/browser/nav-history-service;1&amp;quot;].getService(Components.interfaces.nsINavHistoryService);&lt;br /&gt;
&lt;br /&gt;
var options = historyService.getNewQueryOptions();&lt;br /&gt;
var query = historyService.getNewQuery();&lt;br /&gt;
query.searchTerms = &amp;quot;Mozilla Firefox&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// execute the query&lt;br /&gt;
var result = historyService.executeQuery(query, options);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result will contain a list of URI. A number of options can be specified in query and options making it very powerful. Conjunctive queries can be also be executed with historyService.executeQueries and a list of query as parameter.&lt;br /&gt;
&lt;br /&gt;
Internally the function calls nsNavFullTextIndex::searchDocument(searchTerms)  which returns a list of URI ranked according to algorithm described in  SearchDocument(terms) function that will be described later in this document. The list of URI is further filtered by the other parameters set in query and options variable. In case of executeQueries method, the list is aggregated with results from multiple queries.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndex===&lt;br /&gt;
&lt;br /&gt;
This class interacts with SQLite database. This implements the algorithm for adding document to the index, removing document from the index and search for a given term. This search function is used by nsNavHistoryQuery::search function. Look at [2] for the algorithm used.&lt;br /&gt;
&lt;br /&gt;
Block is a struct used to encode and decode block. A block is variable length delta encoded. Variable Length delta Encoding, compresses very efficiently balancing speed and storage requirement.&lt;br /&gt;
&amp;lt;pre&amp;gt;struct Block {&lt;br /&gt;
	//The width of the field is 255 bytes. The return value is an int indicating number of elements in the data that were encoded in the out byte array.&lt;br /&gt;
	int encode(in int[] data, out byte[] encodedBlock) {&lt;br /&gt;
		//How to encode more efficiently, any idea?&lt;br /&gt;
		int[] bigEndian;&lt;br /&gt;
		int k = 0;&lt;br /&gt;
		data[i - 1] = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; data.length; i++) {&lt;br /&gt;
			data[i] -= data[i - 1];&lt;br /&gt;
			int j = 0;&lt;br /&gt;
			while(data[i] != 0) {&lt;br /&gt;
				bigEndian[j++] = data[i] % 128;&lt;br /&gt;
				data[i] /= 128;&lt;br /&gt;
			}&lt;br /&gt;
			for( ; j &amp;gt; 0; j--, k++) {&lt;br /&gt;
				encodedBlock[k] = (1 &amp;lt;&amp;lt; 8) &amp;amp; bigEndian[j];&lt;br /&gt;
			}&lt;br /&gt;
			encodedBlock[k++] = bigEndian[0];&lt;br /&gt;
			if (k &amp;gt; 255) &lt;br /&gt;
				return i - 1&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	void decode(in byte[] encodedBlock, out int[] data) {&lt;br /&gt;
		int j = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; encodedBlock.length; i++) {&lt;br /&gt;
			if (encodedBlock[i] &amp;amp;&amp;amp; (1 &amp;lt;&amp;lt; 8)) {&lt;br /&gt;
				data[j] *= 128;&lt;br /&gt;
				data[j] += encodedBlock[i] &amp;amp; ((1 &amp;lt;&amp;lt; 8) - 1);&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				data[j] += data[j - 1]; //Because it was delta encoded&lt;br /&gt;
				data[j++] = encodedBlock[i];&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
AddDocument(connection, document, analyzer) {&lt;br /&gt;
	AddDocument in two pass. Scan the document for terms and then invert. We have the doc id. We will require a hash map library to make it very efficient. Term is a hash map. Usage: term[&#039;termname&#039;] = array of pos.&lt;br /&gt;
&amp;lt;pre&amp;gt;	while(analyzer.hasTerms()) {&lt;br /&gt;
		cTerm = analyzer.nextTerm();&lt;br /&gt;
		term[cTerm.Name].add(cTerm.pos);&lt;br /&gt;
	}&lt;br /&gt;
	iterator i = term.iterator();&lt;br /&gt;
	while(i.hasNext()) {&lt;br /&gt;
		termName = i.next();&lt;br /&gt;
		termPos[] = term[termName];&lt;br /&gt;
	&lt;br /&gt;
		//hopefully sqlite caches query results, it will be inefficient otherwise.&lt;br /&gt;
&lt;br /&gt;
		if (term already in table) { &lt;br /&gt;
			record = executeQuery(&amp;quot;SELECT ﬁrstdoc, ﬂags, block FROM postings&lt;br /&gt;
				WHERE word = termName&lt;br /&gt;
				AND ﬁrstdoc == (Given as &amp;gt;= in [2], == is correct in my opinion)&lt;br /&gt;
				   (SELECT max (ﬁrstdoc) FROM postings&lt;br /&gt;
				    WHERE word = termName and flags &amp;lt; 128)&amp;quot;);&lt;br /&gt;
			//Refer to [2] for explanation of this.&lt;br /&gt;
			//only one record is retrieved with flags == 0 or flags between 0 and 128&lt;br /&gt;
			//when flag == 0, the block contains only document list&lt;br /&gt;
			if (flag == 0) {&lt;br /&gt;
				1) Decode the block&lt;br /&gt;
				2) See if one more document can be fitted in this.&lt;br /&gt;
				3) Yes? add to it&lt;br /&gt;
					i) find the position list&lt;br /&gt;
						positionList = executeQuery(&amp;quot;SELECT firstdoc, flags, block FROM positings&lt;br /&gt;
							where firstdoc = &lt;br /&gt;
								SELECT max(ﬁrstdoc) FROM postings&lt;br /&gt;
									WHERE word = termName&lt;br /&gt;
									AND ﬁrstdoc &amp;gt;= record.firstdoc AND flags &amp;gt;= 128&lt;br /&gt;
					ii) Try to add as many position in this block&lt;br /&gt;
					iii) when the block is done, create a new row, with firstdoc == currentdoc &lt;br /&gt;
						and flags == 128 or 129 depending on whether the prev firstdoc was same as this.&lt;br /&gt;
&lt;br /&gt;
					iv) goto ii) if there are more left.&lt;br /&gt;
				4) no?&lt;br /&gt;
					i) create a new row with firstdoc = docid, flags=2&lt;br /&gt;
					ii) To the block  add, doc id and doc freq. And all the pos. Note the position listings are never split when flag == 2.&lt;br /&gt;
						We must try to fit all the position listing in this block.99% of the case this should be possible. Make a small calculation, you&#039;ll find out i am correct&lt;br /&gt;
					iii) The rare case it is not possible? create two rows one with flags==0 and flags==128&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				//This is slightly complex in that there is both document list and position list in the same block. We have to decode the block. Try to add document id and and all the position to the position list. This might not be possible. And we&#039;ll have to create two new rows, one with flags == 0 and other with flags == 128&lt;br /&gt;
			}&lt;br /&gt;
			update the word count in the word list table appropriately&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	commit to the database&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveDocument() {&lt;br /&gt;
	Inherently inefficient because of the structure that we have adopted. Needs to be optimized. The general algorithm revolves around finding the record whose firstDoc is immediately less or same as the docId we are searching for.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;	e.g. Say we want to delete docid = 20. We got two records&lt;br /&gt;
	firstDoc = 18, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	firstDoc = 22, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	So we select the record with docid = 18 that is immediately before docid = 20;&lt;br /&gt;
	&lt;br /&gt;
	query to achieve this: SELECT word, firstdoc, block FROM postings where&lt;br /&gt;
				firstdoc = SELECT max(firstdoc) FROM postings where&lt;br /&gt;
						firstdoc &amp;lt; docIdWeAreSearchingFor&lt;br /&gt;
	This returns a number of records with flag == 0 or 0 &amp;lt; flag &amp;lt; 128 or flag == 128 or flag &amp;gt; 128. &lt;br /&gt;
	for each record we found do the following:&lt;br /&gt;
		docAndPostingTable = decodeBlock(block);&lt;br /&gt;
		we have just decoded the block using Block::decodeBlock(). &lt;br /&gt;
		docAndPostingTable.find(docId) //we check the decode block if it contains docId of our interest. &lt;br /&gt;
		if docId found&lt;br /&gt;
			Check the flag&lt;br /&gt;
			when flag == 0 //only document list&lt;br /&gt;
				remove the document and freq from the block&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				if docId == firstDoc update firstDoc with the immediately following doc&lt;br /&gt;
				if no more docs left in this row, delete the row&lt;br /&gt;
			when 0 &amp;lt; flag &amp;lt; 128, contains both document list and posting table&lt;br /&gt;
				remove the document from the block.&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				remove all the postings of the document for the term in the block&lt;br /&gt;
				if (docId == firstDoc) update firstDoc with immediately following doc&lt;br /&gt;
				if no more docs left in the row, delete the row&lt;br /&gt;
			when flag &amp;gt;= 128 //only postings table&lt;br /&gt;
				remove all the postings corresponding to the doc&lt;br /&gt;
				update the firstDoc for the record&lt;br /&gt;
				delete the record if block is empty&lt;br /&gt;
		&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SearchDocument(terms) {&lt;br /&gt;
	terms is something like: &amp;quot;mac apple panther&amp;quot;. Basically a collection of terms&lt;br /&gt;
	The ranking algorithm as described in this document http://lucene.apache.org/java/2_1_0/api/org/apache/lucene/search/Similarity.html is used.&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndexHelper===&lt;br /&gt;
&lt;br /&gt;
This class contains mechanism for scheduling documents for indexing. It works pretty much like nsNavHistoryExpire. The scheduling balances indexing with overall firefox performance. It has a timer which when expires picks an unindexed document from the history and calls nsNavFullTextIndex::addDocument to index. When and how the timer is set, dictates the overall efficiency. Whenever a user visits a page, the timer is triggered. &lt;br /&gt;
&lt;br /&gt;
When user visits a page, the history service calls OnAddURI function of this class. This function starts the timer. When timer expires the call back function is called. The function checks If there are any more document to be indexed and it has not been a long while since the user called addURI. If so, then it resets the timer and waits to expire again.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextAnalyzer===&lt;br /&gt;
&lt;br /&gt;
The analyzer design is similar to the way Lucene works. The Lucene design enables support for multiple languages.&lt;br /&gt;
The Analyzer takes the tokens generated from the tokenizer and discards those that are not required for indexing. E.g. is, a, an the can be discarded. This is enabled by by tokenStream classes. Refer to nsNavFullTextTokenStream and Lucene API for more detail.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextTokenStream===&lt;br /&gt;
&lt;br /&gt;
TokenStream is an abstract class with three methods next, reset and close. The next method returns a token. Token is a class representing startoffset, endoffset and termtext. TokenStream needs input to tokenize. There are two concrete class for TokenStream:&lt;br /&gt;
# Tokenizer: The input is an inputstream.&lt;br /&gt;
# TokenFilter: The input is another TokenStream. This works like pipes.&lt;br /&gt;
&lt;br /&gt;
== Front-End ==&lt;br /&gt;
===nsNavQueryParser===&lt;br /&gt;
The function of this is to break the query given in human language into a graph of TermQuery and BooleanQuery. BooleanQuery is a struct with two operand(each a TermQuery or BooleanQuery) and an operator(AND, OR, NOT, XOR). Although the idea here is to implement all kind of queries as in: http://lucene.apache.org/java/docs/queryparsersyntax.html eventually. nsNavHistoryQuery is used to query using the query struct. The results of which is url_list which is displayed using view.&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
# [http://www.vldb.org/conf/1992/P353.PDF An Efficient Indexing Technique for full-text database systems] Justin Jobel, Alistair Moffet, Ron Sacks Davis&lt;br /&gt;
# [http://www.n3labs.com/pdf/putz91using-inverted-DBMS.pdf Using a relational database for an Inverted Text index] Steve Putz, Xerox PARC&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=65427</id>
		<title>Places:Full Text Indexing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=65427"/>
		<updated>2007-08-15T06:36:28Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: /* Design Descision */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;ToDO: Formatting, it is badly formatted. Any idea on how to effectively format code?&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
Full Text Indexing feature will allow user to search for a word/phrase from the pages that he has visited. The search query will be tightly integrated with Places&#039;s nsNavHistoryService. The tighter integration will allow queries like &amp;quot;search for pages visited between 01/05/07(dd/mm/yy) to 20/05/07(dd/mm/yy) containing the word &#039;places&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Design Descision ==&lt;br /&gt;
&lt;br /&gt;
A number of options were looked into before proposing this design. The options included implementing using CLucene(like flock), SQLite&#039;s FTS1 and FTS2 module, implementation using B+ Trees, using relational database etc.. The following text will briefly describe the advantage and disadvantage of all the implementation methods.&lt;br /&gt;
&lt;br /&gt;
CLucene is a full-text indexing engine that stores the index as B+ Trees in files. It uses a very efficient method for storage and retrieval. It has an excellent support for CJK languages. The Places system is a new incorporation into firefox. Hence, it is important that during its initial stages all the code that is written or used is flexible, small and tightly integrated with it. Tighter integration would allow future enhancements specific to firefox. Hence this approach was dropped.&lt;br /&gt;
&lt;br /&gt;
A custom implementation using B+ Tree is a very good option but however, it would require additional B+ Tree engine. In light of availability of an efficient algorithm for implementing full-text indexing using relational database, this method is used.&lt;br /&gt;
&lt;br /&gt;
A naive implementation of full-text indexing is very costly in terms of storage. I&#039;ll briefly explain how it is so. Let us define term. A term is any word that appear in a page. So a relational database contains a table with two columns, term and id. Another table contains two columsn term id and doc id(id of the document the term appeard in). The GNU manuals were analyzed [1]. It is 5.15 Mb of text containing 958,774 occurrences of word out of which 27,554 are unique. But table 2 will require that every occurrence has a corresponding doc id. If term id were stored as int, the amount of space required to store the first column alone, would be 958,774 * 4 bytes, which is about 3 Mb. A B+ Tree implementation is atleast 3Mb more efficient. However a nice encoding scheme and storage model proposed by [2] is almost as efficient as a B+ Tree implementation. This algorithm also leverages the capabilites of relational database system while not losing too much in terms of storage and performance. &lt;br /&gt;
&lt;br /&gt;
SQLite&#039;s FTS1 and FTS2 module are open source implementation of full-text indexing integrated with SQLite. According Scott Hess, FTS developer, &amp;quot;It sounds like a lot of what you&#039;re discussing here matches what we did for fts1 in SQLite. fts2 was a great improvement in terms of performance, and has no breaking changes expected&amp;quot; Hence Fts2 is a great option. I have built mozilla with sqlite and fts2 and it was easy. Moreover, FTS2 is integrated nicely with SQLite requiring no change in sqlite3file.h and sqlite.def files which are essentially exports. One can give queries like&lt;br /&gt;
&amp;lt;pre&amp;gt; Create virtual table history_index using fts2(title, meta, content) &amp;lt;/pre&amp;gt;&lt;br /&gt;
The index gets created. Further to insert,&lt;br /&gt;
&amp;lt;pre&amp;gt; insert into history_index(title, meta, content) values(&#039;some value&#039;, &#039;some value&#039;, &#039;some value&#039;) &amp;lt;/pre&amp;gt;&lt;br /&gt;
And to search,&lt;br /&gt;
&amp;lt;pre&amp;gt; select * from history_index where content matches &#039;value&#039;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Use Case ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: User&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Visit Page&amp;lt;br&amp;gt;&lt;br /&gt;
- Search&amp;lt;br&amp;gt;&lt;br /&gt;
- Clear History&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: Browser&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Expire Page&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The use cases above will be used to validate the design.&lt;br /&gt;
&lt;br /&gt;
== Database Design ==&lt;br /&gt;
&lt;br /&gt;
TODO: Check the url_table and put it here. The url_table acts as the document table. The url table will contain additionally the document length&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; &lt;br /&gt;
|+&#039;&#039;&#039;Word Table&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!columnn!!type!!bytes!!description&lt;br /&gt;
|-&lt;br /&gt;
|word||varchar||&amp;lt;=100||term for indexing(Shouldn&#039;t it be unicode? how do i store unicode?)&lt;br /&gt;
|-&lt;br /&gt;
|wordnum||integer||4||unique id. Integer works cause the number of unique words will be atmost a million. Non-english language?&lt;br /&gt;
|-&lt;br /&gt;
|doc_count||integer||4||number of documents the word occurred in&lt;br /&gt;
|-&lt;br /&gt;
|word_count||integer||4||number of occurrences of the word&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; &lt;br /&gt;
|+&#039;&#039;&#039;Posting Table&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!column!!type!!bytes!!description&lt;br /&gt;
|-&lt;br /&gt;
|wordnum||integer||4||This is the foreign key matching that in the word table&lt;br /&gt;
|-&lt;br /&gt;
|firstdoc||integer||4||lowest doc id referenced in the block&lt;br /&gt;
|-&lt;br /&gt;
|flags||tinyint||1||indicates the block type, length of doc list, sequence number&lt;br /&gt;
|-&lt;br /&gt;
|block||varbinary||&amp;lt;=255||contains encoded document and/or position postings for the word&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To Do&lt;br /&gt;
# We might need a table or two more for ranking efficiently&lt;br /&gt;
# Check if SQLite has varbinary datatype. There is a BLOB data type, I am sure.&lt;br /&gt;
&lt;br /&gt;
Note that the tables structure is subject to change to improve efficiency. New Tables might be formed and/or the table might add/remove column&lt;br /&gt;
&lt;br /&gt;
== Detailed Design ==&lt;br /&gt;
&lt;br /&gt;
Classes&lt;br /&gt;
&lt;br /&gt;
There are essentially four classes for the back-end:&lt;br /&gt;
# nsNavHistoryQuery&lt;br /&gt;
# nsNavFullTextIndexHelper&lt;br /&gt;
# nsNavFullTextIndex&lt;br /&gt;
# nsNavFullTextAnalyzer&lt;br /&gt;
&lt;br /&gt;
===nsNavHistoryQuery===&lt;br /&gt;
&lt;br /&gt;
This class is already implemented with all features except searching text. The mechanism of searching is no different from what described in http://developer.mozilla.org/en/docs/Places:Query_System. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var historyService = Components.classes[&amp;quot;@mozilla.org/browser/nav-history-service;1&amp;quot;].getService(Components.interfaces.nsINavHistoryService);&lt;br /&gt;
&lt;br /&gt;
var options = historyService.getNewQueryOptions();&lt;br /&gt;
var query = historyService.getNewQuery();&lt;br /&gt;
query.searchTerms = &amp;quot;Mozilla Firefox&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// execute the query&lt;br /&gt;
var result = historyService.executeQuery(query, options);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result will contain a list of URI. A number of options can be specified in query and options making it very powerful. Conjunctive queries can be also be executed with historyService.executeQueries and a list of query as parameter.&lt;br /&gt;
&lt;br /&gt;
Internally the function calls nsNavFullTextIndex::searchDocument(searchTerms)  which returns a list of URI ranked according to algorithm described in  SearchDocument(terms) function that will be described later in this document. The list of URI is further filtered by the other parameters set in query and options variable. In case of executeQueries method, the list is aggregated with results from multiple queries.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndex===&lt;br /&gt;
&lt;br /&gt;
This class interacts with SQLite database. This implements the algorithm for adding document to the index, removing document from the index and search for a given term. This search function is used by nsNavHistoryQuery::search function. Look at [2] for the algorithm used.&lt;br /&gt;
&lt;br /&gt;
Block is a struct used to encode and decode block. A block is variable length delta encoded. Variable Length delta Encoding, compresses very efficiently balancing speed and storage requirement.&lt;br /&gt;
&amp;lt;pre&amp;gt;struct Block {&lt;br /&gt;
	//The width of the field is 255 bytes. The return value is an int indicating number of elements in the data that were encoded in the out byte array.&lt;br /&gt;
	int encode(in int[] data, out byte[] encodedBlock) {&lt;br /&gt;
		//How to encode more efficiently, any idea?&lt;br /&gt;
		int[] bigEndian;&lt;br /&gt;
		int k = 0;&lt;br /&gt;
		data[i - 1] = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; data.length; i++) {&lt;br /&gt;
			data[i] -= data[i - 1];&lt;br /&gt;
			int j = 0;&lt;br /&gt;
			while(data[i] != 0) {&lt;br /&gt;
				bigEndian[j++] = data[i] % 128;&lt;br /&gt;
				data[i] /= 128;&lt;br /&gt;
			}&lt;br /&gt;
			for( ; j &amp;gt; 0; j--, k++) {&lt;br /&gt;
				encodedBlock[k] = (1 &amp;lt;&amp;lt; 8) &amp;amp; bigEndian[j];&lt;br /&gt;
			}&lt;br /&gt;
			encodedBlock[k++] = bigEndian[0];&lt;br /&gt;
			if (k &amp;gt; 255) &lt;br /&gt;
				return i - 1&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	void decode(in byte[] encodedBlock, out int[] data) {&lt;br /&gt;
		int j = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; encodedBlock.length; i++) {&lt;br /&gt;
			if (encodedBlock[i] &amp;amp;&amp;amp; (1 &amp;lt;&amp;lt; 8)) {&lt;br /&gt;
				data[j] *= 128;&lt;br /&gt;
				data[j] += encodedBlock[i] &amp;amp; ((1 &amp;lt;&amp;lt; 8) - 1);&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				data[j] += data[j - 1]; //Because it was delta encoded&lt;br /&gt;
				data[j++] = encodedBlock[i];&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
AddDocument(connection, document, analyzer) {&lt;br /&gt;
	AddDocument in two pass. Scan the document for terms and then invert. We have the doc id. We will require a hash map library to make it very efficient. Term is a hash map. Usage: term[&#039;termname&#039;] = array of pos.&lt;br /&gt;
&amp;lt;pre&amp;gt;	while(analyzer.hasTerms()) {&lt;br /&gt;
		cTerm = analyzer.nextTerm();&lt;br /&gt;
		term[cTerm.Name].add(cTerm.pos);&lt;br /&gt;
	}&lt;br /&gt;
	iterator i = term.iterator();&lt;br /&gt;
	while(i.hasNext()) {&lt;br /&gt;
		termName = i.next();&lt;br /&gt;
		termPos[] = term[termName];&lt;br /&gt;
	&lt;br /&gt;
		//hopefully sqlite caches query results, it will be inefficient otherwise.&lt;br /&gt;
&lt;br /&gt;
		if (term already in table) { &lt;br /&gt;
			record = executeQuery(&amp;quot;SELECT ﬁrstdoc, ﬂags, block FROM postings&lt;br /&gt;
				WHERE word = termName&lt;br /&gt;
				AND ﬁrstdoc == (Given as &amp;gt;= in [2], == is correct in my opinion)&lt;br /&gt;
				   (SELECT max (ﬁrstdoc) FROM postings&lt;br /&gt;
				    WHERE word = termName and flags &amp;lt; 128)&amp;quot;);&lt;br /&gt;
			//Refer to [2] for explanation of this.&lt;br /&gt;
			//only one record is retrieved with flags == 0 or flags between 0 and 128&lt;br /&gt;
			//when flag == 0, the block contains only document list&lt;br /&gt;
			if (flag == 0) {&lt;br /&gt;
				1) Decode the block&lt;br /&gt;
				2) See if one more document can be fitted in this.&lt;br /&gt;
				3) Yes? add to it&lt;br /&gt;
					i) find the position list&lt;br /&gt;
						positionList = executeQuery(&amp;quot;SELECT firstdoc, flags, block FROM positings&lt;br /&gt;
							where firstdoc = &lt;br /&gt;
								SELECT max(ﬁrstdoc) FROM postings&lt;br /&gt;
									WHERE word = termName&lt;br /&gt;
									AND ﬁrstdoc &amp;gt;= record.firstdoc AND flags &amp;gt;= 128&lt;br /&gt;
					ii) Try to add as many position in this block&lt;br /&gt;
					iii) when the block is done, create a new row, with firstdoc == currentdoc &lt;br /&gt;
						and flags == 128 or 129 depending on whether the prev firstdoc was same as this.&lt;br /&gt;
&lt;br /&gt;
					iv) goto ii) if there are more left.&lt;br /&gt;
				4) no?&lt;br /&gt;
					i) create a new row with firstdoc = docid, flags=2&lt;br /&gt;
					ii) To the block  add, doc id and doc freq. And all the pos. Note the position listings are never split when flag == 2.&lt;br /&gt;
						We must try to fit all the position listing in this block.99% of the case this should be possible. Make a small calculation, you&#039;ll find out i am correct&lt;br /&gt;
					iii) The rare case it is not possible? create two rows one with flags==0 and flags==128&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				//This is slightly complex in that there is both document list and position list in the same block. We have to decode the block. Try to add document id and and all the position to the position list. This might not be possible. And we&#039;ll have to create two new rows, one with flags == 0 and other with flags == 128&lt;br /&gt;
			}&lt;br /&gt;
			update the word count in the word list table appropriately&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	commit to the database&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveDocument() {&lt;br /&gt;
	Inherently inefficient because of the structure that we have adopted. Needs to be optimized. The general algorithm revolves around finding the record whose firstDoc is immediately less or same as the docId we are searching for.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;	e.g. Say we want to delete docid = 20. We got two records&lt;br /&gt;
	firstDoc = 18, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	firstDoc = 22, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	So we select the record with docid = 18 that is immediately before docid = 20;&lt;br /&gt;
	&lt;br /&gt;
	query to achieve this: SELECT word, firstdoc, block FROM postings where&lt;br /&gt;
				firstdoc = SELECT max(firstdoc) FROM postings where&lt;br /&gt;
						firstdoc &amp;lt; docIdWeAreSearchingFor&lt;br /&gt;
	This returns a number of records with flag == 0 or 0 &amp;lt; flag &amp;lt; 128 or flag == 128 or flag &amp;gt; 128. &lt;br /&gt;
	for each record we found do the following:&lt;br /&gt;
		docAndPostingTable = decodeBlock(block);&lt;br /&gt;
		we have just decoded the block using Block::decodeBlock(). &lt;br /&gt;
		docAndPostingTable.find(docId) //we check the decode block if it contains docId of our interest. &lt;br /&gt;
		if docId found&lt;br /&gt;
			Check the flag&lt;br /&gt;
			when flag == 0 //only document list&lt;br /&gt;
				remove the document and freq from the block&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				if docId == firstDoc update firstDoc with the immediately following doc&lt;br /&gt;
				if no more docs left in this row, delete the row&lt;br /&gt;
			when 0 &amp;lt; flag &amp;lt; 128, contains both document list and posting table&lt;br /&gt;
				remove the document from the block.&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				remove all the postings of the document for the term in the block&lt;br /&gt;
				if (docId == firstDoc) update firstDoc with immediately following doc&lt;br /&gt;
				if no more docs left in the row, delete the row&lt;br /&gt;
			when flag &amp;gt;= 128 //only postings table&lt;br /&gt;
				remove all the postings corresponding to the doc&lt;br /&gt;
				update the firstDoc for the record&lt;br /&gt;
				delete the record if block is empty&lt;br /&gt;
		&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SearchDocument(terms) {&lt;br /&gt;
	terms is something like: &amp;quot;mac apple panther&amp;quot;. Basically a collection of terms&lt;br /&gt;
	The ranking algorithm as described in this document http://lucene.apache.org/java/2_1_0/api/org/apache/lucene/search/Similarity.html is used.&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndexHelper===&lt;br /&gt;
&lt;br /&gt;
This class contains mechanism for scheduling documents for indexing. It works pretty much like nsNavHistoryExpire. The scheduling balances indexing with overall firefox performance. It has a timer which when expires picks an unindexed document from the history and calls nsNavFullTextIndex::addDocument to index. When and how the timer is set, dictates the overall efficiency. Whenever a user visits a page, the timer is triggered. &lt;br /&gt;
&lt;br /&gt;
When user visits a page, the history service calls OnAddURI function of this class. This function starts the timer. When timer expires the call back function is called. The function checks If there are any more document to be indexed and it has not been a long while since the user called addURI. If so, then it resets the timer and waits to expire again.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextAnalyzer===&lt;br /&gt;
&lt;br /&gt;
The analyzer design is similar to the way Lucene works. The Lucene design enables support for multiple languages.&lt;br /&gt;
The Analyzer takes the tokens generated from the tokenizer and discards those that are not required for indexing. E.g. is, a, an the can be discarded. This is enabled by by tokenStream classes. Refer to nsNavFullTextTokenStream and Lucene API for more detail.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextTokenStream===&lt;br /&gt;
&lt;br /&gt;
TokenStream is an abstract class with three methods next, reset and close. The next method returns a token. Token is a class representing startoffset, endoffset and termtext. TokenStream needs input to tokenize. There are two concrete class for TokenStream:&lt;br /&gt;
# Tokenizer: The input is an inputstream.&lt;br /&gt;
# TokenFilter: The input is another TokenStream. This works like pipes.&lt;br /&gt;
&lt;br /&gt;
== Front-End ==&lt;br /&gt;
===nsNavQueryParser===&lt;br /&gt;
The function of this is to break the query given in human language into a graph of TermQuery and BooleanQuery. BooleanQuery is a struct with two operand(each a TermQuery or BooleanQuery) and an operator(AND, OR, NOT, XOR). Although the idea here is to implement all kind of queries as in: http://lucene.apache.org/java/docs/queryparsersyntax.html eventually. nsNavHistoryQuery is used to query using the query struct. The results of which is url_list which is displayed using view.&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
# [http://www.vldb.org/conf/1992/P353.PDF An Efficient Indexing Technique for full-text database systems] Justin Jobel, Alistair Moffet, Ron Sacks Davis&lt;br /&gt;
# [http://www.n3labs.com/pdf/putz91using-inverted-DBMS.pdf Using a relational database for an Inverted Text index] Steve Putz, Xerox PARC&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=65443</id>
		<title>Places:Full Text Indexing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=65443"/>
		<updated>2007-08-15T03:02:06Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: /* nsNavFullTextIndex */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
Full Text Indexing feature will allow user to search for a word/phrase from the pages that he has visited. The search query will be tightly integrated with Places&#039;s nsNavHistoryService. The tighter integration will allow queries like &amp;quot;search for pages visited between 01/05/07(dd/mm/yy) to 20/05/07(dd/mm/yy) containing the word &#039;places&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Design Descision ==&lt;br /&gt;
&lt;br /&gt;
A number of options were looked into before proposing this design. The options included implementing using CLucene(like flock), SQLite&#039;s FTS1 and FTS2 module, implementation using B+ Trees, using relational database etc.. The following text will briefly describe the advantage and disadvantage of all the implementation methods.&lt;br /&gt;
&lt;br /&gt;
CLucene is a full-text indexing engine that stores the index as B+ Trees in files. It uses a very efficient method for storage and retrieval. It has an excellent support for CJK languages. The Places system is a new incorporation into firefox. Hence, it is important that during its initial stages all the code that is written or used is flexible, small and tightly integrated with it. Tighter integration would allow future enhancements specific to firefox. Hence this approach was dropped.&lt;br /&gt;
&lt;br /&gt;
A custom implementation using B+ Tree is a very good option but however, it would require additional B+ Tree engine. In light of availability of an efficient algorithm for implementing full-text indexing using relational database, this method is used.&lt;br /&gt;
&lt;br /&gt;
A naive implementation of full-text indexing is very costly in terms of storage. I&#039;ll briefly explain how it is so. Let us define term. A term is any word that appear in a page. So a relational database contains a table with two columns, term and id. Another table contains two columsn term id and doc id(id of the document the term appeard in). The GNU manuals were analyzed [1]. It is 5.15 Mb of text containing 958,774 occurrences of word out of which 27,554 are unique. But table 2 will require that every occurrence has a corresponding doc id. If term id were stored as int, the amount of space required to store the first column alone, would be 958,774 * 4 bytes, which is about 3 Mb. A B+ Tree implementation is atleast 3Mb more efficient. However a nice encoding scheme and storage model proposed by [2] is almost as efficient as a B+ Tree implementation. This algorithm also leverages the capabilites of relational database system while not losing too much in terms of storage and performance. &lt;br /&gt;
&lt;br /&gt;
SQLite&#039;s FTS1 and FTS2 module are open source implementation of full-text indexing integrated with SQLite. According Scott Hess, FTS developer, &amp;quot;It sounds like a lot of what you&#039;re discussing here matches what we did for fts1 in SQLite. fts2 was a great improvement in terms of performance, and has no breaking changes expected&amp;quot; Hence Fts2 is a great option. I have built mozilla with sqlite and fts2 and it was easy. Moreover, FTS2 is integrated nicely with SQLite requiring no change in sqlite3file.h and sqlite.def files which are essentially exports. One can give queries like&lt;br /&gt;
&amp;lt;pre&amp;gt; Create virtual table history_index using fts2(title, meta, content) &amp;lt;/pre&amp;gt;&lt;br /&gt;
The index gets created. Further to insert,&lt;br /&gt;
&amp;lt;pre&amp;gt; insert into history_index(title, meta, content) values(&#039;some value&#039;, &#039;some value&#039;, &#039;some value&#039;) &amp;lt;/pre&amp;gt;&lt;br /&gt;
And to search,&lt;br /&gt;
&amp;lt;pre&amp;gt; select * from history_index where content matches &#039;value&#039;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Use Case ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: User&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Visit Page&amp;lt;br&amp;gt;&lt;br /&gt;
- Search&amp;lt;br&amp;gt;&lt;br /&gt;
- Clear History&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: Browser&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Expire Page&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The use cases above will be used to validate the design.&lt;br /&gt;
&lt;br /&gt;
== Detailed Design ==&lt;br /&gt;
&lt;br /&gt;
Classes&lt;br /&gt;
&lt;br /&gt;
There are essentially four classes for the back-end:&lt;br /&gt;
# nsNavHistoryQuery&lt;br /&gt;
# nsNavFullTextIndexHelper&lt;br /&gt;
# nsNavFullTextIndex&lt;br /&gt;
# nsNavFullTextAnalyzer&lt;br /&gt;
&lt;br /&gt;
===nsNavHistoryQuery===&lt;br /&gt;
&lt;br /&gt;
This class is already implemented with all features except searching text. The mechanism of searching is no different from what described in http://developer.mozilla.org/en/docs/Places:Query_System. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var historyService = Components.classes[&amp;quot;@mozilla.org/browser/nav-history-service;1&amp;quot;].getService(Components.interfaces.nsINavHistoryService);&lt;br /&gt;
&lt;br /&gt;
var options = historyService.getNewQueryOptions();&lt;br /&gt;
var query = historyService.getNewQuery();&lt;br /&gt;
query.searchTerms = &amp;quot;Mozilla Firefox&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// execute the query&lt;br /&gt;
var result = historyService.executeQuery(query, options);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result will contain a list of URI. A number of options can be specified in query and options making it very powerful. Conjunctive queries can be also be executed with historyService.executeQueries and a list of query as parameter.&lt;br /&gt;
&lt;br /&gt;
Internally the function calls nsNavFullTextIndex::searchDocument(searchTerms)  which returns a list of URI ranked according to algorithm described in  SearchDocument(terms) function that will be described later in this document. The list of URI is further filtered by the other parameters set in query and options variable. In case of executeQueries method, the list is aggregated with results from multiple queries.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndex===&lt;br /&gt;
&lt;br /&gt;
This class interacts with FTS2. It is responsible for executing queries that will insert content into the index and generate  URI of matching documents on a search request. This class will be private and will not be exposed outside. A search request will also generate text snippets to be displayed for UI to display&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndexHelper===&lt;br /&gt;
&lt;br /&gt;
This class contains mechanism for scheduling documents for indexing. It works pretty much like nsNavHistoryExpire. The scheduling balances indexing with overall firefox performance. It has a timer which when expires picks an unindexed document from the history and calls nsNavFullTextIndex::addDocument to index. When and how the timer is set, dictates the overall efficiency. Whenever a user visits a page, the timer is triggered. &lt;br /&gt;
&lt;br /&gt;
When user visits a page, the history service calls OnAddURI function of this class. This function starts the timer. When timer expires the call back function is called. The function checks If there are any more document to be indexed and it has not been a long while since the user called addURI. If so, then it resets the timer and waits to expire again.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextAnalyzer===&lt;br /&gt;
&lt;br /&gt;
The analyzer design is similar to the way Lucene works. The Lucene design enables support for multiple languages.&lt;br /&gt;
The Analyzer takes the tokens generated from the tokenizer and discards those that are not required for indexing. E.g. is, a, an the can be discarded. This is enabled by by tokenStream classes. Refer to nsNavFullTextTokenStream and Lucene API for more detail.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextTokenStream===&lt;br /&gt;
&lt;br /&gt;
TokenStream is an abstract class with three methods next, reset and close. The next method returns a token. Token is a class representing startoffset, endoffset and termtext. TokenStream needs input to tokenize. There are two concrete class for TokenStream:&lt;br /&gt;
# Tokenizer: The input is an inputstream.&lt;br /&gt;
# TokenFilter: The input is another TokenStream. This works like pipes.&lt;br /&gt;
&lt;br /&gt;
== Front-End ==&lt;br /&gt;
===nsNavQueryParser===&lt;br /&gt;
The function of this is to break the query given in human language into a graph of TermQuery and BooleanQuery. BooleanQuery is a struct with two operand(each a TermQuery or BooleanQuery) and an operator(AND, OR, NOT, XOR). Although the idea here is to implement all kind of queries as in: http://lucene.apache.org/java/docs/queryparsersyntax.html eventually. nsNavHistoryQuery is used to query using the query struct. The results of which is url_list which is displayed using view.&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
# [http://www.vldb.org/conf/1992/P353.PDF An Efficient Indexing Technique for full-text database systems] Justin Jobel, Alistair Moffet, Ron Sacks Davis&lt;br /&gt;
# [http://www.n3labs.com/pdf/putz91using-inverted-DBMS.pdf Using a relational database for an Inverted Text index] Steve Putz, Xerox PARC&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Talk:Places:Full_Text_Indexing&amp;diff=61420</id>
		<title>Talk:Places:Full Text Indexing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Talk:Places:Full_Text_Indexing&amp;diff=61420"/>
		<updated>2007-07-07T08:08:46Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It sounds like a lot of what you&#039;re discussing here matches what we did for fts1 in SQLite.  fts2 was a great improvement in terms of performance, and has no breaking changes expected.  I&#039;m considering folding in ability to index external content for fts3, but customers actually talking to me get some priority :-).&lt;br /&gt;
&lt;br /&gt;
[shess at google dot com]&lt;br /&gt;
&lt;br /&gt;
Have gone through fts2 again. Looks promising. FTS2 does not support indexing extenal content. Although, this is not ideal, we are still going for this looking at other benefits. Also, FTS3, Scott hints, will have an API similar to FTS2. Will update the wiki with update design very soon. However the basic tasks are as follows:&lt;br /&gt;
&lt;br /&gt;
# Build Mozilla with FTS2 support&lt;br /&gt;
# Design Database Table&lt;br /&gt;
# Code for table generation in nsNavHistory.cpp&lt;br /&gt;
# nsNavFullTextIndexHelper will remain the same&lt;br /&gt;
#  UI&lt;br /&gt;
# Tokenizer System&lt;br /&gt;
# Support for other Languages&lt;br /&gt;
# Performance&lt;br /&gt;
# Test&lt;br /&gt;
&lt;br /&gt;
[vertex3d2004 at gmail dot com]&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Talk:Places:Full_Text_Indexing&amp;diff=61419</id>
		<title>Talk:Places:Full Text Indexing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Talk:Places:Full_Text_Indexing&amp;diff=61419"/>
		<updated>2007-07-07T08:03:35Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It sounds like a lot of what you&#039;re discussing here matches what we did for fts1 in SQLite.  fts2 was a great improvement in terms of performance, and has no breaking changes expected.  I&#039;m considering folding in ability to index external content for fts3, but customers actually talking to me get some priority :-).&lt;br /&gt;
&lt;br /&gt;
[shess at google dot com]&lt;br /&gt;
&lt;br /&gt;
Have gone through fts2 again. Looks promising. FTS2 does not support indexing extenal content. Although, this is not ideal, we are still going for this looking at other benefits. Also, FTS3, Scott hints, will have an API similar to FTS2. Will update the wiki with update design very soon. However the basic tasks are as follows:&lt;br /&gt;
&lt;br /&gt;
1) Build Mozilla with FTS2 support&lt;br /&gt;
2) Design Database Table&lt;br /&gt;
3) Code for table generation in nsNavHistory.cpp&lt;br /&gt;
3) nsNavFullTextIndexHelper will remain the same&lt;br /&gt;
4) UI&lt;br /&gt;
5) Tokenizer System&lt;br /&gt;
6) Support for other Languages&lt;br /&gt;
7) Performance&lt;br /&gt;
8) Test&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=59421</id>
		<title>Places:Full Text Indexing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=59421"/>
		<updated>2007-06-13T05:22:25Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: /* Front-End */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;ToDO: Formatting, it is badly formatted. Any idea on how to effectively format code?&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
Full Text Indexing feature will allow user to search for a word/phrase from the pages that he has visited. The search query will be tightly integrated with Places&#039;s nsNavHistoryService. The tighter integration will allow queries like &amp;quot;search for pages visited between 01/05/07(dd/mm/yy) to 20/05/07(dd/mm/yy) containing the word &#039;places&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Design Descision ==&lt;br /&gt;
&lt;br /&gt;
A number of options were looked into before proposing this design. The options included implementing using CLucene(like flock), SQLite&#039;s FTS1 and FTS2 module, implementation using B+ Trees, using relational database etc.. The following text will briefly describe the advantage and disadvantage of all the implementation methods.&lt;br /&gt;
&lt;br /&gt;
CLucene is a full-text indexing engine that stores the index as B+ Trees in files. It uses a very efficient method for storage and retrieval. It has an excellent support for CJK languages. The Places system is a new incorporation into firefox. Hence, it is important that during its initial stages all the code that is written or used is flexible, small and tightly integrated with it. Tighter integration would allow future enhancements specific to firefox. Hence this approach was dropped.&lt;br /&gt;
&lt;br /&gt;
SQLite&#039;s FTS1 and FTS2 module are open source implementation of full-text indexing integrated with SQLite. FTS1 and FTS2 stores the text in B+ Trees and the access to the full-text index is using SQL. However, there are number of short-comings for our usage. FTS2 is still in stage where the API and data might change without backward compatibility. FTS1 does not have custom tokenizer which means no CJK support. Also, FTS1 stores the entire page duplicating what is aleady stored in the cache.&lt;br /&gt;
&lt;br /&gt;
A custom implementation using B+ Tree is a very good option but however, it would require additional B+ Tree engine. In light of availability of an efficient algorithm for implementing full-text indexing using relational database, this method is used.&lt;br /&gt;
&lt;br /&gt;
A naive implementation of full-text indexing is very costly in terms of storage. I&#039;ll briefly explain how it is so. Let us define term. A term is any word that appear in a page. So a relational database contains a table with two columns, term and id. Another table contains two columsn term id and doc id(id of the document the term appeard in). The GNU manuals were analyzed [1]. It is 5.15 Mb of text containing 958,774 occurrences of word out of which 27,554 are unique. But table 2 will require that every occurrence has a corresponding doc id. If term id were stored as int, the amount of space required to store the first column alone, would be 958,774 * 4 bytes, which is about 3 Mb. A B+ Tree implementation is atleast 3Mb more efficient. However a nice encoding scheme and storage model proposed by [2] is almost as efficient as a B+ Tree implementation. This algorithm also leverages the capabilites of relational database system while not losing too much in terms of storage and performance. Hence I propose to implement this algorithm.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Use Case ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: User&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Visit Page&amp;lt;br&amp;gt;&lt;br /&gt;
- Search&amp;lt;br&amp;gt;&lt;br /&gt;
- Clear History&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: Browser&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Expire Page&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The use cases above will be used to validate the design.&lt;br /&gt;
&lt;br /&gt;
== Database Design ==&lt;br /&gt;
&lt;br /&gt;
TODO: Check the url_table and put it here. The url_table acts as the document table. The url table will contain additionally the document length&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; &lt;br /&gt;
|+&#039;&#039;&#039;Word Table&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!columnn!!type!!bytes!!description&lt;br /&gt;
|-&lt;br /&gt;
|word||varchar||&amp;lt;=100||term for indexing(Shouldn&#039;t it be unicode? how do i store unicode?)&lt;br /&gt;
|-&lt;br /&gt;
|wordnum||integer||4||unique id. Integer works cause the number of unique words will be atmost a million. Non-english language?&lt;br /&gt;
|-&lt;br /&gt;
|doc_count||integer||4||number of documents the word occurred in&lt;br /&gt;
|-&lt;br /&gt;
|word_count||integer||4||number of occurrences of the word&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; &lt;br /&gt;
|+&#039;&#039;&#039;Posting Table&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!column!!type!!bytes!!description&lt;br /&gt;
|-&lt;br /&gt;
|wordnum||integer||4||This is the foreign key matching that in the word table&lt;br /&gt;
|-&lt;br /&gt;
|firstdoc||integer||4||lowest doc id referenced in the block&lt;br /&gt;
|-&lt;br /&gt;
|flags||tinyint||1||indicates the block type, length of doc list, sequence number&lt;br /&gt;
|-&lt;br /&gt;
|block||varbinary||&amp;lt;=255||contains encoded document and/or position postings for the word&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To Do&lt;br /&gt;
# We might need a table or two more for ranking efficiently&lt;br /&gt;
# Check if SQLite has varbinary datatype. There is a BLOB data type, I am sure.&lt;br /&gt;
&lt;br /&gt;
Note that the tables structure is subject to change to improve efficiency. New Tables might be formed and/or the table might add/remove column&lt;br /&gt;
&lt;br /&gt;
== Detailed Design ==&lt;br /&gt;
&lt;br /&gt;
Classes&lt;br /&gt;
&lt;br /&gt;
There are essentially four classes for the back-end:&lt;br /&gt;
# nsNavHistoryQuery&lt;br /&gt;
# nsNavFullTextIndexHelper&lt;br /&gt;
# nsNavFullTextIndex&lt;br /&gt;
# nsNavFullTextAnalyzer&lt;br /&gt;
&lt;br /&gt;
===nsNavHistoryQuery===&lt;br /&gt;
&lt;br /&gt;
This class is already implemented with all features except searching text. The mechanism of searching is no different from what described in http://developer.mozilla.org/en/docs/Places:Query_System. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var historyService = Components.classes[&amp;quot;@mozilla.org/browser/nav-history-service;1&amp;quot;].getService(Components.interfaces.nsINavHistoryService);&lt;br /&gt;
&lt;br /&gt;
var options = historyService.getNewQueryOptions();&lt;br /&gt;
var query = historyService.getNewQuery();&lt;br /&gt;
query.searchTerms = &amp;quot;Mozilla Firefox&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// execute the query&lt;br /&gt;
var result = historyService.executeQuery(query, options);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result will contain a list of URI. A number of options can be specified in query and options making it very powerful. Conjunctive queries can be also be executed with historyService.executeQueries and a list of query as parameter.&lt;br /&gt;
&lt;br /&gt;
Internally the function calls nsNavFullTextIndex::searchDocument(searchTerms)  which returns a list of URI ranked according to algorithm described in  SearchDocument(terms) function that will be described later in this document. The list of URI is further filtered by the other parameters set in query and options variable. In case of executeQueries method, the list is aggregated with results from multiple queries.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndex===&lt;br /&gt;
&lt;br /&gt;
This class interacts with SQLite database. This implements the algorithm for adding document to the index, removing document from the index and search for a given term. This search function is used by nsNavHistoryQuery::search function. Look at [2] for the algorithm used.&lt;br /&gt;
&lt;br /&gt;
Block is a struct used to encode and decode block. A block is variable length delta encoded. Variable Length delta Encoding, compresses very efficiently balancing speed and storage requirement.&lt;br /&gt;
&amp;lt;pre&amp;gt;struct Block {&lt;br /&gt;
	//The width of the field is 255 bytes. The return value is an int indicating number of elements in the data that were encoded in the out byte array.&lt;br /&gt;
	int encode(in int[] data, out byte[] encodedBlock) {&lt;br /&gt;
		//How to encode more efficiently, any idea?&lt;br /&gt;
		int[] bigEndian;&lt;br /&gt;
		int k = 0;&lt;br /&gt;
		data[i - 1] = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; data.length; i++) {&lt;br /&gt;
			data[i] -= data[i - 1];&lt;br /&gt;
			int j = 0;&lt;br /&gt;
			while(data[i] != 0) {&lt;br /&gt;
				bigEndian[j++] = data[i] % 128;&lt;br /&gt;
				data[i] /= 128;&lt;br /&gt;
			}&lt;br /&gt;
			for( ; j &amp;gt; 0; j--, k++) {&lt;br /&gt;
				encodedBlock[k] = (1 &amp;lt;&amp;lt; 8) &amp;amp; bigEndian[j];&lt;br /&gt;
			}&lt;br /&gt;
			encodedBlock[k++] = bigEndian[0];&lt;br /&gt;
			if (k &amp;gt; 255) &lt;br /&gt;
				return i - 1&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	void decode(in byte[] encodedBlock, out int[] data) {&lt;br /&gt;
		int j = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; encodedBlock.length; i++) {&lt;br /&gt;
			if (encodedBlock[i] &amp;amp;&amp;amp; (1 &amp;lt;&amp;lt; 8)) {&lt;br /&gt;
				data[j] *= 128;&lt;br /&gt;
				data[j] += encodedBlock[i] &amp;amp; ((1 &amp;lt;&amp;lt; 8) - 1);&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				data[j] += data[j - 1]; //Because it was delta encoded&lt;br /&gt;
				data[j++] = encodedBlock[i];&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
AddDocument(connection, document, analyzer) {&lt;br /&gt;
	AddDocument in two pass. Scan the document for terms and then invert. We have the doc id. We will require a hash map library to make it very efficient. Term is a hash map. Usage: term[&#039;termname&#039;] = array of pos.&lt;br /&gt;
&amp;lt;pre&amp;gt;	while(analyzer.hasTerms()) {&lt;br /&gt;
		cTerm = analyzer.nextTerm();&lt;br /&gt;
		term[cTerm.Name].add(cTerm.pos);&lt;br /&gt;
	}&lt;br /&gt;
	iterator i = term.iterator();&lt;br /&gt;
	while(i.hasNext()) {&lt;br /&gt;
		termName = i.next();&lt;br /&gt;
		termPos[] = term[termName];&lt;br /&gt;
	&lt;br /&gt;
		//hopefully sqlite caches query results, it will be inefficient otherwise.&lt;br /&gt;
&lt;br /&gt;
		if (term already in table) { &lt;br /&gt;
			record = executeQuery(&amp;quot;SELECT ﬁrstdoc, ﬂags, block FROM postings&lt;br /&gt;
				WHERE word = termName&lt;br /&gt;
				AND ﬁrstdoc == (Given as &amp;gt;= in [2], == is correct in my opinion)&lt;br /&gt;
				   (SELECT max (ﬁrstdoc) FROM postings&lt;br /&gt;
				    WHERE word = termName and flags &amp;lt; 128)&amp;quot;);&lt;br /&gt;
			//Refer to [2] for explanation of this.&lt;br /&gt;
			//only one record is retrieved with flags == 0 or flags between 0 and 128&lt;br /&gt;
			//when flag == 0, the block contains only document list&lt;br /&gt;
			if (flag == 0) {&lt;br /&gt;
				1) Decode the block&lt;br /&gt;
				2) See if one more document can be fitted in this.&lt;br /&gt;
				3) Yes? add to it&lt;br /&gt;
					i) find the position list&lt;br /&gt;
						positionList = executeQuery(&amp;quot;SELECT firstdoc, flags, block FROM positings&lt;br /&gt;
							where firstdoc = &lt;br /&gt;
								SELECT max(ﬁrstdoc) FROM postings&lt;br /&gt;
									WHERE word = termName&lt;br /&gt;
									AND ﬁrstdoc &amp;gt;= record.firstdoc AND flags &amp;gt;= 128&lt;br /&gt;
					ii) Try to add as many position in this block&lt;br /&gt;
					iii) when the block is done, create a new row, with firstdoc == currentdoc &lt;br /&gt;
						and flags == 128 or 129 depending on whether the prev firstdoc was same as this.&lt;br /&gt;
&lt;br /&gt;
					iv) goto ii) if there are more left.&lt;br /&gt;
				4) no?&lt;br /&gt;
					i) create a new row with firstdoc = docid, flags=2&lt;br /&gt;
					ii) To the block  add, doc id and doc freq. And all the pos. Note the position listings are never split when flag == 2.&lt;br /&gt;
						We must try to fit all the position listing in this block.99% of the case this should be possible. Make a small calculation, you&#039;ll find out i am correct&lt;br /&gt;
					iii) The rare case it is not possible? create two rows one with flags==0 and flags==128&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				//This is slightly complex in that there is both document list and position list in the same block. We have to decode the block. Try to add document id and and all the position to the position list. This might not be possible. And we&#039;ll have to create two new rows, one with flags == 0 and other with flags == 128&lt;br /&gt;
			}&lt;br /&gt;
			update the word count in the word list table appropriately&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	commit to the database&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveDocument() {&lt;br /&gt;
	Inherently inefficient because of the structure that we have adopted. Needs to be optimized. The general algorithm revolves around finding the record whose firstDoc is immediately less or same as the docId we are searching for.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;	e.g. Say we want to delete docid = 20. We got two records&lt;br /&gt;
	firstDoc = 18, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	firstDoc = 22, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	So we select the record with docid = 18 that is immediately before docid = 20;&lt;br /&gt;
	&lt;br /&gt;
	query to achieve this: SELECT word, firstdoc, block FROM postings where&lt;br /&gt;
				firstdoc = SELECT max(firstdoc) FROM postings where&lt;br /&gt;
						firstdoc &amp;lt; docIdWeAreSearchingFor&lt;br /&gt;
	This returns a number of records with flag == 0 or 0 &amp;lt; flag &amp;lt; 128 or flag == 128 or flag &amp;gt; 128. &lt;br /&gt;
	for each record we found do the following:&lt;br /&gt;
		docAndPostingTable = decodeBlock(block);&lt;br /&gt;
		we have just decoded the block using Block::decodeBlock(). &lt;br /&gt;
		docAndPostingTable.find(docId) //we check the decode block if it contains docId of our interest. &lt;br /&gt;
		if docId found&lt;br /&gt;
			Check the flag&lt;br /&gt;
			when flag == 0 //only document list&lt;br /&gt;
				remove the document and freq from the block&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				if docId == firstDoc update firstDoc with the immediately following doc&lt;br /&gt;
				if no more docs left in this row, delete the row&lt;br /&gt;
			when 0 &amp;lt; flag &amp;lt; 128, contains both document list and posting table&lt;br /&gt;
				remove the document from the block.&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				remove all the postings of the document for the term in the block&lt;br /&gt;
				if (docId == firstDoc) update firstDoc with immediately following doc&lt;br /&gt;
				if no more docs left in the row, delete the row&lt;br /&gt;
			when flag &amp;gt;= 128 //only postings table&lt;br /&gt;
				remove all the postings corresponding to the doc&lt;br /&gt;
				update the firstDoc for the record&lt;br /&gt;
				delete the record if block is empty&lt;br /&gt;
		&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SearchDocument(terms) {&lt;br /&gt;
	terms is something like: &amp;quot;mac apple panther&amp;quot;. Basically a collection of terms&lt;br /&gt;
	The ranking algorithm as described in this document http://lucene.apache.org/java/2_1_0/api/org/apache/lucene/search/Similarity.html is used.&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndexHelper===&lt;br /&gt;
&lt;br /&gt;
This class contains mechanism for scheduling documents for indexing. It works pretty much like nsNavHistoryExpire. The scheduling balances indexing with overall firefox performance. It has a timer which when expires picks an unindexed document from the history and calls nsNavFullTextIndex::addDocument to index. When and how the timer is set, dictates the overall efficiency. Whenever a user visits a page, the timer is triggered. &lt;br /&gt;
&lt;br /&gt;
When user visits a page, the history service calls OnAddURI function of this class. This function starts the timer. When timer expires the call back function is called. The function checks If there are any more document to be indexed and it has not been a long while since the user called addURI. If so, then it resets the timer and waits to expire again.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextAnalyzer===&lt;br /&gt;
&lt;br /&gt;
The analyzer design is similar to the way Lucene works. The Lucene design enables support for multiple languages.&lt;br /&gt;
The Analyzer takes the tokens generated from the tokenizer and discards those that are not required for indexing. E.g. is, a, an the can be discarded. This is enabled by by tokenStream classes. Refer to nsNavFullTextTokenStream and Lucene API for more detail.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextTokenStream===&lt;br /&gt;
&lt;br /&gt;
TokenStream is an abstract class with three methods next, reset and close. The next method returns a token. Token is a class representing startoffset, endoffset and termtext. TokenStream needs input to tokenize. There are two concrete class for TokenStream:&lt;br /&gt;
# Tokenizer: The input is an inputstream.&lt;br /&gt;
# TokenFilter: The input is another TokenStream. This works like pipes.&lt;br /&gt;
&lt;br /&gt;
== Front-End ==&lt;br /&gt;
===nsNavQueryParser===&lt;br /&gt;
The function of this is to break the query given in human language into a graph of TermQuery and BooleanQuery. BooleanQuery is a struct with two operand(each a TermQuery or BooleanQuery) and an operator(AND, OR, NOT, XOR). Although the idea here is to implement all kind of queries as in: http://lucene.apache.org/java/docs/queryparsersyntax.html eventually. nsNavHistoryQuery is used to query using the query struct. The results of which is url_list which is displayed using view.&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
# [http://www.vldb.org/conf/1992/P353.PDF An Efficient Indexing Technique for full-text database systems] Justin Jobel, Alistair Moffet, Ron Sacks Davis&lt;br /&gt;
# [http://www.n3labs.com/pdf/putz91using-inverted-DBMS.pdf Using a relational database for an Inverted Text index] Steve Putz, Xerox PARC&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=59152</id>
		<title>Places:Full Text Indexing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=59152"/>
		<updated>2007-06-11T05:26:47Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: /* References */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;ToDO: Formatting, it is badly formatted. Any idea on how to effectively format code?&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
Full Text Indexing feature will allow user to search for a word/phrase from the pages that he has visited. The search query will be tightly integrated with Places&#039;s nsNavHistoryService. The tighter integration will allow queries like &amp;quot;search for pages visited between 01/05/07(dd/mm/yy) to 20/05/07(dd/mm/yy) containing the word &#039;places&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Design Descision ==&lt;br /&gt;
&lt;br /&gt;
A number of options were looked into before proposing this design. The options included implementing using CLucene(like flock), SQLite&#039;s FTS1 and FTS2 module, implementation using B+ Trees, using relational database etc.. The following text will briefly describe the advantage and disadvantage of all the implementation methods.&lt;br /&gt;
&lt;br /&gt;
CLucene is a full-text indexing engine that stores the index as B+ Trees in files. It uses a very efficient method for storage and retrieval. It has an excellent support for CJK languages. The Places system is a new incorporation into firefox. Hence, it is important that during its initial stages all the code that is written or used is flexible, small and tightly integrated with it. Tighter integration would allow future enhancements specific to firefox. Hence this approach was dropped.&lt;br /&gt;
&lt;br /&gt;
SQLite&#039;s FTS1 and FTS2 module are open source implementation of full-text indexing integrated with SQLite. FTS1 and FTS2 stores the text in B+ Trees and the access to the full-text index is using SQL. However, there are number of short-comings for our usage. FTS2 is still in stage where the API and data might change without backward compatibility. FTS1 does not have custom tokenizer which means no CJK support. Also, FTS1 stores the entire page duplicating what is aleady stored in the cache.&lt;br /&gt;
&lt;br /&gt;
A custom implementation using B+ Tree is a very good option but however, it would require additional B+ Tree engine. In light of availability of an efficient algorithm for implementing full-text indexing using relational database, this method is used.&lt;br /&gt;
&lt;br /&gt;
A naive implementation of full-text indexing is very costly in terms of storage. I&#039;ll briefly explain how it is so. Let us define term. A term is any word that appear in a page. So a relational database contains a table with two columns, term and id. Another table contains two columsn term id and doc id(id of the document the term appeard in). The GNU manuals were analyzed [1]. It is 5.15 Mb of text containing 958,774 occurrences of word out of which 27,554 are unique. But table 2 will require that every occurrence has a corresponding doc id. If term id were stored as int, the amount of space required to store the first column alone, would be 958,774 * 4 bytes, which is about 3 Mb. A B+ Tree implementation is atleast 3Mb more efficient. However a nice encoding scheme and storage model proposed by [2] is almost as efficient as a B+ Tree implementation. This algorithm also leverages the capabilites of relational database system while not losing too much in terms of storage and performance. Hence I propose to implement this algorithm.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Use Case ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: User&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Visit Page&amp;lt;br&amp;gt;&lt;br /&gt;
- Search&amp;lt;br&amp;gt;&lt;br /&gt;
- Clear History&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: Browser&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Expire Page&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The use cases above will be used to validate the design.&lt;br /&gt;
&lt;br /&gt;
== Database Design ==&lt;br /&gt;
&lt;br /&gt;
TODO: Check the url_table and put it here. The url_table acts as the document table. The url table will contain additionally the document length&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; &lt;br /&gt;
|+&#039;&#039;&#039;Word Table&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!columnn!!type!!bytes!!description&lt;br /&gt;
|-&lt;br /&gt;
|word||varchar||&amp;lt;=100||term for indexing(Shouldn&#039;t it be unicode? how do i store unicode?)&lt;br /&gt;
|-&lt;br /&gt;
|wordnum||integer||4||unique id. Integer works cause the number of unique words will be atmost a million. Non-english language?&lt;br /&gt;
|-&lt;br /&gt;
|doc_count||integer||4||number of documents the word occurred in&lt;br /&gt;
|-&lt;br /&gt;
|word_count||integer||4||number of occurrences of the word&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; &lt;br /&gt;
|+&#039;&#039;&#039;Posting Table&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!column!!type!!bytes!!description&lt;br /&gt;
|-&lt;br /&gt;
|wordnum||integer||4||This is the foreign key matching that in the word table&lt;br /&gt;
|-&lt;br /&gt;
|firstdoc||integer||4||lowest doc id referenced in the block&lt;br /&gt;
|-&lt;br /&gt;
|flags||tinyint||1||indicates the block type, length of doc list, sequence number&lt;br /&gt;
|-&lt;br /&gt;
|block||varbinary||&amp;lt;=255||contains encoded document and/or position postings for the word&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To Do&lt;br /&gt;
# We might need a table or two more for ranking efficiently&lt;br /&gt;
# Check if SQLite has varbinary datatype. There is a BLOB data type, I am sure.&lt;br /&gt;
&lt;br /&gt;
Note that the tables structure is subject to change to improve efficiency. New Tables might be formed and/or the table might add/remove column&lt;br /&gt;
&lt;br /&gt;
== Detailed Design ==&lt;br /&gt;
&lt;br /&gt;
Classes&lt;br /&gt;
&lt;br /&gt;
There are essentially four classes for the back-end:&lt;br /&gt;
# nsNavHistoryQuery&lt;br /&gt;
# nsNavFullTextIndexHelper&lt;br /&gt;
# nsNavFullTextIndex&lt;br /&gt;
# nsNavFullTextAnalyzer&lt;br /&gt;
&lt;br /&gt;
===nsNavHistoryQuery===&lt;br /&gt;
&lt;br /&gt;
This class is already implemented with all features except searching text. The mechanism of searching is no different from what described in http://developer.mozilla.org/en/docs/Places:Query_System. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var historyService = Components.classes[&amp;quot;@mozilla.org/browser/nav-history-service;1&amp;quot;].getService(Components.interfaces.nsINavHistoryService);&lt;br /&gt;
&lt;br /&gt;
var options = historyService.getNewQueryOptions();&lt;br /&gt;
var query = historyService.getNewQuery();&lt;br /&gt;
query.searchTerms = &amp;quot;Mozilla Firefox&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// execute the query&lt;br /&gt;
var result = historyService.executeQuery(query, options);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result will contain a list of URI. A number of options can be specified in query and options making it very powerful. Conjunctive queries can be also be executed with historyService.executeQueries and a list of query as parameter.&lt;br /&gt;
&lt;br /&gt;
Internally the function calls nsNavFullTextIndex::searchDocument(searchTerms)  which returns a list of URI ranked according to algorithm described in  SearchDocument(terms) function that will be described later in this document. The list of URI is further filtered by the other parameters set in query and options variable. In case of executeQueries method, the list is aggregated with results from multiple queries.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndex===&lt;br /&gt;
&lt;br /&gt;
This class interacts with SQLite database. This implements the algorithm for adding document to the index, removing document from the index and search for a given term. This search function is used by nsNavHistoryQuery::search function. Look at [2] for the algorithm used.&lt;br /&gt;
&lt;br /&gt;
Block is a struct used to encode and decode block. A block is variable length delta encoded. Variable Length delta Encoding, compresses very efficiently balancing speed and storage requirement.&lt;br /&gt;
&amp;lt;pre&amp;gt;struct Block {&lt;br /&gt;
	//The width of the field is 255 bytes. The return value is an int indicating number of elements in the data that were encoded in the out byte array.&lt;br /&gt;
	int encode(in int[] data, out byte[] encodedBlock) {&lt;br /&gt;
		//How to encode more efficiently, any idea?&lt;br /&gt;
		int[] bigEndian;&lt;br /&gt;
		int k = 0;&lt;br /&gt;
		data[i - 1] = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; data.length; i++) {&lt;br /&gt;
			data[i] -= data[i - 1];&lt;br /&gt;
			int j = 0;&lt;br /&gt;
			while(data[i] != 0) {&lt;br /&gt;
				bigEndian[j++] = data[i] % 128;&lt;br /&gt;
				data[i] /= 128;&lt;br /&gt;
			}&lt;br /&gt;
			for( ; j &amp;gt; 0; j--, k++) {&lt;br /&gt;
				encodedBlock[k] = (1 &amp;lt;&amp;lt; 8) &amp;amp; bigEndian[j];&lt;br /&gt;
			}&lt;br /&gt;
			encodedBlock[k++] = bigEndian[0];&lt;br /&gt;
			if (k &amp;gt; 255) &lt;br /&gt;
				return i - 1&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	void decode(in byte[] encodedBlock, out int[] data) {&lt;br /&gt;
		int j = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; encodedBlock.length; i++) {&lt;br /&gt;
			if (encodedBlock[i] &amp;amp;&amp;amp; (1 &amp;lt;&amp;lt; 8)) {&lt;br /&gt;
				data[j] *= 128;&lt;br /&gt;
				data[j] += encodedBlock[i] &amp;amp; ((1 &amp;lt;&amp;lt; 8) - 1);&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				data[j] += data[j - 1]; //Because it was delta encoded&lt;br /&gt;
				data[j++] = encodedBlock[i];&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
AddDocument(connection, document, analyzer) {&lt;br /&gt;
	AddDocument in two pass. Scan the document for terms and then invert. We have the doc id. We will require a hash map library to make it very efficient. Term is a hash map. Usage: term[&#039;termname&#039;] = array of pos.&lt;br /&gt;
&amp;lt;pre&amp;gt;	while(analyzer.hasTerms()) {&lt;br /&gt;
		cTerm = analyzer.nextTerm();&lt;br /&gt;
		term[cTerm.Name].add(cTerm.pos);&lt;br /&gt;
	}&lt;br /&gt;
	iterator i = term.iterator();&lt;br /&gt;
	while(i.hasNext()) {&lt;br /&gt;
		termName = i.next();&lt;br /&gt;
		termPos[] = term[termName];&lt;br /&gt;
	&lt;br /&gt;
		//hopefully sqlite caches query results, it will be inefficient otherwise.&lt;br /&gt;
&lt;br /&gt;
		if (term already in table) { &lt;br /&gt;
			record = executeQuery(&amp;quot;SELECT ﬁrstdoc, ﬂags, block FROM postings&lt;br /&gt;
				WHERE word = termName&lt;br /&gt;
				AND ﬁrstdoc == (Given as &amp;gt;= in [2], == is correct in my opinion)&lt;br /&gt;
				   (SELECT max (ﬁrstdoc) FROM postings&lt;br /&gt;
				    WHERE word = termName and flags &amp;lt; 128)&amp;quot;);&lt;br /&gt;
			//Refer to [2] for explanation of this.&lt;br /&gt;
			//only one record is retrieved with flags == 0 or flags between 0 and 128&lt;br /&gt;
			//when flag == 0, the block contains only document list&lt;br /&gt;
			if (flag == 0) {&lt;br /&gt;
				1) Decode the block&lt;br /&gt;
				2) See if one more document can be fitted in this.&lt;br /&gt;
				3) Yes? add to it&lt;br /&gt;
					i) find the position list&lt;br /&gt;
						positionList = executeQuery(&amp;quot;SELECT firstdoc, flags, block FROM positings&lt;br /&gt;
							where firstdoc = &lt;br /&gt;
								SELECT max(ﬁrstdoc) FROM postings&lt;br /&gt;
									WHERE word = termName&lt;br /&gt;
									AND ﬁrstdoc &amp;gt;= record.firstdoc AND flags &amp;gt;= 128&lt;br /&gt;
					ii) Try to add as many position in this block&lt;br /&gt;
					iii) when the block is done, create a new row, with firstdoc == currentdoc &lt;br /&gt;
						and flags == 128 or 129 depending on whether the prev firstdoc was same as this.&lt;br /&gt;
&lt;br /&gt;
					iv) goto ii) if there are more left.&lt;br /&gt;
				4) no?&lt;br /&gt;
					i) create a new row with firstdoc = docid, flags=2&lt;br /&gt;
					ii) To the block  add, doc id and doc freq. And all the pos. Note the position listings are never split when flag == 2.&lt;br /&gt;
						We must try to fit all the position listing in this block.99% of the case this should be possible. Make a small calculation, you&#039;ll find out i am correct&lt;br /&gt;
					iii) The rare case it is not possible? create two rows one with flags==0 and flags==128&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				//This is slightly complex in that there is both document list and position list in the same block. We have to decode the block. Try to add document id and and all the position to the position list. This might not be possible. And we&#039;ll have to create two new rows, one with flags == 0 and other with flags == 128&lt;br /&gt;
			}&lt;br /&gt;
			update the word count in the word list table appropriately&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	commit to the database&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveDocument() {&lt;br /&gt;
	Inherently inefficient because of the structure that we have adopted. Needs to be optimized. The general algorithm revolves around finding the record whose firstDoc is immediately less or same as the docId we are searching for.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;	e.g. Say we want to delete docid = 20. We got two records&lt;br /&gt;
	firstDoc = 18, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	firstDoc = 22, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	So we select the record with docid = 18 that is immediately before docid = 20;&lt;br /&gt;
	&lt;br /&gt;
	query to achieve this: SELECT word, firstdoc, block FROM postings where&lt;br /&gt;
				firstdoc = SELECT max(firstdoc) FROM postings where&lt;br /&gt;
						firstdoc &amp;lt; docIdWeAreSearchingFor&lt;br /&gt;
	This returns a number of records with flag == 0 or 0 &amp;lt; flag &amp;lt; 128 or flag == 128 or flag &amp;gt; 128. &lt;br /&gt;
	for each record we found do the following:&lt;br /&gt;
		docAndPostingTable = decodeBlock(block);&lt;br /&gt;
		we have just decoded the block using Block::decodeBlock(). &lt;br /&gt;
		docAndPostingTable.find(docId) //we check the decode block if it contains docId of our interest. &lt;br /&gt;
		if docId found&lt;br /&gt;
			Check the flag&lt;br /&gt;
			when flag == 0 //only document list&lt;br /&gt;
				remove the document and freq from the block&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				if docId == firstDoc update firstDoc with the immediately following doc&lt;br /&gt;
				if no more docs left in this row, delete the row&lt;br /&gt;
			when 0 &amp;lt; flag &amp;lt; 128, contains both document list and posting table&lt;br /&gt;
				remove the document from the block.&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				remove all the postings of the document for the term in the block&lt;br /&gt;
				if (docId == firstDoc) update firstDoc with immediately following doc&lt;br /&gt;
				if no more docs left in the row, delete the row&lt;br /&gt;
			when flag &amp;gt;= 128 //only postings table&lt;br /&gt;
				remove all the postings corresponding to the doc&lt;br /&gt;
				update the firstDoc for the record&lt;br /&gt;
				delete the record if block is empty&lt;br /&gt;
		&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SearchDocument(terms) {&lt;br /&gt;
	terms is something like: &amp;quot;mac apple panther&amp;quot;. Basically a collection of terms&lt;br /&gt;
	The ranking algorithm as described in this document http://lucene.apache.org/java/2_1_0/api/org/apache/lucene/search/Similarity.html is used.&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndexHelper===&lt;br /&gt;
&lt;br /&gt;
This class contains mechanism for scheduling documents for indexing. It works pretty much like nsNavHistoryExpire. The scheduling balances indexing with overall firefox performance. It has a timer which when expires picks an unindexed document from the history and calls nsNavFullTextIndex::addDocument to index. When and how the timer is set, dictates the overall efficiency. Whenever a user visits a page, the timer is triggered. &lt;br /&gt;
&lt;br /&gt;
When user visits a page, the history service calls OnAddURI function of this class. This function starts the timer. When timer expires the call back function is called. The function checks If there are any more document to be indexed and it has not been a long while since the user called addURI. If so, then it resets the timer and waits to expire again.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextAnalyzer===&lt;br /&gt;
&lt;br /&gt;
The analyzer design is similar to the way Lucene works. The Lucene design enables support for multiple languages.&lt;br /&gt;
The Analyzer takes the tokens generated from the tokenizer and discards those that are not required for indexing. E.g. is, a, an the can be discarded. This is enabled by by tokenStream classes. Refer to nsNavFullTextTokenStream and Lucene API for more detail.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextTokenStream===&lt;br /&gt;
&lt;br /&gt;
TokenStream is an abstract class with three methods next, reset and close. The next method returns a token. Token is a class representing startoffset, endoffset and termtext. TokenStream needs input to tokenize. There are two concrete class for TokenStream:&lt;br /&gt;
# Tokenizer: The input is an inputstream.&lt;br /&gt;
# TokenFilter: The input is another TokenStream. This works like pipes.&lt;br /&gt;
&lt;br /&gt;
== Front-End ==&lt;br /&gt;
1) nsNavQueryParser&lt;br /&gt;
The function of this is to break the query given in human language into a graph of TermQuery and BooleanQuery. BooleanQuery is a struct with two operand(each a TermQuery or BooleanQuery) and an operator(AND, OR, NOT, XOR). Although the idea here is to implement all kind of queries as in: http://lucene.apache.org/java/docs/queryparsersyntax.html eventually. nsNavHistoryQuery is used to query using the query struct. The results of which is url_list which is displayed using view.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
# [http://www.vldb.org/conf/1992/P353.PDF An Efficient Indexing Technique for full-text database systems] Justin Jobel, Alistair Moffet, Ron Sacks Davis&lt;br /&gt;
# [http://www.n3labs.com/pdf/putz91using-inverted-DBMS.pdf Using a relational database for an Inverted Text index] Steve Putz, Xerox PARC&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=59118</id>
		<title>Places:Full Text Indexing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=59118"/>
		<updated>2007-06-10T10:29:43Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: /* nsNavHistoryQuery */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;ToDO: Formatting, it is badly formatted. Any idea on how to effectively format code?&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
Full Text Indexing feature will allow user to search for a word/phrase from the pages that he has visited. The search query will be tightly integrated with Places&#039;s nsNavHistoryService. The tighter integration will allow queries like &amp;quot;search for pages visited between 01/05/07(dd/mm/yy) to 20/05/07(dd/mm/yy) containing the word &#039;places&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Design Descision ==&lt;br /&gt;
&lt;br /&gt;
A number of options were looked into before proposing this design. The options included implementing using CLucene(like flock), SQLite&#039;s FTS1 and FTS2 module, implementation using B+ Trees, using relational database etc.. The following text will briefly describe the advantage and disadvantage of all the implementation methods.&lt;br /&gt;
&lt;br /&gt;
CLucene is a full-text indexing engine that stores the index as B+ Trees in files. It uses a very efficient method for storage and retrieval. It has an excellent support for CJK languages. The Places system is a new incorporation into firefox. Hence, it is important that during its initial stages all the code that is written or used is flexible, small and tightly integrated with it. Tighter integration would allow future enhancements specific to firefox. Hence this approach was dropped.&lt;br /&gt;
&lt;br /&gt;
SQLite&#039;s FTS1 and FTS2 module are open source implementation of full-text indexing integrated with SQLite. FTS1 and FTS2 stores the text in B+ Trees and the access to the full-text index is using SQL. However, there are number of short-comings for our usage. FTS2 is still in stage where the API and data might change without backward compatibility. FTS1 does not have custom tokenizer which means no CJK support. Also, FTS1 stores the entire page duplicating what is aleady stored in the cache.&lt;br /&gt;
&lt;br /&gt;
A custom implementation using B+ Tree is a very good option but however, it would require additional B+ Tree engine. In light of availability of an efficient algorithm for implementing full-text indexing using relational database, this method is used.&lt;br /&gt;
&lt;br /&gt;
A naive implementation of full-text indexing is very costly in terms of storage. I&#039;ll briefly explain how it is so. Let us define term. A term is any word that appear in a page. So a relational database contains a table with two columns, term and id. Another table contains two columsn term id and doc id(id of the document the term appeard in). The GNU manuals were analyzed [1]. It is 5.15 Mb of text containing 958,774 occurrences of word out of which 27,554 are unique. But table 2 will require that every occurrence has a corresponding doc id. If term id were stored as int, the amount of space required to store the first column alone, would be 958,774 * 4 bytes, which is about 3 Mb. A B+ Tree implementation is atleast 3Mb more efficient. However a nice encoding scheme and storage model proposed by [2] is almost as efficient as a B+ Tree implementation. This algorithm also leverages the capabilites of relational database system while not losing too much in terms of storage and performance. Hence I propose to implement this algorithm.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Use Case ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: User&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Visit Page&amp;lt;br&amp;gt;&lt;br /&gt;
- Search&amp;lt;br&amp;gt;&lt;br /&gt;
- Clear History&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: Browser&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Expire Page&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The use cases above will be used to validate the design.&lt;br /&gt;
&lt;br /&gt;
== Database Design ==&lt;br /&gt;
&lt;br /&gt;
TODO: Check the url_table and put it here. The url_table acts as the document table. The url table will contain additionally the document length&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; &lt;br /&gt;
|+&#039;&#039;&#039;Word Table&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!columnn!!type!!bytes!!description&lt;br /&gt;
|-&lt;br /&gt;
|word||varchar||&amp;lt;=100||term for indexing(Shouldn&#039;t it be unicode? how do i store unicode?)&lt;br /&gt;
|-&lt;br /&gt;
|wordnum||integer||4||unique id. Integer works cause the number of unique words will be atmost a million. Non-english language?&lt;br /&gt;
|-&lt;br /&gt;
|doc_count||integer||4||number of documents the word occurred in&lt;br /&gt;
|-&lt;br /&gt;
|word_count||integer||4||number of occurrences of the word&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; &lt;br /&gt;
|+&#039;&#039;&#039;Posting Table&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!column!!type!!bytes!!description&lt;br /&gt;
|-&lt;br /&gt;
|wordnum||integer||4||This is the foreign key matching that in the word table&lt;br /&gt;
|-&lt;br /&gt;
|firstdoc||integer||4||lowest doc id referenced in the block&lt;br /&gt;
|-&lt;br /&gt;
|flags||tinyint||1||indicates the block type, length of doc list, sequence number&lt;br /&gt;
|-&lt;br /&gt;
|block||varbinary||&amp;lt;=255||contains encoded document and/or position postings for the word&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To Do&lt;br /&gt;
# We might need a table or two more for ranking efficiently&lt;br /&gt;
# Check if SQLite has varbinary datatype. There is a BLOB data type, I am sure.&lt;br /&gt;
&lt;br /&gt;
Note that the tables structure is subject to change to improve efficiency. New Tables might be formed and/or the table might add/remove column&lt;br /&gt;
&lt;br /&gt;
== Detailed Design ==&lt;br /&gt;
&lt;br /&gt;
Classes&lt;br /&gt;
&lt;br /&gt;
There are essentially four classes for the back-end:&lt;br /&gt;
# nsNavHistoryQuery&lt;br /&gt;
# nsNavFullTextIndexHelper&lt;br /&gt;
# nsNavFullTextIndex&lt;br /&gt;
# nsNavFullTextAnalyzer&lt;br /&gt;
&lt;br /&gt;
===nsNavHistoryQuery===&lt;br /&gt;
&lt;br /&gt;
This class is already implemented with all features except searching text. The mechanism of searching is no different from what described in http://developer.mozilla.org/en/docs/Places:Query_System. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var historyService = Components.classes[&amp;quot;@mozilla.org/browser/nav-history-service;1&amp;quot;].getService(Components.interfaces.nsINavHistoryService);&lt;br /&gt;
&lt;br /&gt;
var options = historyService.getNewQueryOptions();&lt;br /&gt;
var query = historyService.getNewQuery();&lt;br /&gt;
query.searchTerms = &amp;quot;Mozilla Firefox&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// execute the query&lt;br /&gt;
var result = historyService.executeQuery(query, options);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result will contain a list of URI. A number of options can be specified in query and options making it very powerful. Conjunctive queries can be also be executed with historyService.executeQueries and a list of query as parameter.&lt;br /&gt;
&lt;br /&gt;
Internally the function calls nsNavFullTextIndex::searchDocument(searchTerms)  which returns a list of URI ranked according to algorithm described in  SearchDocument(terms) function that will be described later in this document. The list of URI is further filtered by the other parameters set in query and options variable. In case of executeQueries method, the list is aggregated with results from multiple queries.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndex===&lt;br /&gt;
&lt;br /&gt;
This class interacts with SQLite database. This implements the algorithm for adding document to the index, removing document from the index and search for a given term. This search function is used by nsNavHistoryQuery::search function. Look at [2] for the algorithm used.&lt;br /&gt;
&lt;br /&gt;
Block is a struct used to encode and decode block. A block is variable length delta encoded. Variable Length delta Encoding, compresses very efficiently balancing speed and storage requirement.&lt;br /&gt;
&amp;lt;pre&amp;gt;struct Block {&lt;br /&gt;
	//The width of the field is 255 bytes. The return value is an int indicating number of elements in the data that were encoded in the out byte array.&lt;br /&gt;
	int encode(in int[] data, out byte[] encodedBlock) {&lt;br /&gt;
		//How to encode more efficiently, any idea?&lt;br /&gt;
		int[] bigEndian;&lt;br /&gt;
		int k = 0;&lt;br /&gt;
		data[i - 1] = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; data.length; i++) {&lt;br /&gt;
			data[i] -= data[i - 1];&lt;br /&gt;
			int j = 0;&lt;br /&gt;
			while(data[i] != 0) {&lt;br /&gt;
				bigEndian[j++] = data[i] % 128;&lt;br /&gt;
				data[i] /= 128;&lt;br /&gt;
			}&lt;br /&gt;
			for( ; j &amp;gt; 0; j--, k++) {&lt;br /&gt;
				encodedBlock[k] = (1 &amp;lt;&amp;lt; 8) &amp;amp; bigEndian[j];&lt;br /&gt;
			}&lt;br /&gt;
			encodedBlock[k++] = bigEndian[0];&lt;br /&gt;
			if (k &amp;gt; 255) &lt;br /&gt;
				return i - 1&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	void decode(in byte[] encodedBlock, out int[] data) {&lt;br /&gt;
		int j = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; encodedBlock.length; i++) {&lt;br /&gt;
			if (encodedBlock[i] &amp;amp;&amp;amp; (1 &amp;lt;&amp;lt; 8)) {&lt;br /&gt;
				data[j] *= 128;&lt;br /&gt;
				data[j] += encodedBlock[i] &amp;amp; ((1 &amp;lt;&amp;lt; 8) - 1);&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				data[j] += data[j - 1]; //Because it was delta encoded&lt;br /&gt;
				data[j++] = encodedBlock[i];&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
AddDocument(connection, document, analyzer) {&lt;br /&gt;
	AddDocument in two pass. Scan the document for terms and then invert. We have the doc id. We will require a hash map library to make it very efficient. Term is a hash map. Usage: term[&#039;termname&#039;] = array of pos.&lt;br /&gt;
&amp;lt;pre&amp;gt;	while(analyzer.hasTerms()) {&lt;br /&gt;
		cTerm = analyzer.nextTerm();&lt;br /&gt;
		term[cTerm.Name].add(cTerm.pos);&lt;br /&gt;
	}&lt;br /&gt;
	iterator i = term.iterator();&lt;br /&gt;
	while(i.hasNext()) {&lt;br /&gt;
		termName = i.next();&lt;br /&gt;
		termPos[] = term[termName];&lt;br /&gt;
	&lt;br /&gt;
		//hopefully sqlite caches query results, it will be inefficient otherwise.&lt;br /&gt;
&lt;br /&gt;
		if (term already in table) { &lt;br /&gt;
			record = executeQuery(&amp;quot;SELECT ﬁrstdoc, ﬂags, block FROM postings&lt;br /&gt;
				WHERE word = termName&lt;br /&gt;
				AND ﬁrstdoc == (Given as &amp;gt;= in [2], == is correct in my opinion)&lt;br /&gt;
				   (SELECT max (ﬁrstdoc) FROM postings&lt;br /&gt;
				    WHERE word = termName and flags &amp;lt; 128)&amp;quot;);&lt;br /&gt;
			//Refer to [2] for explanation of this.&lt;br /&gt;
			//only one record is retrieved with flags == 0 or flags between 0 and 128&lt;br /&gt;
			//when flag == 0, the block contains only document list&lt;br /&gt;
			if (flag == 0) {&lt;br /&gt;
				1) Decode the block&lt;br /&gt;
				2) See if one more document can be fitted in this.&lt;br /&gt;
				3) Yes? add to it&lt;br /&gt;
					i) find the position list&lt;br /&gt;
						positionList = executeQuery(&amp;quot;SELECT firstdoc, flags, block FROM positings&lt;br /&gt;
							where firstdoc = &lt;br /&gt;
								SELECT max(ﬁrstdoc) FROM postings&lt;br /&gt;
									WHERE word = termName&lt;br /&gt;
									AND ﬁrstdoc &amp;gt;= record.firstdoc AND flags &amp;gt;= 128&lt;br /&gt;
					ii) Try to add as many position in this block&lt;br /&gt;
					iii) when the block is done, create a new row, with firstdoc == currentdoc &lt;br /&gt;
						and flags == 128 or 129 depending on whether the prev firstdoc was same as this.&lt;br /&gt;
&lt;br /&gt;
					iv) goto ii) if there are more left.&lt;br /&gt;
				4) no?&lt;br /&gt;
					i) create a new row with firstdoc = docid, flags=2&lt;br /&gt;
					ii) To the block  add, doc id and doc freq. And all the pos. Note the position listings are never split when flag == 2.&lt;br /&gt;
						We must try to fit all the position listing in this block.99% of the case this should be possible. Make a small calculation, you&#039;ll find out i am correct&lt;br /&gt;
					iii) The rare case it is not possible? create two rows one with flags==0 and flags==128&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				//This is slightly complex in that there is both document list and position list in the same block. We have to decode the block. Try to add document id and and all the position to the position list. This might not be possible. And we&#039;ll have to create two new rows, one with flags == 0 and other with flags == 128&lt;br /&gt;
			}&lt;br /&gt;
			update the word count in the word list table appropriately&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	commit to the database&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveDocument() {&lt;br /&gt;
	Inherently inefficient because of the structure that we have adopted. Needs to be optimized. The general algorithm revolves around finding the record whose firstDoc is immediately less or same as the docId we are searching for.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;	e.g. Say we want to delete docid = 20. We got two records&lt;br /&gt;
	firstDoc = 18, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	firstDoc = 22, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	So we select the record with docid = 18 that is immediately before docid = 20;&lt;br /&gt;
	&lt;br /&gt;
	query to achieve this: SELECT word, firstdoc, block FROM postings where&lt;br /&gt;
				firstdoc = SELECT max(firstdoc) FROM postings where&lt;br /&gt;
						firstdoc &amp;lt; docIdWeAreSearchingFor&lt;br /&gt;
	This returns a number of records with flag == 0 or 0 &amp;lt; flag &amp;lt; 128 or flag == 128 or flag &amp;gt; 128. &lt;br /&gt;
	for each record we found do the following:&lt;br /&gt;
		docAndPostingTable = decodeBlock(block);&lt;br /&gt;
		we have just decoded the block using Block::decodeBlock(). &lt;br /&gt;
		docAndPostingTable.find(docId) //we check the decode block if it contains docId of our interest. &lt;br /&gt;
		if docId found&lt;br /&gt;
			Check the flag&lt;br /&gt;
			when flag == 0 //only document list&lt;br /&gt;
				remove the document and freq from the block&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				if docId == firstDoc update firstDoc with the immediately following doc&lt;br /&gt;
				if no more docs left in this row, delete the row&lt;br /&gt;
			when 0 &amp;lt; flag &amp;lt; 128, contains both document list and posting table&lt;br /&gt;
				remove the document from the block.&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				remove all the postings of the document for the term in the block&lt;br /&gt;
				if (docId == firstDoc) update firstDoc with immediately following doc&lt;br /&gt;
				if no more docs left in the row, delete the row&lt;br /&gt;
			when flag &amp;gt;= 128 //only postings table&lt;br /&gt;
				remove all the postings corresponding to the doc&lt;br /&gt;
				update the firstDoc for the record&lt;br /&gt;
				delete the record if block is empty&lt;br /&gt;
		&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SearchDocument(terms) {&lt;br /&gt;
	terms is something like: &amp;quot;mac apple panther&amp;quot;. Basically a collection of terms&lt;br /&gt;
	The ranking algorithm as described in this document http://lucene.apache.org/java/2_1_0/api/org/apache/lucene/search/Similarity.html is used.&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndexHelper===&lt;br /&gt;
&lt;br /&gt;
This class contains mechanism for scheduling documents for indexing. It works pretty much like nsNavHistoryExpire. The scheduling balances indexing with overall firefox performance. It has a timer which when expires picks an unindexed document from the history and calls nsNavFullTextIndex::addDocument to index. When and how the timer is set, dictates the overall efficiency. Whenever a user visits a page, the timer is triggered. &lt;br /&gt;
&lt;br /&gt;
When user visits a page, the history service calls OnAddURI function of this class. This function starts the timer. When timer expires the call back function is called. The function checks If there are any more document to be indexed and it has not been a long while since the user called addURI. If so, then it resets the timer and waits to expire again.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextAnalyzer===&lt;br /&gt;
&lt;br /&gt;
The analyzer design is similar to the way Lucene works. The Lucene design enables support for multiple languages.&lt;br /&gt;
The Analyzer takes the tokens generated from the tokenizer and discards those that are not required for indexing. E.g. is, a, an the can be discarded. This is enabled by by tokenStream classes. Refer to nsNavFullTextTokenStream and Lucene API for more detail.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextTokenStream===&lt;br /&gt;
&lt;br /&gt;
TokenStream is an abstract class with three methods next, reset and close. The next method returns a token. Token is a class representing startoffset, endoffset and termtext. TokenStream needs input to tokenize. There are two concrete class for TokenStream:&lt;br /&gt;
# Tokenizer: The input is an inputstream.&lt;br /&gt;
# TokenFilter: The input is another TokenStream. This works like pipes.&lt;br /&gt;
&lt;br /&gt;
== Front-End ==&lt;br /&gt;
1) nsNavQueryParser&lt;br /&gt;
The function of this is to break the query given in human language into a graph of TermQuery and BooleanQuery. BooleanQuery is a struct with two operand(each a TermQuery or BooleanQuery) and an operator(AND, OR, NOT, XOR). Although the idea here is to implement all kind of queries as in: http://lucene.apache.org/java/docs/queryparsersyntax.html eventually. nsNavHistoryQuery is used to query using the query struct. The results of which is url_list which is displayed using view.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
[1] An Efficient Indexing Technique for full-text database systems: Justin Jobel, Alistair Moffet, Ron Sacks Davis&lt;br /&gt;
[2]Using a relational database for full-text index, Steve Putz, Xerox PARC&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=59117</id>
		<title>Places:Full Text Indexing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=59117"/>
		<updated>2007-06-10T10:29:00Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: /* nsNavHistoryQuery */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;ToDO: Formatting, it is badly formatted. Any idea on how to effectively format code?&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
Full Text Indexing feature will allow user to search for a word/phrase from the pages that he has visited. The search query will be tightly integrated with Places&#039;s nsNavHistoryService. The tighter integration will allow queries like &amp;quot;search for pages visited between 01/05/07(dd/mm/yy) to 20/05/07(dd/mm/yy) containing the word &#039;places&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Design Descision ==&lt;br /&gt;
&lt;br /&gt;
A number of options were looked into before proposing this design. The options included implementing using CLucene(like flock), SQLite&#039;s FTS1 and FTS2 module, implementation using B+ Trees, using relational database etc.. The following text will briefly describe the advantage and disadvantage of all the implementation methods.&lt;br /&gt;
&lt;br /&gt;
CLucene is a full-text indexing engine that stores the index as B+ Trees in files. It uses a very efficient method for storage and retrieval. It has an excellent support for CJK languages. The Places system is a new incorporation into firefox. Hence, it is important that during its initial stages all the code that is written or used is flexible, small and tightly integrated with it. Tighter integration would allow future enhancements specific to firefox. Hence this approach was dropped.&lt;br /&gt;
&lt;br /&gt;
SQLite&#039;s FTS1 and FTS2 module are open source implementation of full-text indexing integrated with SQLite. FTS1 and FTS2 stores the text in B+ Trees and the access to the full-text index is using SQL. However, there are number of short-comings for our usage. FTS2 is still in stage where the API and data might change without backward compatibility. FTS1 does not have custom tokenizer which means no CJK support. Also, FTS1 stores the entire page duplicating what is aleady stored in the cache.&lt;br /&gt;
&lt;br /&gt;
A custom implementation using B+ Tree is a very good option but however, it would require additional B+ Tree engine. In light of availability of an efficient algorithm for implementing full-text indexing using relational database, this method is used.&lt;br /&gt;
&lt;br /&gt;
A naive implementation of full-text indexing is very costly in terms of storage. I&#039;ll briefly explain how it is so. Let us define term. A term is any word that appear in a page. So a relational database contains a table with two columns, term and id. Another table contains two columsn term id and doc id(id of the document the term appeard in). The GNU manuals were analyzed [1]. It is 5.15 Mb of text containing 958,774 occurrences of word out of which 27,554 are unique. But table 2 will require that every occurrence has a corresponding doc id. If term id were stored as int, the amount of space required to store the first column alone, would be 958,774 * 4 bytes, which is about 3 Mb. A B+ Tree implementation is atleast 3Mb more efficient. However a nice encoding scheme and storage model proposed by [2] is almost as efficient as a B+ Tree implementation. This algorithm also leverages the capabilites of relational database system while not losing too much in terms of storage and performance. Hence I propose to implement this algorithm.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Use Case ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: User&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Visit Page&amp;lt;br&amp;gt;&lt;br /&gt;
- Search&amp;lt;br&amp;gt;&lt;br /&gt;
- Clear History&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: Browser&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Expire Page&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The use cases above will be used to validate the design.&lt;br /&gt;
&lt;br /&gt;
== Database Design ==&lt;br /&gt;
&lt;br /&gt;
TODO: Check the url_table and put it here. The url_table acts as the document table. The url table will contain additionally the document length&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; &lt;br /&gt;
|+&#039;&#039;&#039;Word Table&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!columnn!!type!!bytes!!description&lt;br /&gt;
|-&lt;br /&gt;
|word||varchar||&amp;lt;=100||term for indexing(Shouldn&#039;t it be unicode? how do i store unicode?)&lt;br /&gt;
|-&lt;br /&gt;
|wordnum||integer||4||unique id. Integer works cause the number of unique words will be atmost a million. Non-english language?&lt;br /&gt;
|-&lt;br /&gt;
|doc_count||integer||4||number of documents the word occurred in&lt;br /&gt;
|-&lt;br /&gt;
|word_count||integer||4||number of occurrences of the word&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; &lt;br /&gt;
|+&#039;&#039;&#039;Posting Table&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!column!!type!!bytes!!description&lt;br /&gt;
|-&lt;br /&gt;
|wordnum||integer||4||This is the foreign key matching that in the word table&lt;br /&gt;
|-&lt;br /&gt;
|firstdoc||integer||4||lowest doc id referenced in the block&lt;br /&gt;
|-&lt;br /&gt;
|flags||tinyint||1||indicates the block type, length of doc list, sequence number&lt;br /&gt;
|-&lt;br /&gt;
|block||varbinary||&amp;lt;=255||contains encoded document and/or position postings for the word&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To Do&lt;br /&gt;
# We might need a table or two more for ranking efficiently&lt;br /&gt;
# Check if SQLite has varbinary datatype. There is a BLOB data type, I am sure.&lt;br /&gt;
&lt;br /&gt;
Note that the tables structure is subject to change to improve efficiency. New Tables might be formed and/or the table might add/remove column&lt;br /&gt;
&lt;br /&gt;
== Detailed Design ==&lt;br /&gt;
&lt;br /&gt;
Classes&lt;br /&gt;
&lt;br /&gt;
There are essentially four classes for the back-end:&lt;br /&gt;
# nsNavHistoryQuery&lt;br /&gt;
# nsNavFullTextIndexHelper&lt;br /&gt;
# nsNavFullTextIndex&lt;br /&gt;
# nsNavFullTextAnalyzer&lt;br /&gt;
&lt;br /&gt;
===nsNavHistoryQuery===&lt;br /&gt;
&lt;br /&gt;
This class is already implemented with all features except searching text. The mechanism of searching is no different from what described in http://developer.mozilla.org/en/docs/Places:Query_System. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var historyService = Components.classes[&amp;quot;@mozilla.org/browser/nav-history-service;1&amp;quot;].getService(Components.interfaces.nsINavHistoryService);// no query parameters will get all history&lt;br /&gt;
&lt;br /&gt;
var options = historyService.getNewQueryOptions();&lt;br /&gt;
var query = historyService.getNewQuery();&lt;br /&gt;
query.searchTerms = &amp;quot;Mozilla Firefox&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// execute the query&lt;br /&gt;
var result = historyService.executeQuery(query, options);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result will contain a list of URI. A number of options can be specified in query and options making it very powerful. Conjunctive queries can be also be executed with historyService.executeQueries and a list of query as parameter.&lt;br /&gt;
&lt;br /&gt;
Internally the function calls nsNavFullTextIndex::searchDocument(searchTerms)  which returns a list of URI ranked according to algorithm described in  SearchDocument(terms) function that will be described later in this document. The list of URI is further filtered by the other parameters set in query and options variable. In case of executeQueries method, the list is aggregated with results from multiple queries.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndex===&lt;br /&gt;
&lt;br /&gt;
This class interacts with SQLite database. This implements the algorithm for adding document to the index, removing document from the index and search for a given term. This search function is used by nsNavHistoryQuery::search function. Look at [2] for the algorithm used.&lt;br /&gt;
&lt;br /&gt;
Block is a struct used to encode and decode block. A block is variable length delta encoded. Variable Length delta Encoding, compresses very efficiently balancing speed and storage requirement.&lt;br /&gt;
&amp;lt;pre&amp;gt;struct Block {&lt;br /&gt;
	//The width of the field is 255 bytes. The return value is an int indicating number of elements in the data that were encoded in the out byte array.&lt;br /&gt;
	int encode(in int[] data, out byte[] encodedBlock) {&lt;br /&gt;
		//How to encode more efficiently, any idea?&lt;br /&gt;
		int[] bigEndian;&lt;br /&gt;
		int k = 0;&lt;br /&gt;
		data[i - 1] = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; data.length; i++) {&lt;br /&gt;
			data[i] -= data[i - 1];&lt;br /&gt;
			int j = 0;&lt;br /&gt;
			while(data[i] != 0) {&lt;br /&gt;
				bigEndian[j++] = data[i] % 128;&lt;br /&gt;
				data[i] /= 128;&lt;br /&gt;
			}&lt;br /&gt;
			for( ; j &amp;gt; 0; j--, k++) {&lt;br /&gt;
				encodedBlock[k] = (1 &amp;lt;&amp;lt; 8) &amp;amp; bigEndian[j];&lt;br /&gt;
			}&lt;br /&gt;
			encodedBlock[k++] = bigEndian[0];&lt;br /&gt;
			if (k &amp;gt; 255) &lt;br /&gt;
				return i - 1&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	void decode(in byte[] encodedBlock, out int[] data) {&lt;br /&gt;
		int j = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; encodedBlock.length; i++) {&lt;br /&gt;
			if (encodedBlock[i] &amp;amp;&amp;amp; (1 &amp;lt;&amp;lt; 8)) {&lt;br /&gt;
				data[j] *= 128;&lt;br /&gt;
				data[j] += encodedBlock[i] &amp;amp; ((1 &amp;lt;&amp;lt; 8) - 1);&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				data[j] += data[j - 1]; //Because it was delta encoded&lt;br /&gt;
				data[j++] = encodedBlock[i];&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
AddDocument(connection, document, analyzer) {&lt;br /&gt;
	AddDocument in two pass. Scan the document for terms and then invert. We have the doc id. We will require a hash map library to make it very efficient. Term is a hash map. Usage: term[&#039;termname&#039;] = array of pos.&lt;br /&gt;
&amp;lt;pre&amp;gt;	while(analyzer.hasTerms()) {&lt;br /&gt;
		cTerm = analyzer.nextTerm();&lt;br /&gt;
		term[cTerm.Name].add(cTerm.pos);&lt;br /&gt;
	}&lt;br /&gt;
	iterator i = term.iterator();&lt;br /&gt;
	while(i.hasNext()) {&lt;br /&gt;
		termName = i.next();&lt;br /&gt;
		termPos[] = term[termName];&lt;br /&gt;
	&lt;br /&gt;
		//hopefully sqlite caches query results, it will be inefficient otherwise.&lt;br /&gt;
&lt;br /&gt;
		if (term already in table) { &lt;br /&gt;
			record = executeQuery(&amp;quot;SELECT ﬁrstdoc, ﬂags, block FROM postings&lt;br /&gt;
				WHERE word = termName&lt;br /&gt;
				AND ﬁrstdoc == (Given as &amp;gt;= in [2], == is correct in my opinion)&lt;br /&gt;
				   (SELECT max (ﬁrstdoc) FROM postings&lt;br /&gt;
				    WHERE word = termName and flags &amp;lt; 128)&amp;quot;);&lt;br /&gt;
			//Refer to [2] for explanation of this.&lt;br /&gt;
			//only one record is retrieved with flags == 0 or flags between 0 and 128&lt;br /&gt;
			//when flag == 0, the block contains only document list&lt;br /&gt;
			if (flag == 0) {&lt;br /&gt;
				1) Decode the block&lt;br /&gt;
				2) See if one more document can be fitted in this.&lt;br /&gt;
				3) Yes? add to it&lt;br /&gt;
					i) find the position list&lt;br /&gt;
						positionList = executeQuery(&amp;quot;SELECT firstdoc, flags, block FROM positings&lt;br /&gt;
							where firstdoc = &lt;br /&gt;
								SELECT max(ﬁrstdoc) FROM postings&lt;br /&gt;
									WHERE word = termName&lt;br /&gt;
									AND ﬁrstdoc &amp;gt;= record.firstdoc AND flags &amp;gt;= 128&lt;br /&gt;
					ii) Try to add as many position in this block&lt;br /&gt;
					iii) when the block is done, create a new row, with firstdoc == currentdoc &lt;br /&gt;
						and flags == 128 or 129 depending on whether the prev firstdoc was same as this.&lt;br /&gt;
&lt;br /&gt;
					iv) goto ii) if there are more left.&lt;br /&gt;
				4) no?&lt;br /&gt;
					i) create a new row with firstdoc = docid, flags=2&lt;br /&gt;
					ii) To the block  add, doc id and doc freq. And all the pos. Note the position listings are never split when flag == 2.&lt;br /&gt;
						We must try to fit all the position listing in this block.99% of the case this should be possible. Make a small calculation, you&#039;ll find out i am correct&lt;br /&gt;
					iii) The rare case it is not possible? create two rows one with flags==0 and flags==128&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				//This is slightly complex in that there is both document list and position list in the same block. We have to decode the block. Try to add document id and and all the position to the position list. This might not be possible. And we&#039;ll have to create two new rows, one with flags == 0 and other with flags == 128&lt;br /&gt;
			}&lt;br /&gt;
			update the word count in the word list table appropriately&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	commit to the database&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveDocument() {&lt;br /&gt;
	Inherently inefficient because of the structure that we have adopted. Needs to be optimized. The general algorithm revolves around finding the record whose firstDoc is immediately less or same as the docId we are searching for.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;	e.g. Say we want to delete docid = 20. We got two records&lt;br /&gt;
	firstDoc = 18, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	firstDoc = 22, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	So we select the record with docid = 18 that is immediately before docid = 20;&lt;br /&gt;
	&lt;br /&gt;
	query to achieve this: SELECT word, firstdoc, block FROM postings where&lt;br /&gt;
				firstdoc = SELECT max(firstdoc) FROM postings where&lt;br /&gt;
						firstdoc &amp;lt; docIdWeAreSearchingFor&lt;br /&gt;
	This returns a number of records with flag == 0 or 0 &amp;lt; flag &amp;lt; 128 or flag == 128 or flag &amp;gt; 128. &lt;br /&gt;
	for each record we found do the following:&lt;br /&gt;
		docAndPostingTable = decodeBlock(block);&lt;br /&gt;
		we have just decoded the block using Block::decodeBlock(). &lt;br /&gt;
		docAndPostingTable.find(docId) //we check the decode block if it contains docId of our interest. &lt;br /&gt;
		if docId found&lt;br /&gt;
			Check the flag&lt;br /&gt;
			when flag == 0 //only document list&lt;br /&gt;
				remove the document and freq from the block&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				if docId == firstDoc update firstDoc with the immediately following doc&lt;br /&gt;
				if no more docs left in this row, delete the row&lt;br /&gt;
			when 0 &amp;lt; flag &amp;lt; 128, contains both document list and posting table&lt;br /&gt;
				remove the document from the block.&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				remove all the postings of the document for the term in the block&lt;br /&gt;
				if (docId == firstDoc) update firstDoc with immediately following doc&lt;br /&gt;
				if no more docs left in the row, delete the row&lt;br /&gt;
			when flag &amp;gt;= 128 //only postings table&lt;br /&gt;
				remove all the postings corresponding to the doc&lt;br /&gt;
				update the firstDoc for the record&lt;br /&gt;
				delete the record if block is empty&lt;br /&gt;
		&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SearchDocument(terms) {&lt;br /&gt;
	terms is something like: &amp;quot;mac apple panther&amp;quot;. Basically a collection of terms&lt;br /&gt;
	The ranking algorithm as described in this document http://lucene.apache.org/java/2_1_0/api/org/apache/lucene/search/Similarity.html is used.&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndexHelper===&lt;br /&gt;
&lt;br /&gt;
This class contains mechanism for scheduling documents for indexing. It works pretty much like nsNavHistoryExpire. The scheduling balances indexing with overall firefox performance. It has a timer which when expires picks an unindexed document from the history and calls nsNavFullTextIndex::addDocument to index. When and how the timer is set, dictates the overall efficiency. Whenever a user visits a page, the timer is triggered. &lt;br /&gt;
&lt;br /&gt;
When user visits a page, the history service calls OnAddURI function of this class. This function starts the timer. When timer expires the call back function is called. The function checks If there are any more document to be indexed and it has not been a long while since the user called addURI. If so, then it resets the timer and waits to expire again.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextAnalyzer===&lt;br /&gt;
&lt;br /&gt;
The analyzer design is similar to the way Lucene works. The Lucene design enables support for multiple languages.&lt;br /&gt;
The Analyzer takes the tokens generated from the tokenizer and discards those that are not required for indexing. E.g. is, a, an the can be discarded. This is enabled by by tokenStream classes. Refer to nsNavFullTextTokenStream and Lucene API for more detail.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextTokenStream===&lt;br /&gt;
&lt;br /&gt;
TokenStream is an abstract class with three methods next, reset and close. The next method returns a token. Token is a class representing startoffset, endoffset and termtext. TokenStream needs input to tokenize. There are two concrete class for TokenStream:&lt;br /&gt;
# Tokenizer: The input is an inputstream.&lt;br /&gt;
# TokenFilter: The input is another TokenStream. This works like pipes.&lt;br /&gt;
&lt;br /&gt;
== Front-End ==&lt;br /&gt;
1) nsNavQueryParser&lt;br /&gt;
The function of this is to break the query given in human language into a graph of TermQuery and BooleanQuery. BooleanQuery is a struct with two operand(each a TermQuery or BooleanQuery) and an operator(AND, OR, NOT, XOR). Although the idea here is to implement all kind of queries as in: http://lucene.apache.org/java/docs/queryparsersyntax.html eventually. nsNavHistoryQuery is used to query using the query struct. The results of which is url_list which is displayed using view.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
[1] An Efficient Indexing Technique for full-text database systems: Justin Jobel, Alistair Moffet, Ron Sacks Davis&lt;br /&gt;
[2]Using a relational database for full-text index, Steve Putz, Xerox PARC&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=59116</id>
		<title>Places:Full Text Indexing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=59116"/>
		<updated>2007-06-10T10:27:29Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: /* nsNavHistoryQuery */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;ToDO: Formatting, it is badly formatted. Any idea on how to effectively format code?&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
Full Text Indexing feature will allow user to search for a word/phrase from the pages that he has visited. The search query will be tightly integrated with Places&#039;s nsNavHistoryService. The tighter integration will allow queries like &amp;quot;search for pages visited between 01/05/07(dd/mm/yy) to 20/05/07(dd/mm/yy) containing the word &#039;places&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Design Descision ==&lt;br /&gt;
&lt;br /&gt;
A number of options were looked into before proposing this design. The options included implementing using CLucene(like flock), SQLite&#039;s FTS1 and FTS2 module, implementation using B+ Trees, using relational database etc.. The following text will briefly describe the advantage and disadvantage of all the implementation methods.&lt;br /&gt;
&lt;br /&gt;
CLucene is a full-text indexing engine that stores the index as B+ Trees in files. It uses a very efficient method for storage and retrieval. It has an excellent support for CJK languages. The Places system is a new incorporation into firefox. Hence, it is important that during its initial stages all the code that is written or used is flexible, small and tightly integrated with it. Tighter integration would allow future enhancements specific to firefox. Hence this approach was dropped.&lt;br /&gt;
&lt;br /&gt;
SQLite&#039;s FTS1 and FTS2 module are open source implementation of full-text indexing integrated with SQLite. FTS1 and FTS2 stores the text in B+ Trees and the access to the full-text index is using SQL. However, there are number of short-comings for our usage. FTS2 is still in stage where the API and data might change without backward compatibility. FTS1 does not have custom tokenizer which means no CJK support. Also, FTS1 stores the entire page duplicating what is aleady stored in the cache.&lt;br /&gt;
&lt;br /&gt;
A custom implementation using B+ Tree is a very good option but however, it would require additional B+ Tree engine. In light of availability of an efficient algorithm for implementing full-text indexing using relational database, this method is used.&lt;br /&gt;
&lt;br /&gt;
A naive implementation of full-text indexing is very costly in terms of storage. I&#039;ll briefly explain how it is so. Let us define term. A term is any word that appear in a page. So a relational database contains a table with two columns, term and id. Another table contains two columsn term id and doc id(id of the document the term appeard in). The GNU manuals were analyzed [1]. It is 5.15 Mb of text containing 958,774 occurrences of word out of which 27,554 are unique. But table 2 will require that every occurrence has a corresponding doc id. If term id were stored as int, the amount of space required to store the first column alone, would be 958,774 * 4 bytes, which is about 3 Mb. A B+ Tree implementation is atleast 3Mb more efficient. However a nice encoding scheme and storage model proposed by [2] is almost as efficient as a B+ Tree implementation. This algorithm also leverages the capabilites of relational database system while not losing too much in terms of storage and performance. Hence I propose to implement this algorithm.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Use Case ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: User&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Visit Page&amp;lt;br&amp;gt;&lt;br /&gt;
- Search&amp;lt;br&amp;gt;&lt;br /&gt;
- Clear History&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: Browser&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Expire Page&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The use cases above will be used to validate the design.&lt;br /&gt;
&lt;br /&gt;
== Database Design ==&lt;br /&gt;
&lt;br /&gt;
TODO: Check the url_table and put it here. The url_table acts as the document table. The url table will contain additionally the document length&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; &lt;br /&gt;
|+&#039;&#039;&#039;Word Table&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!columnn!!type!!bytes!!description&lt;br /&gt;
|-&lt;br /&gt;
|word||varchar||&amp;lt;=100||term for indexing(Shouldn&#039;t it be unicode? how do i store unicode?)&lt;br /&gt;
|-&lt;br /&gt;
|wordnum||integer||4||unique id. Integer works cause the number of unique words will be atmost a million. Non-english language?&lt;br /&gt;
|-&lt;br /&gt;
|doc_count||integer||4||number of documents the word occurred in&lt;br /&gt;
|-&lt;br /&gt;
|word_count||integer||4||number of occurrences of the word&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; &lt;br /&gt;
|+&#039;&#039;&#039;Posting Table&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!column!!type!!bytes!!description&lt;br /&gt;
|-&lt;br /&gt;
|wordnum||integer||4||This is the foreign key matching that in the word table&lt;br /&gt;
|-&lt;br /&gt;
|firstdoc||integer||4||lowest doc id referenced in the block&lt;br /&gt;
|-&lt;br /&gt;
|flags||tinyint||1||indicates the block type, length of doc list, sequence number&lt;br /&gt;
|-&lt;br /&gt;
|block||varbinary||&amp;lt;=255||contains encoded document and/or position postings for the word&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To Do&lt;br /&gt;
# We might need a table or two more for ranking efficiently&lt;br /&gt;
# Check if SQLite has varbinary datatype. There is a BLOB data type, I am sure.&lt;br /&gt;
&lt;br /&gt;
Note that the tables structure is subject to change to improve efficiency. New Tables might be formed and/or the table might add/remove column&lt;br /&gt;
&lt;br /&gt;
== Detailed Design ==&lt;br /&gt;
&lt;br /&gt;
Classes&lt;br /&gt;
&lt;br /&gt;
There are essentially four classes for the back-end:&lt;br /&gt;
# nsNavHistoryQuery&lt;br /&gt;
# nsNavFullTextIndexHelper&lt;br /&gt;
# nsNavFullTextIndex&lt;br /&gt;
# nsNavFullTextAnalyzer&lt;br /&gt;
&lt;br /&gt;
===nsNavHistoryQuery===&lt;br /&gt;
&lt;br /&gt;
This class is already implemented with all features except searching text. The mechanism of searching is no different from what described in http://developer.mozilla.org/en/docs/Places:Query_System. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var historyService = Components.classes[&amp;quot;@mozilla.org/browser/nav-history-service;1&amp;quot;]                               .getService(Components.interfaces.nsINavHistoryService);// no query parameters will get all history&lt;br /&gt;
&lt;br /&gt;
var options = historyService.getNewQueryOptions();&lt;br /&gt;
var query = historyService.getNewQuery();&lt;br /&gt;
query.searchTerms = &amp;quot;Mozilla Firefox&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// execute the query&lt;br /&gt;
var result = historyService.executeQuery(query, options);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result will contain a list of URI. A number of options can be specified in query and options making it very powerful. Conjunctive queries can be also be executed with historyService.executeQueries and a list of query as parameter.&lt;br /&gt;
&lt;br /&gt;
Internally the function calls nsNavFullTextIndex::searchDocument(searchTerms)  which returns a list of URI ranked according to algorithm described in  SearchDocument(terms) function that will be described later in this document. The list of URI is further filtered by the other parameters set in query and options variable. In case of executeQueries method, the list is aggregated with results from multiple queries.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndex===&lt;br /&gt;
&lt;br /&gt;
This class interacts with SQLite database. This implements the algorithm for adding document to the index, removing document from the index and search for a given term. This search function is used by nsNavHistoryQuery::search function. Look at [2] for the algorithm used.&lt;br /&gt;
&lt;br /&gt;
Block is a struct used to encode and decode block. A block is variable length delta encoded. Variable Length delta Encoding, compresses very efficiently balancing speed and storage requirement.&lt;br /&gt;
&amp;lt;pre&amp;gt;struct Block {&lt;br /&gt;
	//The width of the field is 255 bytes. The return value is an int indicating number of elements in the data that were encoded in the out byte array.&lt;br /&gt;
	int encode(in int[] data, out byte[] encodedBlock) {&lt;br /&gt;
		//How to encode more efficiently, any idea?&lt;br /&gt;
		int[] bigEndian;&lt;br /&gt;
		int k = 0;&lt;br /&gt;
		data[i - 1] = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; data.length; i++) {&lt;br /&gt;
			data[i] -= data[i - 1];&lt;br /&gt;
			int j = 0;&lt;br /&gt;
			while(data[i] != 0) {&lt;br /&gt;
				bigEndian[j++] = data[i] % 128;&lt;br /&gt;
				data[i] /= 128;&lt;br /&gt;
			}&lt;br /&gt;
			for( ; j &amp;gt; 0; j--, k++) {&lt;br /&gt;
				encodedBlock[k] = (1 &amp;lt;&amp;lt; 8) &amp;amp; bigEndian[j];&lt;br /&gt;
			}&lt;br /&gt;
			encodedBlock[k++] = bigEndian[0];&lt;br /&gt;
			if (k &amp;gt; 255) &lt;br /&gt;
				return i - 1&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	void decode(in byte[] encodedBlock, out int[] data) {&lt;br /&gt;
		int j = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; encodedBlock.length; i++) {&lt;br /&gt;
			if (encodedBlock[i] &amp;amp;&amp;amp; (1 &amp;lt;&amp;lt; 8)) {&lt;br /&gt;
				data[j] *= 128;&lt;br /&gt;
				data[j] += encodedBlock[i] &amp;amp; ((1 &amp;lt;&amp;lt; 8) - 1);&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				data[j] += data[j - 1]; //Because it was delta encoded&lt;br /&gt;
				data[j++] = encodedBlock[i];&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
AddDocument(connection, document, analyzer) {&lt;br /&gt;
	AddDocument in two pass. Scan the document for terms and then invert. We have the doc id. We will require a hash map library to make it very efficient. Term is a hash map. Usage: term[&#039;termname&#039;] = array of pos.&lt;br /&gt;
&amp;lt;pre&amp;gt;	while(analyzer.hasTerms()) {&lt;br /&gt;
		cTerm = analyzer.nextTerm();&lt;br /&gt;
		term[cTerm.Name].add(cTerm.pos);&lt;br /&gt;
	}&lt;br /&gt;
	iterator i = term.iterator();&lt;br /&gt;
	while(i.hasNext()) {&lt;br /&gt;
		termName = i.next();&lt;br /&gt;
		termPos[] = term[termName];&lt;br /&gt;
	&lt;br /&gt;
		//hopefully sqlite caches query results, it will be inefficient otherwise.&lt;br /&gt;
&lt;br /&gt;
		if (term already in table) { &lt;br /&gt;
			record = executeQuery(&amp;quot;SELECT ﬁrstdoc, ﬂags, block FROM postings&lt;br /&gt;
				WHERE word = termName&lt;br /&gt;
				AND ﬁrstdoc == (Given as &amp;gt;= in [2], == is correct in my opinion)&lt;br /&gt;
				   (SELECT max (ﬁrstdoc) FROM postings&lt;br /&gt;
				    WHERE word = termName and flags &amp;lt; 128)&amp;quot;);&lt;br /&gt;
			//Refer to [2] for explanation of this.&lt;br /&gt;
			//only one record is retrieved with flags == 0 or flags between 0 and 128&lt;br /&gt;
			//when flag == 0, the block contains only document list&lt;br /&gt;
			if (flag == 0) {&lt;br /&gt;
				1) Decode the block&lt;br /&gt;
				2) See if one more document can be fitted in this.&lt;br /&gt;
				3) Yes? add to it&lt;br /&gt;
					i) find the position list&lt;br /&gt;
						positionList = executeQuery(&amp;quot;SELECT firstdoc, flags, block FROM positings&lt;br /&gt;
							where firstdoc = &lt;br /&gt;
								SELECT max(ﬁrstdoc) FROM postings&lt;br /&gt;
									WHERE word = termName&lt;br /&gt;
									AND ﬁrstdoc &amp;gt;= record.firstdoc AND flags &amp;gt;= 128&lt;br /&gt;
					ii) Try to add as many position in this block&lt;br /&gt;
					iii) when the block is done, create a new row, with firstdoc == currentdoc &lt;br /&gt;
						and flags == 128 or 129 depending on whether the prev firstdoc was same as this.&lt;br /&gt;
&lt;br /&gt;
					iv) goto ii) if there are more left.&lt;br /&gt;
				4) no?&lt;br /&gt;
					i) create a new row with firstdoc = docid, flags=2&lt;br /&gt;
					ii) To the block  add, doc id and doc freq. And all the pos. Note the position listings are never split when flag == 2.&lt;br /&gt;
						We must try to fit all the position listing in this block.99% of the case this should be possible. Make a small calculation, you&#039;ll find out i am correct&lt;br /&gt;
					iii) The rare case it is not possible? create two rows one with flags==0 and flags==128&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				//This is slightly complex in that there is both document list and position list in the same block. We have to decode the block. Try to add document id and and all the position to the position list. This might not be possible. And we&#039;ll have to create two new rows, one with flags == 0 and other with flags == 128&lt;br /&gt;
			}&lt;br /&gt;
			update the word count in the word list table appropriately&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	commit to the database&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveDocument() {&lt;br /&gt;
	Inherently inefficient because of the structure that we have adopted. Needs to be optimized. The general algorithm revolves around finding the record whose firstDoc is immediately less or same as the docId we are searching for.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;	e.g. Say we want to delete docid = 20. We got two records&lt;br /&gt;
	firstDoc = 18, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	firstDoc = 22, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	So we select the record with docid = 18 that is immediately before docid = 20;&lt;br /&gt;
	&lt;br /&gt;
	query to achieve this: SELECT word, firstdoc, block FROM postings where&lt;br /&gt;
				firstdoc = SELECT max(firstdoc) FROM postings where&lt;br /&gt;
						firstdoc &amp;lt; docIdWeAreSearchingFor&lt;br /&gt;
	This returns a number of records with flag == 0 or 0 &amp;lt; flag &amp;lt; 128 or flag == 128 or flag &amp;gt; 128. &lt;br /&gt;
	for each record we found do the following:&lt;br /&gt;
		docAndPostingTable = decodeBlock(block);&lt;br /&gt;
		we have just decoded the block using Block::decodeBlock(). &lt;br /&gt;
		docAndPostingTable.find(docId) //we check the decode block if it contains docId of our interest. &lt;br /&gt;
		if docId found&lt;br /&gt;
			Check the flag&lt;br /&gt;
			when flag == 0 //only document list&lt;br /&gt;
				remove the document and freq from the block&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				if docId == firstDoc update firstDoc with the immediately following doc&lt;br /&gt;
				if no more docs left in this row, delete the row&lt;br /&gt;
			when 0 &amp;lt; flag &amp;lt; 128, contains both document list and posting table&lt;br /&gt;
				remove the document from the block.&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				remove all the postings of the document for the term in the block&lt;br /&gt;
				if (docId == firstDoc) update firstDoc with immediately following doc&lt;br /&gt;
				if no more docs left in the row, delete the row&lt;br /&gt;
			when flag &amp;gt;= 128 //only postings table&lt;br /&gt;
				remove all the postings corresponding to the doc&lt;br /&gt;
				update the firstDoc for the record&lt;br /&gt;
				delete the record if block is empty&lt;br /&gt;
		&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SearchDocument(terms) {&lt;br /&gt;
	terms is something like: &amp;quot;mac apple panther&amp;quot;. Basically a collection of terms&lt;br /&gt;
	The ranking algorithm as described in this document http://lucene.apache.org/java/2_1_0/api/org/apache/lucene/search/Similarity.html is used.&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndexHelper===&lt;br /&gt;
&lt;br /&gt;
This class contains mechanism for scheduling documents for indexing. It works pretty much like nsNavHistoryExpire. The scheduling balances indexing with overall firefox performance. It has a timer which when expires picks an unindexed document from the history and calls nsNavFullTextIndex::addDocument to index. When and how the timer is set, dictates the overall efficiency. Whenever a user visits a page, the timer is triggered. &lt;br /&gt;
&lt;br /&gt;
When user visits a page, the history service calls OnAddURI function of this class. This function starts the timer. When timer expires the call back function is called. The function checks If there are any more document to be indexed and it has not been a long while since the user called addURI. If so, then it resets the timer and waits to expire again.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextAnalyzer===&lt;br /&gt;
&lt;br /&gt;
The analyzer design is similar to the way Lucene works. The Lucene design enables support for multiple languages.&lt;br /&gt;
The Analyzer takes the tokens generated from the tokenizer and discards those that are not required for indexing. E.g. is, a, an the can be discarded. This is enabled by by tokenStream classes. Refer to nsNavFullTextTokenStream and Lucene API for more detail.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextTokenStream===&lt;br /&gt;
&lt;br /&gt;
TokenStream is an abstract class with three methods next, reset and close. The next method returns a token. Token is a class representing startoffset, endoffset and termtext. TokenStream needs input to tokenize. There are two concrete class for TokenStream:&lt;br /&gt;
# Tokenizer: The input is an inputstream.&lt;br /&gt;
# TokenFilter: The input is another TokenStream. This works like pipes.&lt;br /&gt;
&lt;br /&gt;
== Front-End ==&lt;br /&gt;
1) nsNavQueryParser&lt;br /&gt;
The function of this is to break the query given in human language into a graph of TermQuery and BooleanQuery. BooleanQuery is a struct with two operand(each a TermQuery or BooleanQuery) and an operator(AND, OR, NOT, XOR). Although the idea here is to implement all kind of queries as in: http://lucene.apache.org/java/docs/queryparsersyntax.html eventually. nsNavHistoryQuery is used to query using the query struct. The results of which is url_list which is displayed using view.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
[1] An Efficient Indexing Technique for full-text database systems: Justin Jobel, Alistair Moffet, Ron Sacks Davis&lt;br /&gt;
[2]Using a relational database for full-text index, Steve Putz, Xerox PARC&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=59115</id>
		<title>Places:Full Text Indexing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=59115"/>
		<updated>2007-06-10T10:25:41Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: /* nsNavHistoryQuery */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;ToDO: Formatting, it is badly formatted. Any idea on how to effectively format code?&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
Full Text Indexing feature will allow user to search for a word/phrase from the pages that he has visited. The search query will be tightly integrated with Places&#039;s nsNavHistoryService. The tighter integration will allow queries like &amp;quot;search for pages visited between 01/05/07(dd/mm/yy) to 20/05/07(dd/mm/yy) containing the word &#039;places&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Design Descision ==&lt;br /&gt;
&lt;br /&gt;
A number of options were looked into before proposing this design. The options included implementing using CLucene(like flock), SQLite&#039;s FTS1 and FTS2 module, implementation using B+ Trees, using relational database etc.. The following text will briefly describe the advantage and disadvantage of all the implementation methods.&lt;br /&gt;
&lt;br /&gt;
CLucene is a full-text indexing engine that stores the index as B+ Trees in files. It uses a very efficient method for storage and retrieval. It has an excellent support for CJK languages. The Places system is a new incorporation into firefox. Hence, it is important that during its initial stages all the code that is written or used is flexible, small and tightly integrated with it. Tighter integration would allow future enhancements specific to firefox. Hence this approach was dropped.&lt;br /&gt;
&lt;br /&gt;
SQLite&#039;s FTS1 and FTS2 module are open source implementation of full-text indexing integrated with SQLite. FTS1 and FTS2 stores the text in B+ Trees and the access to the full-text index is using SQL. However, there are number of short-comings for our usage. FTS2 is still in stage where the API and data might change without backward compatibility. FTS1 does not have custom tokenizer which means no CJK support. Also, FTS1 stores the entire page duplicating what is aleady stored in the cache.&lt;br /&gt;
&lt;br /&gt;
A custom implementation using B+ Tree is a very good option but however, it would require additional B+ Tree engine. In light of availability of an efficient algorithm for implementing full-text indexing using relational database, this method is used.&lt;br /&gt;
&lt;br /&gt;
A naive implementation of full-text indexing is very costly in terms of storage. I&#039;ll briefly explain how it is so. Let us define term. A term is any word that appear in a page. So a relational database contains a table with two columns, term and id. Another table contains two columsn term id and doc id(id of the document the term appeard in). The GNU manuals were analyzed [1]. It is 5.15 Mb of text containing 958,774 occurrences of word out of which 27,554 are unique. But table 2 will require that every occurrence has a corresponding doc id. If term id were stored as int, the amount of space required to store the first column alone, would be 958,774 * 4 bytes, which is about 3 Mb. A B+ Tree implementation is atleast 3Mb more efficient. However a nice encoding scheme and storage model proposed by [2] is almost as efficient as a B+ Tree implementation. This algorithm also leverages the capabilites of relational database system while not losing too much in terms of storage and performance. Hence I propose to implement this algorithm.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Use Case ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: User&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Visit Page&amp;lt;br&amp;gt;&lt;br /&gt;
- Search&amp;lt;br&amp;gt;&lt;br /&gt;
- Clear History&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: Browser&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Expire Page&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The use cases above will be used to validate the design.&lt;br /&gt;
&lt;br /&gt;
== Database Design ==&lt;br /&gt;
&lt;br /&gt;
TODO: Check the url_table and put it here. The url_table acts as the document table. The url table will contain additionally the document length&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; &lt;br /&gt;
|+&#039;&#039;&#039;Word Table&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!columnn!!type!!bytes!!description&lt;br /&gt;
|-&lt;br /&gt;
|word||varchar||&amp;lt;=100||term for indexing(Shouldn&#039;t it be unicode? how do i store unicode?)&lt;br /&gt;
|-&lt;br /&gt;
|wordnum||integer||4||unique id. Integer works cause the number of unique words will be atmost a million. Non-english language?&lt;br /&gt;
|-&lt;br /&gt;
|doc_count||integer||4||number of documents the word occurred in&lt;br /&gt;
|-&lt;br /&gt;
|word_count||integer||4||number of occurrences of the word&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; &lt;br /&gt;
|+&#039;&#039;&#039;Posting Table&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!column!!type!!bytes!!description&lt;br /&gt;
|-&lt;br /&gt;
|wordnum||integer||4||This is the foreign key matching that in the word table&lt;br /&gt;
|-&lt;br /&gt;
|firstdoc||integer||4||lowest doc id referenced in the block&lt;br /&gt;
|-&lt;br /&gt;
|flags||tinyint||1||indicates the block type, length of doc list, sequence number&lt;br /&gt;
|-&lt;br /&gt;
|block||varbinary||&amp;lt;=255||contains encoded document and/or position postings for the word&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To Do&lt;br /&gt;
# We might need a table or two more for ranking efficiently&lt;br /&gt;
# Check if SQLite has varbinary datatype. There is a BLOB data type, I am sure.&lt;br /&gt;
&lt;br /&gt;
Note that the tables structure is subject to change to improve efficiency. New Tables might be formed and/or the table might add/remove column&lt;br /&gt;
&lt;br /&gt;
== Detailed Design ==&lt;br /&gt;
&lt;br /&gt;
Classes&lt;br /&gt;
&lt;br /&gt;
There are essentially four classes for the back-end:&lt;br /&gt;
# nsNavHistoryQuery&lt;br /&gt;
# nsNavFullTextIndexHelper&lt;br /&gt;
# nsNavFullTextIndex&lt;br /&gt;
# nsNavFullTextAnalyzer&lt;br /&gt;
&lt;br /&gt;
===nsNavHistoryQuery===&lt;br /&gt;
&lt;br /&gt;
This class is already implemented with all features except searching text. The mechanism of searching is no different from what described in http://developer.mozilla.org/en/docs/Places:Query_System. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var historyService = Components.classes[&amp;quot;@mozilla.org/browser/nav-history-service;1&amp;quot;]                               .getService(Components.interfaces.nsINavHistoryService);// no query parameters will get all history&lt;br /&gt;
&lt;br /&gt;
var options = historyService.getNewQueryOptions();&lt;br /&gt;
var query = historyService.getNewQuery();&lt;br /&gt;
query.searchTerms = &amp;quot;Mozilla Firefox&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// execute the query&lt;br /&gt;
var result = historyService.executeQuery(query, options);&lt;br /&gt;
&amp;lt;/prev&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result will contain a list of URI. A number of options can be specified in query and options making it very powerful. Conjunctive queries can be also be executed with historyService.executeQueries and a list of query as parameter.&lt;br /&gt;
&lt;br /&gt;
Internally the function calls nsNavFullTextIndex::searchDocument(searchTerms)  which returns a list of URI ranked according to algorithm described in  SearchDocument(terms) function that will be described later in this document. The list of URI is further filtered by the other parameters set in query and options variable. In case of executeQueries method, the list is aggregated with results from multiple queries.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndex===&lt;br /&gt;
&lt;br /&gt;
This class interacts with SQLite database. This implements the algorithm for adding document to the index, removing document from the index and search for a given term. This search function is used by nsNavHistoryQuery::search function. Look at [2] for the algorithm used.&lt;br /&gt;
&lt;br /&gt;
Block is a struct used to encode and decode block. A block is variable length delta encoded. Variable Length delta Encoding, compresses very efficiently balancing speed and storage requirement.&lt;br /&gt;
&amp;lt;pre&amp;gt;struct Block {&lt;br /&gt;
	//The width of the field is 255 bytes. The return value is an int indicating number of elements in the data that were encoded in the out byte array.&lt;br /&gt;
	int encode(in int[] data, out byte[] encodedBlock) {&lt;br /&gt;
		//How to encode more efficiently, any idea?&lt;br /&gt;
		int[] bigEndian;&lt;br /&gt;
		int k = 0;&lt;br /&gt;
		data[i - 1] = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; data.length; i++) {&lt;br /&gt;
			data[i] -= data[i - 1];&lt;br /&gt;
			int j = 0;&lt;br /&gt;
			while(data[i] != 0) {&lt;br /&gt;
				bigEndian[j++] = data[i] % 128;&lt;br /&gt;
				data[i] /= 128;&lt;br /&gt;
			}&lt;br /&gt;
			for( ; j &amp;gt; 0; j--, k++) {&lt;br /&gt;
				encodedBlock[k] = (1 &amp;lt;&amp;lt; 8) &amp;amp; bigEndian[j];&lt;br /&gt;
			}&lt;br /&gt;
			encodedBlock[k++] = bigEndian[0];&lt;br /&gt;
			if (k &amp;gt; 255) &lt;br /&gt;
				return i - 1&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	void decode(in byte[] encodedBlock, out int[] data) {&lt;br /&gt;
		int j = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; encodedBlock.length; i++) {&lt;br /&gt;
			if (encodedBlock[i] &amp;amp;&amp;amp; (1 &amp;lt;&amp;lt; 8)) {&lt;br /&gt;
				data[j] *= 128;&lt;br /&gt;
				data[j] += encodedBlock[i] &amp;amp; ((1 &amp;lt;&amp;lt; 8) - 1);&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				data[j] += data[j - 1]; //Because it was delta encoded&lt;br /&gt;
				data[j++] = encodedBlock[i];&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
AddDocument(connection, document, analyzer) {&lt;br /&gt;
	AddDocument in two pass. Scan the document for terms and then invert. We have the doc id. We will require a hash map library to make it very efficient. Term is a hash map. Usage: term[&#039;termname&#039;] = array of pos.&lt;br /&gt;
&amp;lt;pre&amp;gt;	while(analyzer.hasTerms()) {&lt;br /&gt;
		cTerm = analyzer.nextTerm();&lt;br /&gt;
		term[cTerm.Name].add(cTerm.pos);&lt;br /&gt;
	}&lt;br /&gt;
	iterator i = term.iterator();&lt;br /&gt;
	while(i.hasNext()) {&lt;br /&gt;
		termName = i.next();&lt;br /&gt;
		termPos[] = term[termName];&lt;br /&gt;
	&lt;br /&gt;
		//hopefully sqlite caches query results, it will be inefficient otherwise.&lt;br /&gt;
&lt;br /&gt;
		if (term already in table) { &lt;br /&gt;
			record = executeQuery(&amp;quot;SELECT ﬁrstdoc, ﬂags, block FROM postings&lt;br /&gt;
				WHERE word = termName&lt;br /&gt;
				AND ﬁrstdoc == (Given as &amp;gt;= in [2], == is correct in my opinion)&lt;br /&gt;
				   (SELECT max (ﬁrstdoc) FROM postings&lt;br /&gt;
				    WHERE word = termName and flags &amp;lt; 128)&amp;quot;);&lt;br /&gt;
			//Refer to [2] for explanation of this.&lt;br /&gt;
			//only one record is retrieved with flags == 0 or flags between 0 and 128&lt;br /&gt;
			//when flag == 0, the block contains only document list&lt;br /&gt;
			if (flag == 0) {&lt;br /&gt;
				1) Decode the block&lt;br /&gt;
				2) See if one more document can be fitted in this.&lt;br /&gt;
				3) Yes? add to it&lt;br /&gt;
					i) find the position list&lt;br /&gt;
						positionList = executeQuery(&amp;quot;SELECT firstdoc, flags, block FROM positings&lt;br /&gt;
							where firstdoc = &lt;br /&gt;
								SELECT max(ﬁrstdoc) FROM postings&lt;br /&gt;
									WHERE word = termName&lt;br /&gt;
									AND ﬁrstdoc &amp;gt;= record.firstdoc AND flags &amp;gt;= 128&lt;br /&gt;
					ii) Try to add as many position in this block&lt;br /&gt;
					iii) when the block is done, create a new row, with firstdoc == currentdoc &lt;br /&gt;
						and flags == 128 or 129 depending on whether the prev firstdoc was same as this.&lt;br /&gt;
&lt;br /&gt;
					iv) goto ii) if there are more left.&lt;br /&gt;
				4) no?&lt;br /&gt;
					i) create a new row with firstdoc = docid, flags=2&lt;br /&gt;
					ii) To the block  add, doc id and doc freq. And all the pos. Note the position listings are never split when flag == 2.&lt;br /&gt;
						We must try to fit all the position listing in this block.99% of the case this should be possible. Make a small calculation, you&#039;ll find out i am correct&lt;br /&gt;
					iii) The rare case it is not possible? create two rows one with flags==0 and flags==128&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				//This is slightly complex in that there is both document list and position list in the same block. We have to decode the block. Try to add document id and and all the position to the position list. This might not be possible. And we&#039;ll have to create two new rows, one with flags == 0 and other with flags == 128&lt;br /&gt;
			}&lt;br /&gt;
			update the word count in the word list table appropriately&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	commit to the database&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveDocument() {&lt;br /&gt;
	Inherently inefficient because of the structure that we have adopted. Needs to be optimized. The general algorithm revolves around finding the record whose firstDoc is immediately less or same as the docId we are searching for.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;	e.g. Say we want to delete docid = 20. We got two records&lt;br /&gt;
	firstDoc = 18, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	firstDoc = 22, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	So we select the record with docid = 18 that is immediately before docid = 20;&lt;br /&gt;
	&lt;br /&gt;
	query to achieve this: SELECT word, firstdoc, block FROM postings where&lt;br /&gt;
				firstdoc = SELECT max(firstdoc) FROM postings where&lt;br /&gt;
						firstdoc &amp;lt; docIdWeAreSearchingFor&lt;br /&gt;
	This returns a number of records with flag == 0 or 0 &amp;lt; flag &amp;lt; 128 or flag == 128 or flag &amp;gt; 128. &lt;br /&gt;
	for each record we found do the following:&lt;br /&gt;
		docAndPostingTable = decodeBlock(block);&lt;br /&gt;
		we have just decoded the block using Block::decodeBlock(). &lt;br /&gt;
		docAndPostingTable.find(docId) //we check the decode block if it contains docId of our interest. &lt;br /&gt;
		if docId found&lt;br /&gt;
			Check the flag&lt;br /&gt;
			when flag == 0 //only document list&lt;br /&gt;
				remove the document and freq from the block&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				if docId == firstDoc update firstDoc with the immediately following doc&lt;br /&gt;
				if no more docs left in this row, delete the row&lt;br /&gt;
			when 0 &amp;lt; flag &amp;lt; 128, contains both document list and posting table&lt;br /&gt;
				remove the document from the block.&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				remove all the postings of the document for the term in the block&lt;br /&gt;
				if (docId == firstDoc) update firstDoc with immediately following doc&lt;br /&gt;
				if no more docs left in the row, delete the row&lt;br /&gt;
			when flag &amp;gt;= 128 //only postings table&lt;br /&gt;
				remove all the postings corresponding to the doc&lt;br /&gt;
				update the firstDoc for the record&lt;br /&gt;
				delete the record if block is empty&lt;br /&gt;
		&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SearchDocument(terms) {&lt;br /&gt;
	terms is something like: &amp;quot;mac apple panther&amp;quot;. Basically a collection of terms&lt;br /&gt;
	The ranking algorithm as described in this document is used.&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndexHelper===&lt;br /&gt;
&lt;br /&gt;
This class contains mechanism for scheduling documents for indexing. It works pretty much like nsNavHistoryExpire. The scheduling balances indexing with overall firefox performance. It has a timer which when expires picks an unindexed document from the history and calls nsNavFullTextIndex::addDocument to index. When and how the timer is set, dictates the overall efficiency. Whenever a user visits a page, the timer is triggered. &lt;br /&gt;
&lt;br /&gt;
When user visits a page, the history service calls OnAddURI function of this class. This function starts the timer. When timer expires the call back function is called. The function checks If there are any more document to be indexed and it has not been a long while since the user called addURI. If so, then it resets the timer and waits to expire again.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextAnalyzer===&lt;br /&gt;
&lt;br /&gt;
The analyzer design is similar to the way Lucene works. The Lucene design enables support for multiple languages.&lt;br /&gt;
The Analyzer takes the tokens generated from the tokenizer and discards those that are not required for indexing. E.g. is, a, an the can be discarded. This is enabled by by tokenStream classes. Refer to nsNavFullTextTokenStream and Lucene API for more detail.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextTokenStream===&lt;br /&gt;
&lt;br /&gt;
TokenStream is an abstract class with three methods next, reset and close. The next method returns a token. Token is a class representing startoffset, endoffset and termtext. TokenStream needs input to tokenize. There are two concrete class for TokenStream:&lt;br /&gt;
# Tokenizer: The input is an inputstream.&lt;br /&gt;
# TokenFilter: The input is another TokenStream. This works like pipes.&lt;br /&gt;
&lt;br /&gt;
== Front-End ==&lt;br /&gt;
1) nsNavQueryParser&lt;br /&gt;
The function of this is to break the query given in human language into a graph of TermQuery and BooleanQuery. BooleanQuery is a struct with two operand(each a TermQuery or BooleanQuery) and an operator(AND, OR, NOT, XOR). Although the idea here is to implement all kind of queries as in: http://lucene.apache.org/java/docs/queryparsersyntax.html eventually. nsNavHistoryQuery is used to query using the query struct. The results of which is url_list which is displayed using view.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
[1] An Efficient Indexing Technique for full-text database systems: Justin Jobel, Alistair Moffet, Ron Sacks Davis&lt;br /&gt;
[2]Using a relational database for full-text index, Steve Putz, Xerox PARC&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=59114</id>
		<title>Places:Full Text Indexing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=59114"/>
		<updated>2007-06-10T09:45:57Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: /* Detailed Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;ToDO: Formatting, it is badly formatted. Any idea on how to effectively format code?&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
Full Text Indexing feature will allow user to search for a word/phrase from the pages that he has visited. The search query will be tightly integrated with Places&#039;s nsNavHistoryService. The tighter integration will allow queries like &amp;quot;search for pages visited between 01/05/07(dd/mm/yy) to 20/05/07(dd/mm/yy) containing the word &#039;places&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Design Descision ==&lt;br /&gt;
&lt;br /&gt;
A number of options were looked into before proposing this design. The options included implementing using CLucene(like flock), SQLite&#039;s FTS1 and FTS2 module, implementation using B+ Trees, using relational database etc.. The following text will briefly describe the advantage and disadvantage of all the implementation methods.&lt;br /&gt;
&lt;br /&gt;
CLucene is a full-text indexing engine that stores the index as B+ Trees in files. It uses a very efficient method for storage and retrieval. It has an excellent support for CJK languages. The Places system is a new incorporation into firefox. Hence, it is important that during its initial stages all the code that is written or used is flexible, small and tightly integrated with it. Tighter integration would allow future enhancements specific to firefox. Hence this approach was dropped.&lt;br /&gt;
&lt;br /&gt;
SQLite&#039;s FTS1 and FTS2 module are open source implementation of full-text indexing integrated with SQLite. FTS1 and FTS2 stores the text in B+ Trees and the access to the full-text index is using SQL. However, there are number of short-comings for our usage. FTS2 is still in stage where the API and data might change without backward compatibility. FTS1 does not have custom tokenizer which means no CJK support. Also, FTS1 stores the entire page duplicating what is aleady stored in the cache.&lt;br /&gt;
&lt;br /&gt;
A custom implementation using B+ Tree is a very good option but however, it would require additional B+ Tree engine. In light of availability of an efficient algorithm for implementing full-text indexing using relational database, this method is used.&lt;br /&gt;
&lt;br /&gt;
A naive implementation of full-text indexing is very costly in terms of storage. I&#039;ll briefly explain how it is so. Let us define term. A term is any word that appear in a page. So a relational database contains a table with two columns, term and id. Another table contains two columsn term id and doc id(id of the document the term appeard in). The GNU manuals were analyzed [1]. It is 5.15 Mb of text containing 958,774 occurrences of word out of which 27,554 are unique. But table 2 will require that every occurrence has a corresponding doc id. If term id were stored as int, the amount of space required to store the first column alone, would be 958,774 * 4 bytes, which is about 3 Mb. A B+ Tree implementation is atleast 3Mb more efficient. However a nice encoding scheme and storage model proposed by [2] is almost as efficient as a B+ Tree implementation. This algorithm also leverages the capabilites of relational database system while not losing too much in terms of storage and performance. Hence I propose to implement this algorithm.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Use Case ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: User&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Visit Page&amp;lt;br&amp;gt;&lt;br /&gt;
- Search&amp;lt;br&amp;gt;&lt;br /&gt;
- Clear History&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: Browser&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Expire Page&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The use cases above will be used to validate the design.&lt;br /&gt;
&lt;br /&gt;
== Database Design ==&lt;br /&gt;
&lt;br /&gt;
TODO: Check the url_table and put it here. The url_table acts as the document table. The url table will contain additionally the document length&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; &lt;br /&gt;
|+&#039;&#039;&#039;Word Table&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!columnn!!type!!bytes!!description&lt;br /&gt;
|-&lt;br /&gt;
|word||varchar||&amp;lt;=100||term for indexing(Shouldn&#039;t it be unicode? how do i store unicode?)&lt;br /&gt;
|-&lt;br /&gt;
|wordnum||integer||4||unique id. Integer works cause the number of unique words will be atmost a million. Non-english language?&lt;br /&gt;
|-&lt;br /&gt;
|doc_count||integer||4||number of documents the word occurred in&lt;br /&gt;
|-&lt;br /&gt;
|word_count||integer||4||number of occurrences of the word&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; &lt;br /&gt;
|+&#039;&#039;&#039;Posting Table&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!column!!type!!bytes!!description&lt;br /&gt;
|-&lt;br /&gt;
|wordnum||integer||4||This is the foreign key matching that in the word table&lt;br /&gt;
|-&lt;br /&gt;
|firstdoc||integer||4||lowest doc id referenced in the block&lt;br /&gt;
|-&lt;br /&gt;
|flags||tinyint||1||indicates the block type, length of doc list, sequence number&lt;br /&gt;
|-&lt;br /&gt;
|block||varbinary||&amp;lt;=255||contains encoded document and/or position postings for the word&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To Do&lt;br /&gt;
# We might need a table or two more for ranking efficiently&lt;br /&gt;
# Check if SQLite has varbinary datatype. There is a BLOB data type, I am sure.&lt;br /&gt;
&lt;br /&gt;
Note that the tables structure is subject to change to improve efficiency. New Tables might be formed and/or the table might add/remove column&lt;br /&gt;
&lt;br /&gt;
== Detailed Design ==&lt;br /&gt;
&lt;br /&gt;
Classes&lt;br /&gt;
&lt;br /&gt;
There are essentially four classes for the back-end:&lt;br /&gt;
# nsNavHistoryQuery&lt;br /&gt;
# nsNavFullTextIndexHelper&lt;br /&gt;
# nsNavFullTextIndex&lt;br /&gt;
# nsNavFullTextAnalyzer&lt;br /&gt;
&lt;br /&gt;
===nsNavHistoryQuery===&lt;br /&gt;
&lt;br /&gt;
This class is already implemented with all features except searching text. The mechanism of searching is no different from what described in http://developer.mozilla.org/en/docs/Places:Query_System. The result of nsNavHistoryQuery::search will do a full-text search with nsNavHistoryQuery::searchTerms. This is very powerful with the number of options that you can specify. Conjunctive queries can be executed. &lt;br /&gt;
&lt;br /&gt;
The search function uses the searchTerms to call nsNavFullTextIndex::searchDocument(term). This returns a url list based on a certain ranking. This function further filters based on other criteria such as date etc.. provided in the query options. The filtered url list is returned&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndex===&lt;br /&gt;
&lt;br /&gt;
This class interacts with SQLite database. This implements the algorithm for adding document to the index, removing document from the index and search for a given term. This search function is used by nsNavHistoryQuery::search function. Look at [2] for the algorithm used.&lt;br /&gt;
&lt;br /&gt;
Block is a struct used to encode and decode block. A block is variable length delta encoded. Variable Length delta Encoding, compresses very efficiently balancing speed and storage requirement.&lt;br /&gt;
&amp;lt;pre&amp;gt;struct Block {&lt;br /&gt;
	//The width of the field is 255 bytes. The return value is an int indicating number of elements in the data that were encoded in the out byte array.&lt;br /&gt;
	int encode(in int[] data, out byte[] encodedBlock) {&lt;br /&gt;
		//How to encode more efficiently, any idea?&lt;br /&gt;
		int[] bigEndian;&lt;br /&gt;
		int k = 0;&lt;br /&gt;
		data[i - 1] = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; data.length; i++) {&lt;br /&gt;
			data[i] -= data[i - 1];&lt;br /&gt;
			int j = 0;&lt;br /&gt;
			while(data[i] != 0) {&lt;br /&gt;
				bigEndian[j++] = data[i] % 128;&lt;br /&gt;
				data[i] /= 128;&lt;br /&gt;
			}&lt;br /&gt;
			for( ; j &amp;gt; 0; j--, k++) {&lt;br /&gt;
				encodedBlock[k] = (1 &amp;lt;&amp;lt; 8) &amp;amp; bigEndian[j];&lt;br /&gt;
			}&lt;br /&gt;
			encodedBlock[k++] = bigEndian[0];&lt;br /&gt;
			if (k &amp;gt; 255) &lt;br /&gt;
				return i - 1&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	void decode(in byte[] encodedBlock, out int[] data) {&lt;br /&gt;
		int j = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; encodedBlock.length; i++) {&lt;br /&gt;
			if (encodedBlock[i] &amp;amp;&amp;amp; (1 &amp;lt;&amp;lt; 8)) {&lt;br /&gt;
				data[j] *= 128;&lt;br /&gt;
				data[j] += encodedBlock[i] &amp;amp; ((1 &amp;lt;&amp;lt; 8) - 1);&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				data[j] += data[j - 1]; //Because it was delta encoded&lt;br /&gt;
				data[j++] = encodedBlock[i];&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
AddDocument(connection, document, analyzer) {&lt;br /&gt;
	AddDocument in two pass. Scan the document for terms and then invert. We have the doc id. We will require a hash map library to make it very efficient. Term is a hash map. Usage: term[&#039;termname&#039;] = array of pos.&lt;br /&gt;
&amp;lt;pre&amp;gt;	while(analyzer.hasTerms()) {&lt;br /&gt;
		cTerm = analyzer.nextTerm();&lt;br /&gt;
		term[cTerm.Name].add(cTerm.pos);&lt;br /&gt;
	}&lt;br /&gt;
	iterator i = term.iterator();&lt;br /&gt;
	while(i.hasNext()) {&lt;br /&gt;
		termName = i.next();&lt;br /&gt;
		termPos[] = term[termName];&lt;br /&gt;
	&lt;br /&gt;
		//hopefully sqlite caches query results, it will be inefficient otherwise.&lt;br /&gt;
&lt;br /&gt;
		if (term already in table) { &lt;br /&gt;
			record = executeQuery(&amp;quot;SELECT ﬁrstdoc, ﬂags, block FROM postings&lt;br /&gt;
				WHERE word = termName&lt;br /&gt;
				AND ﬁrstdoc == (Given as &amp;gt;= in [2], == is correct in my opinion)&lt;br /&gt;
				   (SELECT max (ﬁrstdoc) FROM postings&lt;br /&gt;
				    WHERE word = termName and flags &amp;lt; 128)&amp;quot;);&lt;br /&gt;
			//Refer to [2] for explanation of this.&lt;br /&gt;
			//only one record is retrieved with flags == 0 or flags between 0 and 128&lt;br /&gt;
			//when flag == 0, the block contains only document list&lt;br /&gt;
			if (flag == 0) {&lt;br /&gt;
				1) Decode the block&lt;br /&gt;
				2) See if one more document can be fitted in this.&lt;br /&gt;
				3) Yes? add to it&lt;br /&gt;
					i) find the position list&lt;br /&gt;
						positionList = executeQuery(&amp;quot;SELECT firstdoc, flags, block FROM positings&lt;br /&gt;
							where firstdoc = &lt;br /&gt;
								SELECT max(ﬁrstdoc) FROM postings&lt;br /&gt;
									WHERE word = termName&lt;br /&gt;
									AND ﬁrstdoc &amp;gt;= record.firstdoc AND flags &amp;gt;= 128&lt;br /&gt;
					ii) Try to add as many position in this block&lt;br /&gt;
					iii) when the block is done, create a new row, with firstdoc == currentdoc &lt;br /&gt;
						and flags == 128 or 129 depending on whether the prev firstdoc was same as this.&lt;br /&gt;
&lt;br /&gt;
					iv) goto ii) if there are more left.&lt;br /&gt;
				4) no?&lt;br /&gt;
					i) create a new row with firstdoc = docid, flags=2&lt;br /&gt;
					ii) To the block  add, doc id and doc freq. And all the pos. Note the position listings are never split when flag == 2.&lt;br /&gt;
						We must try to fit all the position listing in this block.99% of the case this should be possible. Make a small calculation, you&#039;ll find out i am correct&lt;br /&gt;
					iii) The rare case it is not possible? create two rows one with flags==0 and flags==128&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				//This is slightly complex in that there is both document list and position list in the same block. We have to decode the block. Try to add document id and and all the position to the position list. This might not be possible. And we&#039;ll have to create two new rows, one with flags == 0 and other with flags == 128&lt;br /&gt;
			}&lt;br /&gt;
			update the word count in the word list table appropriately&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	commit to the database&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveDocument() {&lt;br /&gt;
	Inherently inefficient because of the structure that we have adopted. Needs to be optimized. The general algorithm revolves around finding the record whose firstDoc is immediately less or same as the docId we are searching for.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;	e.g. Say we want to delete docid = 20. We got two records&lt;br /&gt;
	firstDoc = 18, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	firstDoc = 22, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	So we select the record with docid = 18 that is immediately before docid = 20;&lt;br /&gt;
	&lt;br /&gt;
	query to achieve this: SELECT word, firstdoc, block FROM postings where&lt;br /&gt;
				firstdoc = SELECT max(firstdoc) FROM postings where&lt;br /&gt;
						firstdoc &amp;lt; docIdWeAreSearchingFor&lt;br /&gt;
	This returns a number of records with flag == 0 or 0 &amp;lt; flag &amp;lt; 128 or flag == 128 or flag &amp;gt; 128. &lt;br /&gt;
	for each record we found do the following:&lt;br /&gt;
		docAndPostingTable = decodeBlock(block);&lt;br /&gt;
		we have just decoded the block using Block::decodeBlock(). &lt;br /&gt;
		docAndPostingTable.find(docId) //we check the decode block if it contains docId of our interest. &lt;br /&gt;
		if docId found&lt;br /&gt;
			Check the flag&lt;br /&gt;
			when flag == 0 //only document list&lt;br /&gt;
				remove the document and freq from the block&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				if docId == firstDoc update firstDoc with the immediately following doc&lt;br /&gt;
				if no more docs left in this row, delete the row&lt;br /&gt;
			when 0 &amp;lt; flag &amp;lt; 128, contains both document list and posting table&lt;br /&gt;
				remove the document from the block.&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				remove all the postings of the document for the term in the block&lt;br /&gt;
				if (docId == firstDoc) update firstDoc with immediately following doc&lt;br /&gt;
				if no more docs left in the row, delete the row&lt;br /&gt;
			when flag &amp;gt;= 128 //only postings table&lt;br /&gt;
				remove all the postings corresponding to the doc&lt;br /&gt;
				update the firstDoc for the record&lt;br /&gt;
				delete the record if block is empty&lt;br /&gt;
		&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SearchDocument(terms) {&lt;br /&gt;
	terms is something like: &amp;quot;mac apple panther&amp;quot;. Basically a collection of terms&lt;br /&gt;
	The ranking algorithm as described in this document is used.&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndexHelper===&lt;br /&gt;
&lt;br /&gt;
This class contains mechanism for scheduling documents for indexing. It works pretty much like nsNavHistoryExpire. The scheduling balances indexing with overall firefox performance. It has a timer which when expires picks an unindexed document from the history and calls nsNavFullTextIndex::addDocument to index. When and how the timer is set, dictates the overall efficiency. Whenever a user visits a page, the timer is triggered. &lt;br /&gt;
&lt;br /&gt;
When user visits a page, the history service calls OnAddURI function of this class. This function starts the timer. When timer expires the call back function is called. The function checks If there are any more document to be indexed and it has not been a long while since the user called addURI. If so, then it resets the timer and waits to expire again.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextAnalyzer===&lt;br /&gt;
&lt;br /&gt;
The analyzer design is similar to the way Lucene works. The Lucene design enables support for multiple languages.&lt;br /&gt;
The Analyzer takes the tokens generated from the tokenizer and discards those that are not required for indexing. E.g. is, a, an the can be discarded. This is enabled by by tokenStream classes. Refer to nsNavFullTextTokenStream and Lucene API for more detail.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextTokenStream===&lt;br /&gt;
&lt;br /&gt;
TokenStream is an abstract class with three methods next, reset and close. The next method returns a token. Token is a class representing startoffset, endoffset and termtext. TokenStream needs input to tokenize. There are two concrete class for TokenStream:&lt;br /&gt;
# Tokenizer: The input is an inputstream.&lt;br /&gt;
# TokenFilter: The input is another TokenStream. This works like pipes.&lt;br /&gt;
&lt;br /&gt;
== Front-End ==&lt;br /&gt;
1) nsNavQueryParser&lt;br /&gt;
The function of this is to break the query given in human language into a graph of TermQuery and BooleanQuery. BooleanQuery is a struct with two operand(each a TermQuery or BooleanQuery) and an operator(AND, OR, NOT, XOR). Although the idea here is to implement all kind of queries as in: http://lucene.apache.org/java/docs/queryparsersyntax.html eventually. nsNavHistoryQuery is used to query using the query struct. The results of which is url_list which is displayed using view.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
[1] An Efficient Indexing Technique for full-text database systems: Justin Jobel, Alistair Moffet, Ron Sacks Davis&lt;br /&gt;
[2]Using a relational database for full-text index, Steve Putz, Xerox PARC&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=59113</id>
		<title>Places:Full Text Indexing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=59113"/>
		<updated>2007-06-10T09:45:04Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: /* Database Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;ToDO: Formatting, it is badly formatted. Any idea on how to effectively format code?&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
Full Text Indexing feature will allow user to search for a word/phrase from the pages that he has visited. The search query will be tightly integrated with Places&#039;s nsNavHistoryService. The tighter integration will allow queries like &amp;quot;search for pages visited between 01/05/07(dd/mm/yy) to 20/05/07(dd/mm/yy) containing the word &#039;places&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Design Descision ==&lt;br /&gt;
&lt;br /&gt;
A number of options were looked into before proposing this design. The options included implementing using CLucene(like flock), SQLite&#039;s FTS1 and FTS2 module, implementation using B+ Trees, using relational database etc.. The following text will briefly describe the advantage and disadvantage of all the implementation methods.&lt;br /&gt;
&lt;br /&gt;
CLucene is a full-text indexing engine that stores the index as B+ Trees in files. It uses a very efficient method for storage and retrieval. It has an excellent support for CJK languages. The Places system is a new incorporation into firefox. Hence, it is important that during its initial stages all the code that is written or used is flexible, small and tightly integrated with it. Tighter integration would allow future enhancements specific to firefox. Hence this approach was dropped.&lt;br /&gt;
&lt;br /&gt;
SQLite&#039;s FTS1 and FTS2 module are open source implementation of full-text indexing integrated with SQLite. FTS1 and FTS2 stores the text in B+ Trees and the access to the full-text index is using SQL. However, there are number of short-comings for our usage. FTS2 is still in stage where the API and data might change without backward compatibility. FTS1 does not have custom tokenizer which means no CJK support. Also, FTS1 stores the entire page duplicating what is aleady stored in the cache.&lt;br /&gt;
&lt;br /&gt;
A custom implementation using B+ Tree is a very good option but however, it would require additional B+ Tree engine. In light of availability of an efficient algorithm for implementing full-text indexing using relational database, this method is used.&lt;br /&gt;
&lt;br /&gt;
A naive implementation of full-text indexing is very costly in terms of storage. I&#039;ll briefly explain how it is so. Let us define term. A term is any word that appear in a page. So a relational database contains a table with two columns, term and id. Another table contains two columsn term id and doc id(id of the document the term appeard in). The GNU manuals were analyzed [1]. It is 5.15 Mb of text containing 958,774 occurrences of word out of which 27,554 are unique. But table 2 will require that every occurrence has a corresponding doc id. If term id were stored as int, the amount of space required to store the first column alone, would be 958,774 * 4 bytes, which is about 3 Mb. A B+ Tree implementation is atleast 3Mb more efficient. However a nice encoding scheme and storage model proposed by [2] is almost as efficient as a B+ Tree implementation. This algorithm also leverages the capabilites of relational database system while not losing too much in terms of storage and performance. Hence I propose to implement this algorithm.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Use Case ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: User&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Visit Page&amp;lt;br&amp;gt;&lt;br /&gt;
- Search&amp;lt;br&amp;gt;&lt;br /&gt;
- Clear History&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: Browser&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Expire Page&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The use cases above will be used to validate the design.&lt;br /&gt;
&lt;br /&gt;
== Database Design ==&lt;br /&gt;
&lt;br /&gt;
TODO: Check the url_table and put it here. The url_table acts as the document table. The url table will contain additionally the document length&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; &lt;br /&gt;
|+&#039;&#039;&#039;Word Table&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!columnn!!type!!bytes!!description&lt;br /&gt;
|-&lt;br /&gt;
|word||varchar||&amp;lt;=100||term for indexing(Shouldn&#039;t it be unicode? how do i store unicode?)&lt;br /&gt;
|-&lt;br /&gt;
|wordnum||integer||4||unique id. Integer works cause the number of unique words will be atmost a million. Non-english language?&lt;br /&gt;
|-&lt;br /&gt;
|doc_count||integer||4||number of documents the word occurred in&lt;br /&gt;
|-&lt;br /&gt;
|word_count||integer||4||number of occurrences of the word&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; &lt;br /&gt;
|+&#039;&#039;&#039;Posting Table&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!column!!type!!bytes!!description&lt;br /&gt;
|-&lt;br /&gt;
|wordnum||integer||4||This is the foreign key matching that in the word table&lt;br /&gt;
|-&lt;br /&gt;
|firstdoc||integer||4||lowest doc id referenced in the block&lt;br /&gt;
|-&lt;br /&gt;
|flags||tinyint||1||indicates the block type, length of doc list, sequence number&lt;br /&gt;
|-&lt;br /&gt;
|block||varbinary||&amp;lt;=255||contains encoded document and/or position postings for the word&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To Do&lt;br /&gt;
# We might need a table or two more for ranking efficiently&lt;br /&gt;
# Check if SQLite has varbinary datatype. There is a BLOB data type, I am sure.&lt;br /&gt;
&lt;br /&gt;
Note that the tables structure is subject to change to improve efficiency. New Tables might be formed and/or the table might add/remove column&lt;br /&gt;
&lt;br /&gt;
== Detailed Design ==&lt;br /&gt;
&lt;br /&gt;
Classes&lt;br /&gt;
&lt;br /&gt;
There are essentially five classes for the back-end:&lt;br /&gt;
# nsNavHistoryQuery&lt;br /&gt;
# nsNavFullTextIndexHelper&lt;br /&gt;
# nsNavFullTextIndex&lt;br /&gt;
# nsNavFullTextAnalyzer&lt;br /&gt;
&lt;br /&gt;
===nsNavHistoryQuery===&lt;br /&gt;
&lt;br /&gt;
This class is already implemented with all features except searching text. The mechanism of searching is no different from what described in http://developer.mozilla.org/en/docs/Places:Query_System. The result of nsNavHistoryQuery::search will do a full-text search with nsNavHistoryQuery::searchTerms. This is very powerful with the number of options that you can specify. Conjunctive queries can be executed. &lt;br /&gt;
&lt;br /&gt;
The search function uses the searchTerms to call nsNavFullTextIndex::searchDocument(term). This returns a url list based on a certain ranking. This function further filters based on other criteria such as date etc.. provided in the query options. The filtered url list is returned&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndex===&lt;br /&gt;
&lt;br /&gt;
This class interacts with SQLite database. This implements the algorithm for adding document to the index, removing document from the index and search for a given term. This search function is used by nsNavHistoryQuery::search function. Look at [2] for the algorithm used.&lt;br /&gt;
&lt;br /&gt;
Block is a struct used to encode and decode block. A block is variable length delta encoded. Variable Length delta Encoding, compresses very efficiently balancing speed and storage requirement.&lt;br /&gt;
&amp;lt;pre&amp;gt;struct Block {&lt;br /&gt;
	//The width of the field is 255 bytes. The return value is an int indicating number of elements in the data that were encoded in the out byte array.&lt;br /&gt;
	int encode(in int[] data, out byte[] encodedBlock) {&lt;br /&gt;
		//How to encode more efficiently, any idea?&lt;br /&gt;
		int[] bigEndian;&lt;br /&gt;
		int k = 0;&lt;br /&gt;
		data[i - 1] = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; data.length; i++) {&lt;br /&gt;
			data[i] -= data[i - 1];&lt;br /&gt;
			int j = 0;&lt;br /&gt;
			while(data[i] != 0) {&lt;br /&gt;
				bigEndian[j++] = data[i] % 128;&lt;br /&gt;
				data[i] /= 128;&lt;br /&gt;
			}&lt;br /&gt;
			for( ; j &amp;gt; 0; j--, k++) {&lt;br /&gt;
				encodedBlock[k] = (1 &amp;lt;&amp;lt; 8) &amp;amp; bigEndian[j];&lt;br /&gt;
			}&lt;br /&gt;
			encodedBlock[k++] = bigEndian[0];&lt;br /&gt;
			if (k &amp;gt; 255) &lt;br /&gt;
				return i - 1&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	void decode(in byte[] encodedBlock, out int[] data) {&lt;br /&gt;
		int j = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; encodedBlock.length; i++) {&lt;br /&gt;
			if (encodedBlock[i] &amp;amp;&amp;amp; (1 &amp;lt;&amp;lt; 8)) {&lt;br /&gt;
				data[j] *= 128;&lt;br /&gt;
				data[j] += encodedBlock[i] &amp;amp; ((1 &amp;lt;&amp;lt; 8) - 1);&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				data[j] += data[j - 1]; //Because it was delta encoded&lt;br /&gt;
				data[j++] = encodedBlock[i];&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
AddDocument(connection, document, analyzer) {&lt;br /&gt;
	AddDocument in two pass. Scan the document for terms and then invert. We have the doc id. We will require a hash map library to make it very efficient. Term is a hash map. Usage: term[&#039;termname&#039;] = array of pos.&lt;br /&gt;
&amp;lt;pre&amp;gt;	while(analyzer.hasTerms()) {&lt;br /&gt;
		cTerm = analyzer.nextTerm();&lt;br /&gt;
		term[cTerm.Name].add(cTerm.pos);&lt;br /&gt;
	}&lt;br /&gt;
	iterator i = term.iterator();&lt;br /&gt;
	while(i.hasNext()) {&lt;br /&gt;
		termName = i.next();&lt;br /&gt;
		termPos[] = term[termName];&lt;br /&gt;
	&lt;br /&gt;
		//hopefully sqlite caches query results, it will be inefficient otherwise.&lt;br /&gt;
&lt;br /&gt;
		if (term already in table) { &lt;br /&gt;
			record = executeQuery(&amp;quot;SELECT ﬁrstdoc, ﬂags, block FROM postings&lt;br /&gt;
				WHERE word = termName&lt;br /&gt;
				AND ﬁrstdoc == (Given as &amp;gt;= in [2], == is correct in my opinion)&lt;br /&gt;
				   (SELECT max (ﬁrstdoc) FROM postings&lt;br /&gt;
				    WHERE word = termName and flags &amp;lt; 128)&amp;quot;);&lt;br /&gt;
			//Refer to [2] for explanation of this.&lt;br /&gt;
			//only one record is retrieved with flags == 0 or flags between 0 and 128&lt;br /&gt;
			//when flag == 0, the block contains only document list&lt;br /&gt;
			if (flag == 0) {&lt;br /&gt;
				1) Decode the block&lt;br /&gt;
				2) See if one more document can be fitted in this.&lt;br /&gt;
				3) Yes? add to it&lt;br /&gt;
					i) find the position list&lt;br /&gt;
						positionList = executeQuery(&amp;quot;SELECT firstdoc, flags, block FROM positings&lt;br /&gt;
							where firstdoc = &lt;br /&gt;
								SELECT max(ﬁrstdoc) FROM postings&lt;br /&gt;
									WHERE word = termName&lt;br /&gt;
									AND ﬁrstdoc &amp;gt;= record.firstdoc AND flags &amp;gt;= 128&lt;br /&gt;
					ii) Try to add as many position in this block&lt;br /&gt;
					iii) when the block is done, create a new row, with firstdoc == currentdoc &lt;br /&gt;
						and flags == 128 or 129 depending on whether the prev firstdoc was same as this.&lt;br /&gt;
&lt;br /&gt;
					iv) goto ii) if there are more left.&lt;br /&gt;
				4) no?&lt;br /&gt;
					i) create a new row with firstdoc = docid, flags=2&lt;br /&gt;
					ii) To the block  add, doc id and doc freq. And all the pos. Note the position listings are never split when flag == 2.&lt;br /&gt;
						We must try to fit all the position listing in this block.99% of the case this should be possible. Make a small calculation, you&#039;ll find out i am correct&lt;br /&gt;
					iii) The rare case it is not possible? create two rows one with flags==0 and flags==128&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				//This is slightly complex in that there is both document list and position list in the same block. We have to decode the block. Try to add document id and and all the position to the position list. This might not be possible. And we&#039;ll have to create two new rows, one with flags == 0 and other with flags == 128&lt;br /&gt;
			}&lt;br /&gt;
			update the word count in the word list table appropriately&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	commit to the database&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveDocument() {&lt;br /&gt;
	Inherently inefficient because of the structure that we have adopted. Needs to be optimized. The general algorithm revolves around finding the record whose firstDoc is immediately less or same as the docId we are searching for.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;	e.g. Say we want to delete docid = 20. We got two records&lt;br /&gt;
	firstDoc = 18, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	firstDoc = 22, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	So we select the record with docid = 18 that is immediately before docid = 20;&lt;br /&gt;
	&lt;br /&gt;
	query to achieve this: SELECT word, firstdoc, block FROM postings where&lt;br /&gt;
				firstdoc = SELECT max(firstdoc) FROM postings where&lt;br /&gt;
						firstdoc &amp;lt; docIdWeAreSearchingFor&lt;br /&gt;
	This returns a number of records with flag == 0 or 0 &amp;lt; flag &amp;lt; 128 or flag == 128 or flag &amp;gt; 128. &lt;br /&gt;
	for each record we found do the following:&lt;br /&gt;
		docAndPostingTable = decodeBlock(block);&lt;br /&gt;
		we have just decoded the block using Block::decodeBlock(). &lt;br /&gt;
		docAndPostingTable.find(docId) //we check the decode block if it contains docId of our interest. &lt;br /&gt;
		if docId found&lt;br /&gt;
			Check the flag&lt;br /&gt;
			when flag == 0 //only document list&lt;br /&gt;
				remove the document and freq from the block&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				if docId == firstDoc update firstDoc with the immediately following doc&lt;br /&gt;
				if no more docs left in this row, delete the row&lt;br /&gt;
			when 0 &amp;lt; flag &amp;lt; 128, contains both document list and posting table&lt;br /&gt;
				remove the document from the block.&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				remove all the postings of the document for the term in the block&lt;br /&gt;
				if (docId == firstDoc) update firstDoc with immediately following doc&lt;br /&gt;
				if no more docs left in the row, delete the row&lt;br /&gt;
			when flag &amp;gt;= 128 //only postings table&lt;br /&gt;
				remove all the postings corresponding to the doc&lt;br /&gt;
				update the firstDoc for the record&lt;br /&gt;
				delete the record if block is empty&lt;br /&gt;
		&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SearchDocument(terms) {&lt;br /&gt;
	terms is something like: &amp;quot;mac apple panther&amp;quot;. Basically a collection of terms&lt;br /&gt;
	The ranking algorithm as described in this document is used.&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextIndexHelper===&lt;br /&gt;
&lt;br /&gt;
This class contains mechanism for scheduling documents for indexing. It works pretty much like nsNavHistoryExpire. The scheduling balances indexing with overall firefox performance. It has a timer which when expires picks an unindexed document from the history and calls nsNavFullTextIndex::addDocument to index. When and how the timer is set, dictates the overall efficiency. Whenever a user visits a page, the timer is triggered. &lt;br /&gt;
&lt;br /&gt;
When user visits a page, the history service calls OnAddURI function of this class. This function starts the timer. When timer expires the call back function is called. The function checks If there are any more document to be indexed and it has not been a long while since the user called addURI. If so, then it resets the timer and waits to expire again.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextAnalyzer===&lt;br /&gt;
&lt;br /&gt;
The analyzer design is similar to the way Lucene works. The Lucene design enables support for multiple languages.&lt;br /&gt;
The Analyzer takes the tokens generated from the tokenizer and discards those that are not required for indexing. E.g. is, a, an the can be discarded. This is enabled by by tokenStream classes. Refer to nsNavFullTextTokenStream and Lucene API for more detail.&lt;br /&gt;
&lt;br /&gt;
===nsNavFullTextTokenStream===&lt;br /&gt;
&lt;br /&gt;
TokenStream is an abstract class with three methods next, reset and close. The next method returns a token. Token is a class representing startoffset, endoffset and termtext. TokenStream needs input to tokenize. There are two concrete class for TokenStream:&lt;br /&gt;
# Tokenizer: The input is an inputstream.&lt;br /&gt;
# TokenFilter: The input is another TokenStream. This works like pipes.&lt;br /&gt;
&lt;br /&gt;
== Front-End ==&lt;br /&gt;
1) nsNavQueryParser&lt;br /&gt;
The function of this is to break the query given in human language into a graph of TermQuery and BooleanQuery. BooleanQuery is a struct with two operand(each a TermQuery or BooleanQuery) and an operator(AND, OR, NOT, XOR). Although the idea here is to implement all kind of queries as in: http://lucene.apache.org/java/docs/queryparsersyntax.html eventually. nsNavHistoryQuery is used to query using the query struct. The results of which is url_list which is displayed using view.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
[1] An Efficient Indexing Technique for full-text database systems: Justin Jobel, Alistair Moffet, Ron Sacks Davis&lt;br /&gt;
[2]Using a relational database for full-text index, Steve Putz, Xerox PARC&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=58487</id>
		<title>Places:Full Text Indexing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=58487"/>
		<updated>2007-06-02T10:44:07Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;ToDO: Formatting, it is badly formatted. Any idea on how to effectively format code?&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
Full Text Indexing feature will allow user to search for a word/phrase from the pages that he has visited. The search query will be tightly integrated with Places&#039;s nsNavHistoryService. The tighter integration will allow queries like &amp;quot;search for pages visited between 01/05/07(dd/mm/yy) to 20/05/07(dd/mm/yy) containing the word &#039;places&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Design Descision ==&lt;br /&gt;
&lt;br /&gt;
A number of options were looked into before proposing this design. The options included implementing using CLucene(like flock), SQLite&#039;s FTS1 and FTS2 module, implementation using B+ Trees, using relational database etc.. The following text will briefly describe the advantage and disadvantage of all the implementation methods.&lt;br /&gt;
&lt;br /&gt;
CLucene is a full-text indexing engine that stores the index as B+ Trees in files. It uses a very efficient method for storage and retrieval. It has an excellent support for CJK languages. The Places system is a new incorporation into firefox. Hence, it is important that during its initial stages all the code that is written or used is flexible, small and tightly integrated with it. Tighter integration would allow future enhancements specific to firefox. Hence this approach was dropped.&lt;br /&gt;
&lt;br /&gt;
SQLite&#039;s FTS1 and FTS2 module are open source implementation of full-text indexing integrated with SQLite. FTS1 and FTS2 stores the text in B+ Trees and the access to the full-text index is using SQL. However, there are number of short-comings for our usage. FTS2 is still in stage where the API and data might change without backward compatibility. FTS1 does not have custom tokenizer which means no CJK support. Also, FTS1 stores the entire page duplicating what is aleady stored in the cache.&lt;br /&gt;
&lt;br /&gt;
A custom implementation using B+ Tree is a very good option but however, it would require additional B+ Tree engine. In light of availability of an efficient algorithm for implementing full-text indexing using relational database, this method is used.&lt;br /&gt;
&lt;br /&gt;
A naive implementation of full-text indexing is very costly in terms of storage. I&#039;ll briefly explain how it is so. Let us define term. A term is any word that appear in a page. So a relational database contains a table with two columns, term and id. Another table contains two columsn term id and doc id(id of the document the term appeard in). The GNU manuals were analyzed [1]. It is 5.15 Mb of text containing 958,774 occurrences of word out of which 27,554 are unique. But table 2 will require that every occurrence has a corresponding doc id. If term id were stored as int, the amount of space required to store the first column alone, would be 958,774 * 4 bytes, which is about 3 Mb. A B+ Tree implementation is atleast 3Mb more efficient. However a nice encoding scheme and storage model proposed by [2] is almost as efficient as a B+ Tree implementation. This algorithm also leverages the capabilites of relational database system while not losing too much in terms of storage and performance. Hence I propose to implement this algorithm.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Use Case ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: User&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Visit Page&amp;lt;br&amp;gt;&lt;br /&gt;
- Search&amp;lt;br&amp;gt;&lt;br /&gt;
- Clear History&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actor: Browser&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
- Expire Page&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The use cases above will be used to validate the design.&lt;br /&gt;
&lt;br /&gt;
== Database Design ==&lt;br /&gt;
&lt;br /&gt;
TODO: Check the url_table and put it here. The url_table acts as the document table. The url table will contain additionally the document length&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; &lt;br /&gt;
|+&#039;&#039;&#039;Word Table&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!columnn!!type!!bytes!!description&lt;br /&gt;
|-&lt;br /&gt;
|word||varchar||&amp;lt;=100||term for indexing(Shouldn&#039;t it be unicode? how do i store unicode?)&lt;br /&gt;
|-&lt;br /&gt;
|wordnum||integer||4||unique id. Integer works cause the number of unique words will be atmost a million. Non-english language?&lt;br /&gt;
|-&lt;br /&gt;
|doc_count||integer||4||number of documents the word occurred in&lt;br /&gt;
|-&lt;br /&gt;
|word_count||integer||4||number of occurrences of the word&lt;br /&gt;
|}&lt;br /&gt;
Posting Table&lt;br /&gt;
column		type		bytes	description&lt;br /&gt;
wordnum		integer		4	This is the foreign key matching that in the word table&lt;br /&gt;
firstdoc	integer		4	lowest doc id referenced in the block&lt;br /&gt;
flags		tinyint		1	indicates the block type, length of doc list, sequence number&lt;br /&gt;
block 		varbinary	&amp;lt;=255	contains encoded document and/or position postings for the word&lt;br /&gt;
&lt;br /&gt;
To Do&lt;br /&gt;
1) We might need a table or two more for efficiently ranking&lt;br /&gt;
2) See how docid will referenced&lt;br /&gt;
3) check if there is varbinary. There is a BLOB&lt;br /&gt;
&lt;br /&gt;
Note that the tables structure is subject to change to improve efficiency. New Tables might be formed and/or the table might add/remove column	&lt;br /&gt;
&lt;br /&gt;
== Detailed Design ==&lt;br /&gt;
&lt;br /&gt;
Classes&lt;br /&gt;
&lt;br /&gt;
There are essentially five classes for the back-end:&lt;br /&gt;
1) nsNavHistoryQuery&lt;br /&gt;
2) nsNavFullTextIndexHelper&lt;br /&gt;
3) nsNavFullTextIndex&lt;br /&gt;
4) nsNavFullTextAnalyzer&lt;br /&gt;
&lt;br /&gt;
nsNavHistoryQuery.&lt;br /&gt;
&lt;br /&gt;
This class is already implemented with all features except searching text. The mechanism of searching is no different from what described in http://developer.mozilla.org/en/docs/Places:Query_System. The result of nsNavHistoryQuery::search will do a full-text search with nsNavHistoryQuery::searchTerms. This is very powerful with the number of options that you can specify. Conjunctive queries can be executed. &lt;br /&gt;
&lt;br /&gt;
The search function uses the searchTerms to call nsNavFullTextIndex::searchDocument(term). This returns a url list based on a certain ranking. This function further filters based on other criteria such as date etc.. provided in the query options. The filtered url list is returned&lt;br /&gt;
&lt;br /&gt;
nsNavFullTextIndex&lt;br /&gt;
&lt;br /&gt;
This class interacts with SQLite database. This implements the algorithm for adding document to the index, removing document from the index and search for a given term. This search function is used by nsNavHistoryQuery::search function. Look at [2] for the algorithm used.&lt;br /&gt;
&lt;br /&gt;
Block is a struct used to encode and decode block. A block is variable length delta encoded. Variable Length delta Encoding, compresses very efficiently balancing speed and storage requirement.&lt;br /&gt;
&lt;br /&gt;
struct Block {&lt;br /&gt;
	//The width of the field is 255 bytes. The return value is an int indicating number of elements in the data that were encoded in the out byte array.&lt;br /&gt;
	int encode(in int[] data, out byte[] encodedBlock) {&lt;br /&gt;
		//How to encode more efficiently, any idea?&lt;br /&gt;
		int[] bigEndian;&lt;br /&gt;
		int k = 0;&lt;br /&gt;
		data[i - 1] = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; data.length; i++) {&lt;br /&gt;
			data[i] -= data[i - 1];&lt;br /&gt;
			int j = 0;&lt;br /&gt;
			while(data[i] != 0) {&lt;br /&gt;
				bigEndian[j++] = data[i] % 128;&lt;br /&gt;
				data[i] /= 128;&lt;br /&gt;
			}&lt;br /&gt;
			for( ; j &amp;gt; 0; j--, k++) {&lt;br /&gt;
				encodedBlock[k] = (1 &amp;lt;&amp;lt; 8) &amp;amp; bigEndian[j];&lt;br /&gt;
			}&lt;br /&gt;
			encodedBlock[k++] = bigEndian[0];&lt;br /&gt;
			if (k &amp;gt; 255) &lt;br /&gt;
				return i - 1&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	void decode(in byte[] encodedBlock, out int[] data) {&lt;br /&gt;
		int j = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; encodedBlock.length; i++) {&lt;br /&gt;
			if (encodedBlock[i] &amp;amp;&amp;amp; (1 &amp;lt;&amp;lt; 8)) {&lt;br /&gt;
				data[j] *= 128;&lt;br /&gt;
				data[j] += encodedBlock[i] &amp;amp; ((1 &amp;lt;&amp;lt; 8) - 1);&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				data[j] += data[j - 1]; //Because it was delta encoded&lt;br /&gt;
				data[j++] = encodedBlock[i];&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
AddDocument(connection, document, analyzer) {&lt;br /&gt;
	AddDocument in two pass. Scan the document for terms and then invert. We have the doc id. We will require a hash map library to make it very efficient. Term is a hash map. Usage: term[&#039;termname&#039;] = array of pos.&lt;br /&gt;
	while(analyzer.hasTerms()) {&lt;br /&gt;
		cTerm = analyzer.nextTerm();&lt;br /&gt;
		term[cTerm.Name].add(cTerm.pos);&lt;br /&gt;
	}&lt;br /&gt;
	iterator i = term.iterator();&lt;br /&gt;
	while(i.hasNext()) {&lt;br /&gt;
		termName = i.next();&lt;br /&gt;
		termPos[] = term[termName];&lt;br /&gt;
	&lt;br /&gt;
		//hopefully sqlite caches query results, it will be inefficient otherwise.&lt;br /&gt;
&lt;br /&gt;
		if (term already in table) { &lt;br /&gt;
			record = executeQuery(&amp;quot;SELECT ﬁrstdoc, ﬂags, block FROM postings&lt;br /&gt;
				WHERE word = termName&lt;br /&gt;
				AND ﬁrstdoc == (Given as &amp;gt;= in [2], == is correct in my opinion)&lt;br /&gt;
				   (SELECT max (ﬁrstdoc) FROM postings&lt;br /&gt;
				    WHERE word = termName and flags &amp;lt; 128)&amp;quot;);&lt;br /&gt;
			//Refer to [2] for explanation of this.&lt;br /&gt;
			//only one record is retrieved with flags == 0 or flags between 0 and 128&lt;br /&gt;
			//when flag == 0, the block contains only document list&lt;br /&gt;
			if (flag == 0) {&lt;br /&gt;
				1) Decode the block&lt;br /&gt;
				2) See if one more document can be fitted in this.&lt;br /&gt;
				3) Yes? add to it&lt;br /&gt;
					i) find the position list&lt;br /&gt;
						positionList = executeQuery(&amp;quot;SELECT firstdoc, flags, block FROM positings&lt;br /&gt;
							where firstdoc = &lt;br /&gt;
								SELECT max(ﬁrstdoc) FROM postings&lt;br /&gt;
									WHERE word = termName&lt;br /&gt;
									AND ﬁrstdoc &amp;gt;= record.firstdoc AND flags &amp;gt;= 128&lt;br /&gt;
					ii) Try to add as many position in this block&lt;br /&gt;
					iii) when the block is done, create a new row, with firstdoc == currentdoc &lt;br /&gt;
						and flags == 128 or 129 depending on whether the prev firstdoc was same as this.&lt;br /&gt;
&lt;br /&gt;
					iv) goto ii) if there are more left.&lt;br /&gt;
				4) no?&lt;br /&gt;
					i) create a new row with firstdoc = docid, flags=2&lt;br /&gt;
					ii) To the block  add, doc id and doc freq. And all the pos. Note the position listings are never split when flag == 2.&lt;br /&gt;
						We must try to fit all the position listing in this block.99% of the case this should be possible. Make a small calculation, you&#039;ll find out i am correct&lt;br /&gt;
					iii) The rare case it is not possible? create two rows one with flags==0 and flags==128&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				//This is slightly complex in that there is both document list and position list in the same block. We have to decode the block. Try to add document id and and all the position to the position list. This might not be possible. And we&#039;ll have to create two new rows, one with flags == 0 and other with flags == 128&lt;br /&gt;
			}&lt;br /&gt;
			update the word count in the word list table appropriately&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	commit to the database&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveDocument() {&lt;br /&gt;
	Inherently inefficient because of the structure that we have adopted. Needs to be optimized. The general algorithm revolves around finding the record whose firstDoc is immediately less or same as the docId we are searching for.&lt;br /&gt;
&lt;br /&gt;
	e.g. Say we want to delete docid = 20. We got two records&lt;br /&gt;
	firstDoc = 18, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	firstDoc = 22, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	So we select the record with docid = 18 that is immediately before docid = 20;&lt;br /&gt;
	&lt;br /&gt;
	query to achieve this: SELECT word, firstdoc, block FROM postings where&lt;br /&gt;
				firstdoc = SELECT max(firstdoc) FROM postings where&lt;br /&gt;
						firstdoc &amp;lt; docIdWeAreSearchingFor&lt;br /&gt;
	This returns a number of records with flag == 0 or 0 &amp;lt; flag &amp;lt; 128 or flag == 128 or flag &amp;gt; 128. &lt;br /&gt;
	for each record we found do the following:&lt;br /&gt;
		docAndPostingTable = decodeBlock(block);&lt;br /&gt;
		we have just decoded the block using Block::decodeBlock(). &lt;br /&gt;
		docAndPostingTable.find(docId) //we check the decode block if it contains docId of our interest. &lt;br /&gt;
		if docId found&lt;br /&gt;
			Check the flag&lt;br /&gt;
			when flag == 0 //only document list&lt;br /&gt;
				remove the document and freq from the block&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				if docId == firstDoc update firstDoc with the immediately following doc&lt;br /&gt;
				if no more docs left in this row, delete the row&lt;br /&gt;
			when 0 &amp;lt; flag &amp;lt; 128, contains both document list and posting table&lt;br /&gt;
				remove the document from the block.&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				remove all the postings of the document for the term in the block&lt;br /&gt;
				if (docId == firstDoc) update firstDoc with immediately following doc&lt;br /&gt;
				if no more docs left in the row, delete the row&lt;br /&gt;
			when flag &amp;gt;= 128 //only postings table&lt;br /&gt;
				remove all the postings corresponding to the doc&lt;br /&gt;
				update the firstDoc for the record&lt;br /&gt;
				delete the record if block is empty&lt;br /&gt;
		&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SearchDocument(terms) {&lt;br /&gt;
	terms is something like: &amp;quot;mac apple panther&amp;quot;. Basically a collection of terms&lt;br /&gt;
	The ranking algorithm as described in this document is used.&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
nsNavFullTextIndexHelper&lt;br /&gt;
&lt;br /&gt;
This class contains mechanism for scheduling documents for indexing. It works pretty much like nsNavHistoryExpire. The scheduling balances indexing with overall firefox performance. It has a timer which when expires picks an unindexed document from the history and calls nsNavFullTextIndex::addDocument to index. When and how the timer is set, dictates the overall efficiency. Whenever a user visits a page, the timer is triggered. &lt;br /&gt;
&lt;br /&gt;
When user visits a page, the history service calls OnAddURI function of this class. This function starts the timer. When timer expires the call back function is called. The function checks If there are any more document to be indexed and it has not been a long while since the user called addURI. If so, then it resets the timer and waits to expire again.&lt;br /&gt;
&lt;br /&gt;
nsNavFullTextAnalyzer&lt;br /&gt;
&lt;br /&gt;
The analyzer design is similar to the way Lucene works. The Lucene design enables support for multiple languages.&lt;br /&gt;
The Analyzer takes the tokens generated from the tokenizer and discards those that are not required for indexing. E.g. is, a, an the can be discarded. This is enabled by by tokenStream classes. Refer to nsNavFullTextTokenStream and Lucene API for more detail.&lt;br /&gt;
&lt;br /&gt;
nsNavFullTextTokenStream&lt;br /&gt;
&lt;br /&gt;
TokenStream is an abstract class with three methods next, reset and close. The next method returns a token. Token is a class representing startoffset, endoffset and termtext. TokenStream needs input to tokenize. There are two concrete class for TokenStream:&lt;br /&gt;
1) Tokenizer: The input is an inputstream.&lt;br /&gt;
2) TokenFilter: The input is another TokenStream. This works like pipes.&lt;br /&gt;
&lt;br /&gt;
== Front-End ==&lt;br /&gt;
1) nsNavQueryParser&lt;br /&gt;
The function of this is to break the query given in human language into a graph of TermQuery and BooleanQuery. BooleanQuery is a struct with two operand(each a TermQuery or BooleanQuery) and an operator(AND, OR, NOT, XOR). Although the idea here is to implement all kind of queries as in: http://lucene.apache.org/java/docs/queryparsersyntax.html eventually. nsNavHistoryQuery is used to query using the query struct. The results of which is url_list which is displayed using view.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
[1] An Efficient Indexing Technique for full-text database systems: Justin Jobel, Alistair Moffet, Ron Sacks Davis&lt;br /&gt;
[2]Using a relational database for full-text index, Steve Putz, Xerox PARC&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=58072</id>
		<title>Places:Full Text Indexing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=58072"/>
		<updated>2007-05-30T13:22:57Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;ToDO: Formatting, it is badly formatted. Any idea on how to effectively format code?&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
Full Text Indexing feature will allow user to search for a word/phrase from the pages that he has visited. The search query will be tightly integrated with Places&#039;s nsNavHistoryService. The tighter integration will allow queries like &amp;quot;search for pages visited between 01/05/07(dd/mm/yy) to 20/05/07(dd/mm/yy) containing the word &#039;places&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Design Descision ==&lt;br /&gt;
&lt;br /&gt;
A number of options were looked into before proposing this design. The options included implementing using CLucene(like flock), SQLite&#039;s FTS1 and FTS2 module, implementation using B+ Trees, using relational database etc.. The following text will briefly describe the advantage and disadvantage of all the implementation methods.&lt;br /&gt;
&lt;br /&gt;
CLucene is a full-text indexing engine that stores the index as B+ Trees in files. It uses a very efficient method for storage and retrieval. It has an excellent support for CJK languages. The Places system is a new incorporation into firefox. Hence, it is important that during its initial stages all the code that is written or used is flexible, small and tightly integrated with it. Tighter integration would allow future enhancements specific to firefox. Hence this approach was dropped.&lt;br /&gt;
&lt;br /&gt;
SQLite&#039;s FTS1 and FTS2 module are open source implementation of full-text indexing integrated with SQLite. FTS1 and FTS2 stores the text in B+ Trees and the access to the full-text index is using SQL. However, there are number of short-comings for our usage. FTS2 is still in stage where the API and data might change without backward compatibility. FTS1 does not have custom tokenizer which means no CJK support. Also, FTS1 stores the entire page duplicating what is aleady stored in the cache.&lt;br /&gt;
&lt;br /&gt;
A custom implementation using B+ Tree is a very good option but however, it would require additional B+ Tree engine. In light of availability of an efficient algorithm for implementing full-text indexing using relational database, this method is used.&lt;br /&gt;
&lt;br /&gt;
A naive implementation of full-text indexing is very costly in terms of storage. I&#039;ll briefly explain how it is so. Let us define term. A term is any word that appear in a page. So a relational database contains a table with two columns, term and id. Another table contains two columsn term id and doc id(id of the document the term appeard in). The GNU manuals were analyzed [1]. It is 5.15 Mb of text containing 958,774 occurrences of word out of which 27,554 are unique. But table 2 will require that every occurrence has a corresponding doc id. If term id were stored as int, the amount of space required to store the first column alone, would be 958,774 * 4 bytes, which is about 3 Mb. A B+ Tree implementation is atleast 3Mb more efficient. However a nice encoding scheme and storage model proposed by [2] is almost as efficient as a B+ Tree implementation. This algorithm also leverages the capabilites of relational database system while not losing too much in terms of storage and performance. Hence I propose to implement this algorithm.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Use Case ==&lt;br /&gt;
&lt;br /&gt;
Actor: User&lt;br /&gt;
- Visit Page&lt;br /&gt;
- Search&lt;br /&gt;
- Clear History&lt;br /&gt;
&lt;br /&gt;
Actor: Browser&lt;br /&gt;
- Expire Page&lt;br /&gt;
&lt;br /&gt;
The use cases above will be used to validate the design.&lt;br /&gt;
&lt;br /&gt;
== Database Design ==&lt;br /&gt;
&lt;br /&gt;
TODO: Check the url_table and put it here. The url_table acts as the document table. The url table will contain additionally the document length&lt;br /&gt;
&lt;br /&gt;
Word Table&lt;br /&gt;
columnn		type		bytes	description&lt;br /&gt;
word		varchar		&amp;lt;=100	term for indexing(Shouldn&#039;t it be unicode? how do i store unicode?)&lt;br /&gt;
wordnum		integer		4	unique id. Integer works cause the number of unique words will be atmost a million. Non-english language?&lt;br /&gt;
doc_count	integer		4	number of documents the word occurred in&lt;br /&gt;
word_count	integer		4	number of occurrences of the word&lt;br /&gt;
&lt;br /&gt;
Posting Table&lt;br /&gt;
column		type		bytes	description&lt;br /&gt;
wordnum		integer		4	This is the foreign key matching that in the word table&lt;br /&gt;
firstdoc	integer		4	lowest doc id referenced in the block&lt;br /&gt;
flags		tinyint		1	indicates the block type, length of doc list, sequence number&lt;br /&gt;
block 		varbinary	&amp;lt;=255	contains encoded document and/or position postings for the word&lt;br /&gt;
&lt;br /&gt;
To Do&lt;br /&gt;
1) We might need a table or two more for efficiently ranking&lt;br /&gt;
2) See how docid will referenced&lt;br /&gt;
3) check if there is varbinary. There is a BLOB&lt;br /&gt;
&lt;br /&gt;
Note that the tables structure is subject to change to improve efficiency. New Tables might be formed and/or the table might add/remove column	&lt;br /&gt;
&lt;br /&gt;
== Detailed Design ==&lt;br /&gt;
&lt;br /&gt;
Classes&lt;br /&gt;
&lt;br /&gt;
There are essentially five classes for the back-end:&lt;br /&gt;
1) nsNavHistoryQuery&lt;br /&gt;
2) nsNavFullTextIndexHelper&lt;br /&gt;
3) nsNavFullTextIndex&lt;br /&gt;
4) nsNavFullTextAnalyzer&lt;br /&gt;
&lt;br /&gt;
nsNavHistoryQuery.&lt;br /&gt;
&lt;br /&gt;
This class is already implemented with all features except searching text. The mechanism of searching is no different from what described in http://developer.mozilla.org/en/docs/Places:Query_System. The result of nsNavHistoryQuery::search will do a full-text search with nsNavHistoryQuery::searchTerms. This is very powerful with the number of options that you can specify. Conjunctive queries can be executed. &lt;br /&gt;
&lt;br /&gt;
The search function uses the searchTerms to call nsNavFullTextIndex::searchDocument(term). This returns a url list based on a certain ranking. This function further filters based on other criteria such as date etc.. provided in the query options. The filtered url list is returned&lt;br /&gt;
&lt;br /&gt;
nsNavFullTextIndex&lt;br /&gt;
&lt;br /&gt;
This class interacts with SQLite database. This implements the algorithm for adding document to the index, removing document from the index and search for a given term. This search function is used by nsNavHistoryQuery::search function. Look at [2] for the algorithm used.&lt;br /&gt;
&lt;br /&gt;
Block is a struct used to encode and decode block. A block is variable length delta encoded. Variable Length delta Encoding, compresses very efficiently balancing speed and storage requirement.&lt;br /&gt;
&lt;br /&gt;
struct Block {&lt;br /&gt;
	//The width of the field is 255 bytes. The return value is an int indicating number of elements in the data that were encoded in the out byte array.&lt;br /&gt;
	int encode(in int[] data, out byte[] encodedBlock) {&lt;br /&gt;
		//How to encode more efficiently, any idea?&lt;br /&gt;
		int[] bigEndian;&lt;br /&gt;
		int k = 0;&lt;br /&gt;
		data[i - 1] = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; data.length; i++) {&lt;br /&gt;
			data[i] -= data[i - 1];&lt;br /&gt;
			int j = 0;&lt;br /&gt;
			while(data[i] != 0) {&lt;br /&gt;
				bigEndian[j++] = data[i] % 128;&lt;br /&gt;
				data[i] /= 128;&lt;br /&gt;
			}&lt;br /&gt;
			for( ; j &amp;gt; 0; j--, k++) {&lt;br /&gt;
				encodedBlock[k] = (1 &amp;lt;&amp;lt; 8) &amp;amp; bigEndian[j];&lt;br /&gt;
			}&lt;br /&gt;
			encodedBlock[k++] = bigEndian[0];&lt;br /&gt;
			if (k &amp;gt; 255) &lt;br /&gt;
				return i - 1&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	void decode(in byte[] encodedBlock, out int[] data) {&lt;br /&gt;
		int j = 0;&lt;br /&gt;
		for(int i = 0; i &amp;lt; encodedBlock.length; i++) {&lt;br /&gt;
			if (encodedBlock[i] &amp;amp;&amp;amp; (1 &amp;lt;&amp;lt; 8)) {&lt;br /&gt;
				data[j] *= 128;&lt;br /&gt;
				data[j] += encodedBlock[i] &amp;amp; ((1 &amp;lt;&amp;lt; 8) - 1);&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				data[j] += data[j - 1]; //Because it was delta encoded&lt;br /&gt;
				data[j++] = encodedBlock[i];&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
AddDocument(connection, document, analyzer) {&lt;br /&gt;
	AddDocument in two pass. Scan the document for terms and then invert. We have the doc id. We will require a hash map library to make it very efficient. Term is a hash map. Usage: term[&#039;termname&#039;] = array of pos.&lt;br /&gt;
	while(analyzer.hasTerms()) {&lt;br /&gt;
		cTerm = analyzer.nextTerm();&lt;br /&gt;
		term[cTerm.Name].add(cTerm.pos);&lt;br /&gt;
	}&lt;br /&gt;
	iterator i = term.iterator();&lt;br /&gt;
	while(i.hasNext()) {&lt;br /&gt;
		termName = i.next();&lt;br /&gt;
		termPos[] = term[termName];&lt;br /&gt;
	&lt;br /&gt;
		//hopefully sqlite caches query results, it will be inefficient otherwise.&lt;br /&gt;
&lt;br /&gt;
		if (term already in table) { &lt;br /&gt;
			record = executeQuery(&amp;quot;SELECT ﬁrstdoc, ﬂags, block FROM postings&lt;br /&gt;
				WHERE word = termName&lt;br /&gt;
				AND ﬁrstdoc == (Given as &amp;gt;= in [2], == is correct in my opinion)&lt;br /&gt;
				   (SELECT max (ﬁrstdoc) FROM postings&lt;br /&gt;
				    WHERE word = termName and flags &amp;lt; 128)&amp;quot;);&lt;br /&gt;
			//Refer to [2] for explanation of this.&lt;br /&gt;
			//only one record is retrieved with flags == 0 or flags between 0 and 128&lt;br /&gt;
			//when flag == 0, the block contains only document list&lt;br /&gt;
			if (flag == 0) {&lt;br /&gt;
				1) Decode the block&lt;br /&gt;
				2) See if one more document can be fitted in this.&lt;br /&gt;
				3) Yes? add to it&lt;br /&gt;
					i) find the position list&lt;br /&gt;
						positionList = executeQuery(&amp;quot;SELECT firstdoc, flags, block FROM positings&lt;br /&gt;
							where firstdoc = &lt;br /&gt;
								SELECT max(ﬁrstdoc) FROM postings&lt;br /&gt;
									WHERE word = termName&lt;br /&gt;
									AND ﬁrstdoc &amp;gt;= record.firstdoc AND flags &amp;gt;= 128&lt;br /&gt;
					ii) Try to add as many position in this block&lt;br /&gt;
					iii) when the block is done, create a new row, with firstdoc == currentdoc &lt;br /&gt;
						and flags == 128 or 129 depending on whether the prev firstdoc was same as this.&lt;br /&gt;
&lt;br /&gt;
					iv) goto ii) if there are more left.&lt;br /&gt;
				4) no?&lt;br /&gt;
					i) create a new row with firstdoc = docid, flags=2&lt;br /&gt;
					ii) To the block  add, doc id and doc freq. And all the pos. Note the position listings are never split when flag == 2.&lt;br /&gt;
						We must try to fit all the position listing in this block.99% of the case this should be possible. Make a small calculation, you&#039;ll find out i am correct&lt;br /&gt;
					iii) The rare case it is not possible? create two rows one with flags==0 and flags==128&lt;br /&gt;
			}&lt;br /&gt;
			else {&lt;br /&gt;
				//This is slightly complex in that there is both document list and position list in the same block. We have to decode the block. Try to add document id and and all the position to the position list. This might not be possible. And we&#039;ll have to create two new rows, one with flags == 0 and other with flags == 128&lt;br /&gt;
			}&lt;br /&gt;
			update the word count in the word list table appropriately&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	commit to the database&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveDocument() {&lt;br /&gt;
	Inherently inefficient because of the structure that we have adopted. Needs to be optimized. The general algorithm revolves around finding the record whose firstDoc is immediately less or same as the docId we are searching for.&lt;br /&gt;
&lt;br /&gt;
	e.g. Say we want to delete docid = 20. We got two records&lt;br /&gt;
	firstDoc = 18, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	firstDoc = 22, block=&amp;quot;blahblah&amp;quot;&lt;br /&gt;
	So we select the record with docid = 18 that is immediately before docid = 20;&lt;br /&gt;
	&lt;br /&gt;
	query to achieve this: SELECT word, firstdoc, block FROM postings where&lt;br /&gt;
				firstdoc = SELECT max(firstdoc) FROM postings where&lt;br /&gt;
						firstdoc &amp;lt; docIdWeAreSearchingFor&lt;br /&gt;
	This returns a number of records with flag == 0 or 0 &amp;lt; flag &amp;lt; 128 or flag == 128 or flag &amp;gt; 128. &lt;br /&gt;
	for each record we found do the following:&lt;br /&gt;
		docAndPostingTable = decodeBlock(block);&lt;br /&gt;
		we have just decoded the block using Block::decodeBlock(). &lt;br /&gt;
		docAndPostingTable.find(docId) //we check the decode block if it contains docId of our interest. &lt;br /&gt;
		if docId found&lt;br /&gt;
			Check the flag&lt;br /&gt;
			when flag == 0 //only document list&lt;br /&gt;
				remove the document and freq from the block&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				if docId == firstDoc update firstDoc with the immediately following doc&lt;br /&gt;
				if no more docs left in this row, delete the row&lt;br /&gt;
			when 0 &amp;lt; flag &amp;lt; 128, contains both document list and posting table&lt;br /&gt;
				remove the document from the block.&lt;br /&gt;
				update the word count for the term in word table&lt;br /&gt;
				update the delta coding for other doc in the block.&lt;br /&gt;
				remove all the postings of the document for the term in the block&lt;br /&gt;
				if (docId == firstDoc) update firstDoc with immediately following doc&lt;br /&gt;
				if no more docs left in the row, delete the row&lt;br /&gt;
			when flag &amp;gt;= 128 //only postings table&lt;br /&gt;
				remove all the postings corresponding to the doc&lt;br /&gt;
				update the firstDoc for the record&lt;br /&gt;
				delete the record if block is empty&lt;br /&gt;
		&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SearchDocument(terms) {&lt;br /&gt;
	terms is something like: &amp;quot;mac apple panther&amp;quot;. Basically a collection of terms&lt;br /&gt;
	The ranking algorithm as described in this document is used.&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
nsNavFullTextIndexHelper&lt;br /&gt;
&lt;br /&gt;
This class contains mechanism for scheduling documents for indexing. It works pretty much like nsNavHistoryExpire. The scheduling balances indexing with overall firefox performance. It has a timer which when expires picks an unindexed document from the history and calls nsNavFullTextIndex::addDocument to index. When and how the timer is set, dictates the overall efficiency. Whenever a user visits a page, the timer is triggered. &lt;br /&gt;
&lt;br /&gt;
When user visits a page, the history service calls OnAddURI function of this class. This function starts the timer. When timer expires the call back function is called. The function checks If there are any more document to be indexed and it has not been a long while since the user called addURI. If so, then it resets the timer and waits to expire again.&lt;br /&gt;
&lt;br /&gt;
nsNavFullTextAnalyzer&lt;br /&gt;
&lt;br /&gt;
The analyzer design is similar to the way Lucene works. The Lucene design enables support for multiple languages.&lt;br /&gt;
The Analyzer takes the tokens generated from the tokenizer and discards those that are not required for indexing. E.g. is, a, an the can be discarded. This is enabled by by tokenStream classes. Refer to nsNavFullTextTokenStream and Lucene API for more detail.&lt;br /&gt;
&lt;br /&gt;
nsNavFullTextTokenStream&lt;br /&gt;
&lt;br /&gt;
TokenStream is an abstract class with three methods next, reset and close. The next method returns a token. Token is a class representing startoffset, endoffset and termtext. TokenStream needs input to tokenize. There are two concrete class for TokenStream:&lt;br /&gt;
1) Tokenizer: The input is an inputstream.&lt;br /&gt;
2) TokenFilter: The input is another TokenStream. This works like pipes.&lt;br /&gt;
&lt;br /&gt;
== Front-End ==&lt;br /&gt;
1) nsNavQueryParser&lt;br /&gt;
The function of this is to break the query given in human language into a graph of TermQuery and BooleanQuery. BooleanQuery is a struct with two operand(each a TermQuery or BooleanQuery) and an operator(AND, OR, NOT, XOR). Although the idea here is to implement all kind of queries as in: http://lucene.apache.org/java/docs/queryparsersyntax.html eventually. nsNavHistoryQuery is used to query using the query struct. The results of which is url_list which is displayed using view.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
[1] An Efficient Indexing Technique for full-text database systems: Justin Jobel, Alistair Moffet, Ron Sacks Davis&lt;br /&gt;
[2]Using a relational database for full-text index, Steve Putz, Xerox PARC&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=57862</id>
		<title>Places:Full Text Indexing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=57862"/>
		<updated>2007-05-26T15:43:48Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: /* Detailed Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
Full Text Indexing feature will allow user to search for a word/phrase from the pages that he has visited. The search query will be tightly integrated with Places&#039;s nsNavHistoryService. The tighter integration will allow queries like &amp;quot;search for pages visited between 01/05/07(dd/mm/yy) to 20/05/07(dd/mm/yy) containing the word &#039;places&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Design Descision ==&lt;br /&gt;
&lt;br /&gt;
A number of options were looked into before proposing this design. The options included implementing using CLucene(like flock), SQLite&#039;s FTS1 and FTS2 module, implementation using B+ Trees, using relational database etc.. The following text will briefly describe the advantage and disadvantage of all the implementation methods.&lt;br /&gt;
&lt;br /&gt;
CLucene is a full-text indexing engine that stores the index as B+ Trees in files. It uses a very efficient method for storage and retrieval. It has an excellent support for CJK languages. The Places system is a new incorporation into firefox. Hence, it is important that during its initial stages all the code that is written or used is flexible, small and tightly integrated with it. Tighter integration would allow future enhancements specific to firefox. Hence this approach was dropped.&lt;br /&gt;
&lt;br /&gt;
SQLite&#039;s FTS1 and FTS2 module are open source implementation of full-text indexing integrated with SQLite. FTS1 and FTS2 stores the text in B+ Trees and the access to the full-text index is using SQL. However, there are number of short-comings for our usage. FTS2 is still in stage where the API and data might change without backward compatibility. FTS1 does not have custom tokenizer which means no CJK support. Also, FTS1 stores the entire page duplicating what is aleady stored in the cache.&lt;br /&gt;
&lt;br /&gt;
A custom implementation using B+ Tree is a very good option but however, it would require additional B+ Tree engine. In light of availability of an efficient algorithm for implementing full-text indexing using relational database, this method is used.&lt;br /&gt;
&lt;br /&gt;
A naive implementation of full-text indexing is very costly in terms of storage. I&#039;ll briefly explain how it is so. Let us define term. A term is any word that appear in a page. So a relational database contains a table with two columns, term and id. Another table contains two columsn term id and doc id(id of the document the term appeard in). The GNU manuals were analyzed [1]. It is 5.15 Mb of text containing 958,774 occurrences of word out of which 27,554 are unique. But table 2 will require that every occurrence has a corresponding doc id. If term id were stored as int, the amount of space required to store the first column alone, would be 958,774 * 4 bytes, which is about 3 Mb. A B+ Tree implementation is atleast 3Mb more efficient. However a nice encoding scheme and storage model proposed by [2] is almost as efficient as a B+ Tree implementation. This algorithm also leverages the capabilites of relational database system while not losing too much in terms of storage and performance. Hence I propose to implement this algorithm.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: Formatting. Learn how wiki formatting is done&lt;br /&gt;
== Use Case ==&lt;br /&gt;
&lt;br /&gt;
Actor: User&lt;br /&gt;
- Visit Page&lt;br /&gt;
- Search&lt;br /&gt;
- Clear History&lt;br /&gt;
&lt;br /&gt;
Actor: Browser&lt;br /&gt;
- Expire Page&lt;br /&gt;
&lt;br /&gt;
The use cases above will be used to validate the design.&lt;br /&gt;
&lt;br /&gt;
== Database Design ==&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&lt;br /&gt;
Word Table&lt;br /&gt;
columnn		type		bytes	description&lt;br /&gt;
word		varchar		&amp;lt;=100	term for indexing(Shouldn&#039;t it be unicode? how do i store unicode?)&lt;br /&gt;
wordnum		integer		4	unique id. Integer works cause the number of unique words will be atmost a million. Non-english language?&lt;br /&gt;
doc_count	integer		4	number of documents the word occurred in&lt;br /&gt;
word_count	integer		4	number of occurrences of the word&lt;br /&gt;
&lt;br /&gt;
Posting Table&lt;br /&gt;
column		type		bytes	description&lt;br /&gt;
wordnum		integer		4	This is the foreign key matching that in the word table&lt;br /&gt;
firstdoc	integer		4	lowest doc id referenced in the block&lt;br /&gt;
flags		tinyint		1	indicates the block type, length of doc list, sequence number&lt;br /&gt;
block 		varbinary	&amp;lt;=255	contains encoded document and/or position postings for the word&lt;br /&gt;
&lt;br /&gt;
To Do&lt;br /&gt;
1) We might need a table or two more for ranking related data&lt;br /&gt;
2) See how docid will referenced&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
Note that the tables structure is subject to change to improve efficiency. New Tables might be formed and/or the table might add/remove column	&lt;br /&gt;
&lt;br /&gt;
== Detailed Design ==&lt;br /&gt;
&lt;br /&gt;
Classes&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are essentially five classes for the back-end:&amp;lt;br&amp;gt;&lt;br /&gt;
1) nsNavHistoryQuery&amp;lt;br&amp;gt;&lt;br /&gt;
2) nsNavFullTextIndexHelper&amp;lt;br&amp;gt;&lt;br /&gt;
3) nsNavFullTextIndex&amp;lt;br&amp;gt;&lt;br /&gt;
4) nsNavFullTextTokenizer&amp;lt;br&amp;gt;&lt;br /&gt;
5) nsNavFullTextAnalyzer&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
nsNavHistoryQuery&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This class is already implemented with all features except searching text. The mechanism of searching is no different from what described in http://developer.mozilla.org/en/docs/Places:Query_System. The result of nsNavHistoryQuery::search will do a full-text search with nsNavHistoryQuery::searchTerms. This is very powerful with the number of options that you can specify. Conjunctive queries can be executed. &lt;br /&gt;
&lt;br /&gt;
nsNavFullTextIndex&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This class interacts with SQLite database. This implements the algorithm for adding document to the index, removing document from the index and search for a given term. This search function is used by nsNavHistoryQuery::search function. Look at [2] for the algorithm used.&lt;br /&gt;
&lt;br /&gt;
Block is a struct used to encode and decode block. A block is variable length delta encoded.&lt;br /&gt;
Variable Length delta Encoding, compresses very efficiently balancing speed and storage requirement&lt;br /&gt;
&lt;br /&gt;
struct Block {&amp;lt;br&amp;gt;&lt;br /&gt;
: //The width of the field is 250 bytes. The return value is an int indicating number of elements in the data that were encoded in the out byte array.&amp;lt;br&amp;gt;&lt;br /&gt;
: int encode(in int[] data, out byte[] encodedBlock) {&amp;lt;br&amp;gt;&lt;br /&gt;
:: //How to encode more efficiently, any idea?&amp;lt;br&amp;gt;&lt;br /&gt;
:: int[] bigEndian;&amp;lt;br&amp;gt;&lt;br /&gt;
:: int k = 0;&amp;lt;br&amp;gt;&lt;br /&gt;
:: data[i - 1] = 0;&amp;lt;br&amp;gt;&lt;br /&gt;
:: for(int i = 0; i &amp;lt; data.length; i++) {&amp;lt;br&amp;gt;&lt;br /&gt;
::: data[i] -= data[i - 1];&amp;lt;br&amp;gt;&lt;br /&gt;
::: int j = 0;&amp;lt;br&amp;gt;&lt;br /&gt;
::: while(data[i] != 0) {&amp;lt;br&amp;gt;&lt;br /&gt;
:::: bigEndian[j++] = data[i] % 128;&amp;lt;br&amp;gt;&lt;br /&gt;
:::: data[i] /= 128;&amp;lt;br&amp;gt;&lt;br /&gt;
::: }&amp;lt;br&amp;gt;&lt;br /&gt;
::: for( ; j &amp;gt; 0; j--, k++) {&amp;lt;br&amp;gt;&lt;br /&gt;
:::: encodedBlock[k] = (1 &amp;lt;&amp;lt; 8) &amp;amp; bigEndian[j];&amp;lt;br&amp;gt;&lt;br /&gt;
::: }&amp;lt;br&amp;gt;&lt;br /&gt;
::: encodedBlock[k++] = bigEndian[0];&amp;lt;br&amp;gt;&lt;br /&gt;
::: if (k &amp;gt; 255) &amp;lt;br&amp;gt;&lt;br /&gt;
:::: return i - 1&amp;lt;br&amp;gt;&lt;br /&gt;
::: }&amp;lt;br&amp;gt;&lt;br /&gt;
::}&amp;lt;br&amp;gt;&lt;br /&gt;
: void decode(in byte[] encodedBlock, out int[] data) {&amp;lt;br&amp;gt;&lt;br /&gt;
:: int j = 0;&amp;lt;br&amp;gt;&lt;br /&gt;
:: for(int i = 0; i &amp;lt; encodedBlock.length; i++) {&amp;lt;br&amp;gt;&lt;br /&gt;
::: if (encodedBlock[i] &amp;amp;&amp;amp; (1 &amp;lt;&amp;lt; 8)) {&amp;lt;br&amp;gt;&lt;br /&gt;
::: data[j] *= 128;&amp;lt;br&amp;gt;&lt;br /&gt;
::: data[j] += encodedBlock[i] &amp;amp; ((1 &amp;lt;&amp;lt; 8) - 1);&amp;lt;br&amp;gt;&lt;br /&gt;
:: }&amp;lt;br&amp;gt;&lt;br /&gt;
:: else {&amp;lt;br&amp;gt;&lt;br /&gt;
::: data[j] += data[j - 1]; //Because it is delta encoded &amp;lt;br&amp;gt;&lt;br /&gt;
::: data[j++] = encodedBlock[i];&amp;lt;br&amp;gt;&lt;br /&gt;
:: }&amp;lt;br&amp;gt;&lt;br /&gt;
: }&amp;lt;br&amp;gt;&lt;br /&gt;
	}&amp;lt;br&amp;gt;&lt;br /&gt;
}&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
AddDocument(connection, document, analyzer) {&amp;lt;br&amp;gt;&lt;br /&gt;
	AddDocument in two pass. Scan the document for terms and then invert.&lt;br /&gt;
	we have the doc id. We will require a hash map library to make it very efficient&lt;br /&gt;
	term is a hash map.&amp;lt;br&amp;gt;&lt;br /&gt;
	Usage: 	term[&#039;termname&#039;] = array of pos.&lt;br /&gt;
	while(analyzer.hasTerms()) {&amp;lt;br&amp;gt;&lt;br /&gt;
		cTerm = analyzer.nextTerm();&amp;lt;br&amp;gt;&lt;br /&gt;
		term[cTerm.Name].add(cTerm.pos);&amp;lt;br&amp;gt;&lt;br /&gt;
	}&amp;lt;br&amp;gt;&lt;br /&gt;
	iterator i = term.iterator();&amp;lt;br&amp;gt;&lt;br /&gt;
	while(i.hasNext()) {&amp;lt;br&amp;gt;&lt;br /&gt;
		termName = i.next();&amp;lt;br&amp;gt;&lt;br /&gt;
		termPos[] = term[termName];&amp;lt;br&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
		//hopefully sqlite caches query results, it will be inefficient otherwise.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		if (term already in table) &amp;lt;br&amp;gt;&lt;br /&gt;
			retrieve the last record, update the record(if necessary split)&amp;lt;br&amp;gt;&lt;br /&gt;
		else&amp;lt;br&amp;gt;&lt;br /&gt;
			create a new record, if necessary cache it for performance; &amp;lt;br&amp;gt;&lt;br /&gt;
		TODO: Expand the algorithm, too generic here &amp;lt;br&amp;gt;&lt;br /&gt;
		if (term already in table) { &amp;lt;br&amp;gt;&lt;br /&gt;
			records = executeQuery(&amp;quot;SELECT ﬁrstdoc, ﬂags, block FROM postings WHERE word = “box” AND ﬁrstdoc &amp;gt;= (SELECT max (ﬁrstdoc) FROM postings	    WHERE word = “box” AND ﬂags &amp;lt; 128)&amp;quot;);&amp;lt;br&amp;gt;&lt;br /&gt;
			Block b = new Block();&amp;lt;br&amp;gt;&lt;br /&gt;
		}&amp;lt;br&amp;gt;&lt;br /&gt;
		else {&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		}&amp;lt;br&amp;gt;&lt;br /&gt;
	}&amp;lt;br&amp;gt;&lt;br /&gt;
	commit to the database&amp;lt;br&amp;gt;&lt;br /&gt;
}&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
RemoveDocument() {&amp;lt;br&amp;gt;&lt;br /&gt;
	inherently inefficient because of the structure that we have adopted. Needs to be optimized.&amp;lt;br&amp;gt;&lt;br /&gt;
}&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
nsNavFullTextIndexHelper&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This class contains mechanism for scheduling documents for indexing. It works pretty much like nsNavHistoryExpire. The scheduling balances indexing with overall firefox performance. It has a timer which when expires picks an unindexed document from the history and calls nsNavFullTextIndex::addDocument to index. When and how the timer is set, dictates the overall efficiency. Whenever a user visits a page, the timer is triggered. ** fill in here how it works **&lt;br /&gt;
&lt;br /&gt;
nsNavFullTextTokenizer&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The tokenizer is used by nsNavFullTextIndex::AddDocument function. Tokenizer is an abstract class. Tokenizer splits the text into tokens. Tokenizer and Analyzer enable support for other languages.&lt;br /&gt;
&lt;br /&gt;
nsNavFullTextAnalyzer&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Analyzer takes the tokens generated from the tokenizer and discards those that are not required for indexing. E.g. is, a, an the can be discarded.&lt;br /&gt;
&lt;br /&gt;
== Front-End ==&lt;br /&gt;
&lt;br /&gt;
TODO: Write about the front-end design&lt;br /&gt;
1) nsNavQueryParser&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
[1] An Efficient Indexing Technique for full-text database systems: Justin Jobel, Alistair Moffet, Ron Sacks Davis&lt;br /&gt;
[2]Using a relational database for full-text index, Steve Putz, Xerox PARC&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=57852</id>
		<title>Places:Full Text Indexing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=57852"/>
		<updated>2007-05-26T06:25:59Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
Full Text Indexing feature will allow user to search for a word/phrase from the pages that he has visited. The search query will be tightly integrated with Places&#039;s nsNavHistoryService. The tighter integration will allow queries like &amp;quot;search for pages visited between 01/05/07(dd/mm/yy) to 20/05/07(dd/mm/yy) containing the word &#039;places&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Design Descision ==&lt;br /&gt;
&lt;br /&gt;
A number of options were looked into before proposing this design. The options included implementing using CLucene(like flock), SQLite&#039;s FTS1 and FTS2 module, implementation using B+ Trees, using relational database etc.. The following text will briefly describe the advantage and disadvantage of all the implementation methods.&lt;br /&gt;
&lt;br /&gt;
CLucene is a full-text indexing engine that stores the index as B+ Trees in files. It uses a very efficient method for storage and retrieval. It has an excellent support for CJK languages. The Places system is a new incorporation into firefox. Hence, it is important that during its initial stages all the code that is written or used is flexible, small and tightly integrated with it. Tighter integration would allow future enhancements specific to firefox. Hence this approach was dropped.&lt;br /&gt;
&lt;br /&gt;
SQLite&#039;s FTS1 and FTS2 module are open source implementation of full-text indexing integrated with SQLite. FTS1 and FTS2 stores the text in B+ Trees and the access to the full-text index is using SQL. However, there are number of short-comings for our usage. FTS2 is still in stage where the API and data might change without backward compatibility. FTS1 does not have custom tokenizer which means no CJK support. Also, FTS1 stores the entire page duplicating what is aleady stored in the cache.&lt;br /&gt;
&lt;br /&gt;
A custom implementation using B+ Tree is a very good option but however, it would require additional B+ Tree engine. In light of availability of an efficient algorithm for implementing full-text indexing using relational database, this method is used.&lt;br /&gt;
&lt;br /&gt;
A naive implementation of full-text indexing is very costly in terms of storage. I&#039;ll briefly explain how it is so. Let us define term. A term is any word that appear in a page. So a relational database contains a table with two columns, term and id. Another table contains two columsn term id and doc id(id of the document the term appeard in). The GNU manuals were analyzed [1]. It is 5.15 Mb of text containing 958,774 occurrences of word out of which 27,554 are unique. But table 2 will require that every occurrence has a corresponding doc id. If term id were stored as int, the amount of space required to store the first column alone, would be 958,774 * 4 bytes, which is about 3 Mb. A B+ Tree implementation is atleast 3Mb more efficient. However a nice encoding scheme and storage model proposed by [2] is almost as efficient as a B+ Tree implementation. This algorithm also leverages the capabilites of relational database system while not losing too much in terms of storage and performance. Hence I propose to implement this algorithm.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: Formatting. Learn how wiki formatting is done&lt;br /&gt;
== Use Case ==&lt;br /&gt;
&lt;br /&gt;
Actor: User&lt;br /&gt;
- Visit Page&lt;br /&gt;
- Search&lt;br /&gt;
- Clear History&lt;br /&gt;
&lt;br /&gt;
Actor: Browser&lt;br /&gt;
- Expire Page&lt;br /&gt;
&lt;br /&gt;
The use cases above will be used to validate the design.&lt;br /&gt;
&lt;br /&gt;
== Database Design ==&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&lt;br /&gt;
Word Table&lt;br /&gt;
columnn		type		bytes	description&lt;br /&gt;
word		varchar		&amp;lt;=100	term for indexing(Shouldn&#039;t it be unicode? how do i store unicode?)&lt;br /&gt;
wordnum		integer		4	unique id. Integer works cause the number of unique words will be atmost a million. Non-english language?&lt;br /&gt;
doc_count	integer		4	number of documents the word occurred in&lt;br /&gt;
word_count	integer		4	number of occurrences of the word&lt;br /&gt;
&lt;br /&gt;
Posting Table&lt;br /&gt;
column		type		bytes	description&lt;br /&gt;
wordnum		integer		4	This is the foreign key matching that in the word table&lt;br /&gt;
firstdoc	integer		4	lowest doc id referenced in the block&lt;br /&gt;
flags		tinyint		1	indicates the block type, length of doc list, sequence number&lt;br /&gt;
block 		varbinary	&amp;lt;=255	contains encoded document and/or position postings for the word&lt;br /&gt;
&lt;br /&gt;
To Do&lt;br /&gt;
1) We might need a table or two more for ranking related data&lt;br /&gt;
2) See how docid will referenced&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
Note that the tables structure is subject to change to improve efficiency. New Tables might be formed and/or the table might add/remove column	&lt;br /&gt;
&lt;br /&gt;
== Detailed Design ==&lt;br /&gt;
&lt;br /&gt;
Classes&lt;br /&gt;
&lt;br /&gt;
There are essentially five classes for the back-end:&lt;br /&gt;
1) nsNavHistoryQuery&lt;br /&gt;
2) nsNavFullTextIndexHelper&lt;br /&gt;
3) nsNavFullTextIndex&lt;br /&gt;
4) nsNavFullTextTokenizer&lt;br /&gt;
5) nsNavFullTextAnalyzer&lt;br /&gt;
&lt;br /&gt;
nsNavHistoryQuery&lt;br /&gt;
&lt;br /&gt;
This class is already implemented with all features except searching text. The mechanism of searching is no different from what described in http://developer.mozilla.org/en/docs/Places:Query_System. The result of nsNavHistoryQuery::search will do a full-text search with nsNavHistoryQuery::searchTerms. This is very powerful with the number of options that you can specify. Conjunctive queries can be executed. &lt;br /&gt;
&lt;br /&gt;
nsNavFullTextIndex&lt;br /&gt;
&lt;br /&gt;
This class interacts with SQLite database. This implements the algorithm for adding document to the index, removing document from the index and search for a given term. This search function is used by nsNavHistoryQuery::search function. Look at [2] for the algorithm used.&lt;br /&gt;
&lt;br /&gt;
Block is a struct used to encode and decode block. The structure of a block is as described in [2]&lt;br /&gt;
&lt;br /&gt;
struct Block {&lt;br /&gt;
	int docCount;&lt;br /&gt;
	int[] docId;&lt;br /&gt;
	int[] docFreq;&lt;br /&gt;
	int[][] docPos;&lt;br /&gt;
	//Variable Length Encoding, compresses very efficiently balancing speed and storage requirement&lt;br /&gt;
	void encode(out byte[] encodedBlock) //encodes the block from docCount, docId, docFreq and docPos&lt;br /&gt;
	void decode(in byte[] encodedBlock); //Decodes the block and fills docCount, docId, docFreq and docPos&lt;br /&gt;
	void addDoc(in int id, in int freq, in int[] pos) //Updates the internal structure&lt;br /&gt;
	TODO: Fill the algorithm here&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
AddDocument(connection, document, analyzer) {&lt;br /&gt;
	while(analyzer.hasTerms()) {&lt;br /&gt;
		term = analyzer.nextTerm();&lt;br /&gt;
		if (term already in table) &lt;br /&gt;
			retrieve the last record, update the record(if necessary split) and commit to the database&lt;br /&gt;
		else&lt;br /&gt;
			create a new record, if necessary cache it for performance and commit to the database;&lt;br /&gt;
		TODO: Expand the algorithm, too generic here&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
nsNavFullTextIndexHelper&lt;br /&gt;
&lt;br /&gt;
This class contains mechanism for scheduling documents for indexing. It works pretty much like nsNavHistoryExpire. The scheduling balances indexing with overall firefox performance. It has a timer which when expires picks an unindexed document from the history and calls nsNavFullTextIndex::addDocument to index. When and how the timer is set, dictates the overall efficiency. Whenever a user visits a page, the timer is triggered. ** fill in here how it works **&lt;br /&gt;
&lt;br /&gt;
nsNavFullTextTokenizer&lt;br /&gt;
&lt;br /&gt;
The tokenizer is used by nsNavFullTextIndex::AddDocument function. Tokenizer is an abstract class. Tokenizer splits the text into tokens. Tokenizer and Analyzer enable support for other languages.&lt;br /&gt;
&lt;br /&gt;
nsNavFullTextAnalyzer&lt;br /&gt;
&lt;br /&gt;
The Analyzer takes the tokens generated from the tokenizer and discards those that are not required for indexing. E.g. is, a, an the can be discarded.&lt;br /&gt;
&lt;br /&gt;
== Front-End ==&lt;br /&gt;
&lt;br /&gt;
TODO: Write about the front-end design&lt;br /&gt;
1) nsNavQueryParser&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
[1] An Efficient Indexing Technique for full-text database systems: Justin Jobel, Alistair Moffet, Ron Sacks Davis&lt;br /&gt;
[2]Using a relational database for full-text index, Steve Putz, Xerox PARC&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=57791</id>
		<title>Places:Full Text Indexing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=57791"/>
		<updated>2007-05-25T11:16:47Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: The complete design of Places: Full Text Indexing feature&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
Full Text Indexing feature will allow user to search for a word/phrase from the pages that he has visited. The search query will be tightly integrated with Places&#039;s nsNavHistoryService. The tighter integration will allow queries like &amp;quot;search for pages visited between 01/05/07(dd/mm/yy) to 20/05/07(dd/mm/yy) containing the word &#039;places&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Design Descision ==&lt;br /&gt;
&lt;br /&gt;
A number of options were looked into before proposing this design. The options included implementing using CLucene(like flock), SQLite&#039;s FTS1 and FTS2 module, implementation using B+ Trees, using relational database etc.. The following text will briefly describe the advantage and disadvantage of all the implementation methods.&lt;br /&gt;
&lt;br /&gt;
CLucene is a full-text indexing engine that stores the index as B+ Trees in files. It uses a very efficient method for storage and retrieval. It has an excellent support for CJK languages. The Places system is a new incorporation into firefox. Hence, it is important that during its initial stages all the code that is written or used is flexible, small and tightly integrated with it. Tighter integration would allow future enhancements specific to firefox. Hence this approach was dropped.&lt;br /&gt;
&lt;br /&gt;
SQLite&#039;s FTS1 and FTS2 module are open source implementation of full-text indexing integrated with SQLite. FTS1 and FTS2 stores the text in B+ Trees and the access to the full-text index is using SQL. However, there are number of short-comings for our usage. FTS2 is still in stage where the API and data might change without backward compatibility. FTS1 does not have custom tokenizer which means no CJK support. Also, FTS1 stores the entire page duplicating what is aleady stored in the cache.&lt;br /&gt;
&lt;br /&gt;
A custom implementation using B+ Tree is a very good option but however, it would require additional B+ Tree engine. In light of availability of an efficient algorithm for implementing full-text indexing using relational database, this method is used.&lt;br /&gt;
&lt;br /&gt;
A naive implementation of full-text indexing is very costly in terms of storage. I&#039;ll briefly explain how it is so. Let us define term. A term is any word that appear in a page. So a relational database contains a table with two columns, term and id. Another table contains two columsn term id and doc id(id of the document the term appeard in). The GNU manuals were analyzed [1]. It is 5.15 Mb of text containing 958,774 occurrences of word out of which 27,554 are unique. But table 2 will require that every occurrence has a corresponding doc id. If term id were stored as int, the amount of space required to store the first column alone, would be 958,774 * 4 bytes, which is about 3 Mb. A B+ Tree implementation is atleast 3Mb more efficient. However a nice encoding scheme and storage model proposed by [2] is almost as efficient as a B+ Tree implementation. This algorithm also leverages the capabilites of relational database system while not losing too much in terms of storage and performance. Hence I propose to implement this algorithm.&lt;br /&gt;
&lt;br /&gt;
== Detailed Design ==&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
[1] An Efficient Indexing Technique for full-text database systems: Justin Jobel, Alistair Moffet, Ron Sacks Davis&lt;br /&gt;
[2]Using a relational database for full-text index, Steve Putz, Xerox PARC&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=57783</id>
		<title>Places:Full Text Indexing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Places:Full_Text_Indexing&amp;diff=57783"/>
		<updated>2007-05-25T06:43:05Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
Full Text Indexing feature will allow user to search for a word/phrase from the pages that he has visited. The search query will be tightly integrated with Places&#039;s nsNavHist&lt;br /&gt;
&lt;br /&gt;
This page is under construction. If you have come across this page, please visit again in a day or two. Thank you.&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Community:SummerOfCode07:Brainstorming&amp;diff=52877</id>
		<title>Community:SummerOfCode07:Brainstorming</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Community:SummerOfCode07:Brainstorming&amp;diff=52877"/>
		<updated>2007-03-24T14:42:01Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: /* Suggestion List */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Projects with a confirmed mentor and approved by the Mozilla project SoC administrator will be moved to [[Community:SummerOfCode07]]. Potential students should look at that page to find project ideas for which we&#039;d like submissions.&lt;br /&gt;
&lt;br /&gt;
==Ground Rules==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Be specific&#039;&#039;&#039;. It&#039;s hard to understand the impact of, or the size of, vague proposals.&lt;br /&gt;
* &#039;&#039;&#039;Consider size&#039;&#039;&#039;. The student has eight weeks to design, code, test and document the proposal. It needs to fill, but not overfill, that time.&lt;br /&gt;
* &#039;&#039;&#039;Do your research&#039;&#039;&#039;. Support the idea with well-researched links.&lt;br /&gt;
* &#039;&#039;&#039;Don&#039;t morph other people&#039;s ideas&#039;&#039;&#039;. If you have a related idea, place it next to the existing one, or add a comment. &lt;br /&gt;
* &#039;&#039;&#039;Insert only your own name into the Mentor column&#039;&#039;&#039;, and then only if you are willing to take on the responsibility. Potential mentors [[Community:SummerOfCode07:Mentors|sign up here]].&lt;br /&gt;
&lt;br /&gt;
([http://weblogs.mozillazine.org/gerv/archives/2006/05/making_a_soc_project_list.html More thoughts on making a good list])&lt;br /&gt;
&lt;br /&gt;
==Suggestion List==&lt;br /&gt;
&lt;br /&gt;
Last year&#039;s ideas: [[Community:SummerOfCode06|General]], [[Thunderbird:Summer_Of_Code_2006|Thunderbird]]&lt;br /&gt;
&lt;br /&gt;
Please use this format for submitting ideas.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;3&amp;quot; width=&amp;quot;100%&amp;quot; valign=&amp;quot;top&amp;quot;&lt;br /&gt;
|- align=&amp;quot;center&amp;quot;&lt;br /&gt;
| style=&amp;quot;background-color: #efefef;&amp;quot; | &#039;&#039;&#039;Title&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;background-color: #efefef;&amp;quot; | &#039;&#039;&#039;Abstract - links to details/bugs/etc&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;background-color: #efefef;&amp;quot; | &#039;&#039;&#039;Reporter&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;background-color: #efefef;&amp;quot; | &#039;&#039;&#039;Mentor(s)&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;background-color: #efefef;&amp;quot; | &#039;&#039;&#039;Comments&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Internal streamed audio player for Firefox&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |This tool will play streamed audio files in firefox itself.( For example .ra files). Currently there must be external player (like real player) to play them. &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | maxaeran&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |When user clicks to play a streamed audio file, I am suggesting two methods to play it. First one is view the downloader and supply a Firefox’s “internal player” to play it. Second one is supply a player options within tool bar. I don’t know the possibility of this project. Please comment on this.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
maxaeran: how does this fit with the WHAT-WG Audio object proposals? - Gerv&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Extension for bookmarking and sharing scripts and extensions.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |An extension tying into a web-based tool - a &amp;quot;del.icio.us for extensions&amp;quot; that also allows users to load their preferred extensions on any firefox browser in seconds.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Hivemya&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |JS coded extension (w/ Open ID based accounts?). SQL-based bookmark accounts directly linking to XPIs, with support for RSS. Is extension auto-installation possible through RSS/JSON subscription? &amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Who regularly wants to &amp;quot;load their preferred extensions on any firefox browser in seconds&amp;quot;? - Gerv&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
That would be a side-benefit. Basically, it&#039;s adding the same batch-installation that is currently being discussed in the Google Greasemonkey group, but for extensions at large. Once enough extensions are bookmarked and tagged, the social bookmarking system could be integrated into the Mozilla Add-Ons site. &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
I think it&#039;s an *important* idea because the quantity of extensions is going to increase ten-fold over the next year, and therefore there is much more of a need for (1) attention agents and (2) spam filters; both needs can be solved through a social bookmarking system. - Hivemya&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Firefox Tab Grouping&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | This feature will group the logically related tabs into related groups in Firefox into similar logical groups. &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | maxaeran&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |These groups can be made by user or it can be done automatically ( can be configured )&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;This proposal is too vague - Gerv&lt;br /&gt;
|-&lt;br /&gt;
| valing=&amp;quot;top&amp;quot; | Allow the option of passing URL to helper application instead of downloading&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | See {{bug|225882}} and {{bug|137339}}&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Metalink&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | A simple XML format for downloads ({{bug|331979}}) that lists mirrors and checksums, along with other useful metadata such as mirror location. Listing multiple URLs for a file increases availability while the checksums guarantee integrity and let downloads be repaired automatically. You can also filter downloads by location and other things. This is currently supported by over ten download managers.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Antini&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Auto verify MD5/SHA1 hashes &amp;amp; PGP signatures&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Automatically verifying MD5/SHA1 hashes, and optionally PGP signatures, of downloads. When you have downloaded a file, the download manager should try to download filename.md5, filename.sha, filename.asc and run the associated tool on the downloaded file to verify. Mark the entry as red or something in the download manager, and change the Open link to Info link, if the file did not verify. The Info link would open a page explaining what is wrong. It could perhaps have a open or preferably just delete file button. More difficult case would be to get the md5/sha1 signature if it is just embedded on the page where the download link is, but you could try some heuristics... (see also bug 292481).&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | HeikkiToivonen&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | PGP signature support would probably be easiest to build on top of Enigmail extension. See Metalink which supports associating MD5/SHA1 hashes and PGP signatures with files, and [http://microformats.org/wiki/hash-examples hash microformat] for embedding within a page.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Making three extra 404 hits on a website for each file downloaded is not a friendly thing to do (remember favicon.ico) - Gerv&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Internal audio&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [https://bugzilla.mozilla.org/show_bug.cgi?id=92110 Allow Firefox to play WAV and AIFF audio files internally]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | schapel&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | This would probably need to be done following the [http://www.whatwg.org/specs/web-apps/current-work/#sound WHAT-WG specs for the Audio() object] - Gerv &lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Memory Manager&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Try to implement an internal memory manager. It should, for example, pre-allocate about 10% of system ram memory and try to operate within that memory. All calls to &amp;quot;free()&amp;quot; should release memory to this global memory pool and all calls to &amp;quot;malloc()&amp;quot; must allocate memory from this memory pool. If properly implemented, we can even reduce the overheads that may arise due to such an implementation.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [[User:Shyamk|Shyam]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | A repost of the idea I posted [http://wiki.mozilla.org/Firefox/Feature_Brainstorming:Performance Here] (Firefox3 Brainstorming).&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Shyam: what qualifications do you have to mentor this project? - Gerv &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Grev: Replied to you by e-mail, and updated this [http://wiki.mozilla.org/Community:SummerOfCode07:Mentors wiki]. Needless to say, I {can/would like} to get dirty in implementing this along with the student (In case of time constraints).&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Shyam: mentors need to have Mozilla community experience. A mentor is not a co-worker by another name :-) - Gerv. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt; Grev: Point taken ! Removed my name from mentor column. I can help the student who comes in to work on this as an outside contributor, and not as GSoC student, as I just graduated :-(&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Image type finder&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Implement an image type finder as described in [https://bugzilla.mozilla.org/show_bug.cgi?id=18574#c672 this Bugzilla comment]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | schapel&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Remote Cookies&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Write a Firefox extension that stores/retrieves cookies on a server instead of in the local cookies.txt file. This will enable Firefox users to use the same cookies on all their computers and Firefox profiles.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [[User:ericjung|Eric H. Jung]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [[User:ericjung|Eric H. Jung]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Think of never having to authenticate against all of your websites again! If the student runs out of time, I will write code to keep the server contents encrypted and the SSL delivery/retrieval mechanism. Student needs to write the GUI and web progress listener hooks.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Why just cookies? Why not full remote profiles? Do you have an algorithm for handling merge conflicts? How does this relate to Google Sync? - Gerv&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Broken Add-on Detector&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Let the user show a problem with the application which happens only with extensions enabled (fine in -safe-mode) and let the application search for the broken add-on/conflicting add-ons itself.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [[User:Archaeopteryx|Archaeopteryx]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | The user recognizes a problem with the application (Fx, Tb, ...) which does not happen in safe mode, so a wizard will demand him to perform the steps to reproduce in normal mode and for comparing in safe mode and try to find the problematic extension by disabling an extension, starting the app and testing and continue with the next extension. Basically, I think about the red code (broken translations and so on) or obvious problems with doubling the event handler which let the tab control keys jump two tabs instead of one. Finally, the problematic extension(s) should be disabled and the user informed about this action.&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | E-mail send/receive progress dialog&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Write a Thunderbird extension that displays a dialog showing the progress of e-mail send/receive, showing the total number of mails to process, their size and a progress bar.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [[User:piecu|Bartosz Piec]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Look at Microsoft Outlook or Outlook Express for an example dialog&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;What benefits does having such a dialog give us? - Gerv&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Bugzilla: Duplicate Bug Detection&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Implement a system in Bugzilla that detects automatically that the user has likely entered a bug that is a duplicate of another bug, and display a list of bugs that this bug might be a duplicate of.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [[User:MaxKanatAlexander|mkanat]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [https://launchpad.net/malone Malone] can do this now, although  I&#039;m not certain its code is actually open source. (Anyhow, GPL&#039;ed code can&#039;t be included in Bugzilla, which uses the MPL.)&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;quot;Search as you Type in addressbar&amp;quot; extension&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | This extension will search in local bookmark and History &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | jigar shah&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [[User:jigarashah|jigar shah]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Most of the time user want to find a page on a particular website; say mozilla developer, He goes to that website and browses through all available links. If when he starts typing in addressbar he gets suggestions based on his bookmarks and History it will reduce his search time. This is easy to do in Firefox 3 since there are plans to add SQLLite in FF3. Don&#039;t know about possibilities for FF2.&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | ODF stylesheet support&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Extension using XSLT stylesheets to make ODF documents viewable in-browser&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Gerv&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [https://addons.mozilla.org/firefox/1888/ ODFReader] already exists, although it&#039;s quite simple, for OpenDocument Text only, and requires a stylesheet whose licensing isn&#039;t quite compatible with that of Mozilla. This project would enhance ODT support, and perhaps add support for ODS (spreadsheet) and ODP (presentation), such that these types could be reliably viewed in a pleasant (if not 100% accurate) way directly in the browser. A &amp;quot;Save&amp;quot; link or button would also be provided, for the potentially confused.&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Firefox 2 Go.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Write an extension that will allow users to sign into firefox anywhere in the world and have their history,bookmarks,browser settings, plugins(firefox profiles) automaticaly loaded into the browser. They will basicaly have a browser that will go anywhere they do. Ofcourse when they sign off everything will be removed if they wish. &lt;br /&gt;
&lt;br /&gt;
Please post comments on this idea&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Peter Kemp (BCIT Student)&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Looking for mentor&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Why should you be limited to surfing your way and style only at home! What if you could travel anywear in the world, to any computer and your browser would be right there for you. It would Supply you all of your bookmarks, your browser settings, your history and even the plugins that you use everyday.&lt;br /&gt;
&lt;br /&gt;
No long is firefox just a browser, but a travel companion.&lt;br /&gt;
&lt;br /&gt;
Firefox, you travel, we follow.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Comment(jigar): &lt;br /&gt;
Google Browser Sync extension already exist for this purpose [http://www.google.com/tools/firefox/browsersync/ Google Browser Sync]&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Such an extension would have value if it were open source and usable with any storage backend, not just Google&#039;s - Gerv&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Comment(Peter Kemp): &lt;br /&gt;
Thank you jigar, I didnt know about the browsersync. &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
(To Gerv):For the storage backend i was thinking about implementing it via XML files. What is your opinion for a backend storage ? Thank you&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | SVG as an image format&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | One of the possibilities that having a native SVG implementation in the browser provides is being able to use SVG in contexts where normally a raster image would be used, such as &amp;amp;lt;html:img&amp;amp;gt; and CSS properties that accept images.&lt;br /&gt;
&lt;br /&gt;
Someone interested in taking this on as a SoC project would need to be pretty familiar with the Mozilla codebase, as this involves getting bits of code that weren&#039;t originally planned to work with one another to play nicely together.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | tor&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | tor&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Merge the two existing French spelling dictionaries&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | There are currently [https://addons.mozilla.org/thunderbird/dictionaries/?lang=fr two French spelling dictionaries] for MySpell. The first was made available from a former ISpell dictionary, and it was later &amp;quot;enhanced&amp;quot; by another group wanting to support new spellings only (1990 reform), although those are not mandatory. As a result, we have two dictionaries, but none of practical use (the first is outdated, the other is underlining perfectly valid words).&lt;br /&gt;
&lt;br /&gt;
A possible implementation of this project would be to take the new dictionary, re-add the hundreds of words that were removed from the old one, and enhance it in other ways (for example, HunSpell allows you to remove some words from the spelling suggestions without underlining them). It might looks like a trivial task, but it is not the case. There were structural changes in the affix dictionary file which can&#039;t be resolved by a simple diff. &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Benoit / [http://frenchmozilla.sourceforge.net/ The FrenchMozilla team]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Create a new French dictionary (HunSpell) from scratch&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | No matter how good the French spelling dictionary may become, it can&#039;t be shipped with Firefox or Thunderbird because of licensing issues (It&#039;s GPL only, Mozilla products are tri-licensed)[http://frenchmozilla.sourceforge.net/blog/index.php/2006/02/02/21-correction-orthographique-et-logiciels-mozilla explanation in French].&lt;br /&gt;
This proposal is to build a new French dictionary from scratch, taking advantage of the new features in [http://sourceforge.net/docman/display_doc.php?docid=29374&amp;amp;group_id=143754 HunSpell] &lt;br /&gt;
&lt;br /&gt;
Someone interested in taking this on as a SoC project would probably need to have a strong background in linguistics or a similar field.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Benoit / [http://frenchmozilla.sourceforge.net/ The FrenchMozilla team]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Index visited pages. Allow query on it.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | People need to re-find the information that they have already found on the web. This mechanism is currently provided through bookmarks, history and the navigation buttons. Firefox 3 is set to include a number of features through “Places”. “Places” can be further enhanced by allowing user “word-search” the visited web pages. This project will add indexing capabilities to firefox and allow user queries on visited web pages. Thus helping the user find what they need to know. Reported as an enhancement for firefox3. {{bug|342913}}&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [http://wiki.mozilla.org/User:Mindboggler Kunal]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Looking for mentor&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |Places will add exciting capabilities to firefox. Indexing visited pages is a consistent demand seen in the wikis. Further users spend a lot of time on re-finding information on the web. A feature like this will enhance his user-experience. Sooner or later, competiting browsers will implement this feature. An early start on this will make firefox even more competitive compared to its rival browsers.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Community:SummerOfCode07:Brainstorming&amp;diff=52876</id>
		<title>Community:SummerOfCode07:Brainstorming</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Community:SummerOfCode07:Brainstorming&amp;diff=52876"/>
		<updated>2007-03-24T14:38:24Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: /* Suggestion List */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Projects with a confirmed mentor and approved by the Mozilla project SoC administrator will be moved to [[Community:SummerOfCode07]]. Potential students should look at that page to find project ideas for which we&#039;d like submissions.&lt;br /&gt;
&lt;br /&gt;
==Ground Rules==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Be specific&#039;&#039;&#039;. It&#039;s hard to understand the impact of, or the size of, vague proposals.&lt;br /&gt;
* &#039;&#039;&#039;Consider size&#039;&#039;&#039;. The student has eight weeks to design, code, test and document the proposal. It needs to fill, but not overfill, that time.&lt;br /&gt;
* &#039;&#039;&#039;Do your research&#039;&#039;&#039;. Support the idea with well-researched links.&lt;br /&gt;
* &#039;&#039;&#039;Don&#039;t morph other people&#039;s ideas&#039;&#039;&#039;. If you have a related idea, place it next to the existing one, or add a comment. &lt;br /&gt;
* &#039;&#039;&#039;Insert only your own name into the Mentor column&#039;&#039;&#039;, and then only if you are willing to take on the responsibility. Potential mentors [[Community:SummerOfCode07:Mentors|sign up here]].&lt;br /&gt;
&lt;br /&gt;
([http://weblogs.mozillazine.org/gerv/archives/2006/05/making_a_soc_project_list.html More thoughts on making a good list])&lt;br /&gt;
&lt;br /&gt;
==Suggestion List==&lt;br /&gt;
&lt;br /&gt;
Last year&#039;s ideas: [[Community:SummerOfCode06|General]], [[Thunderbird:Summer_Of_Code_2006|Thunderbird]]&lt;br /&gt;
&lt;br /&gt;
Please use this format for submitting ideas.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;3&amp;quot; width=&amp;quot;100%&amp;quot; valign=&amp;quot;top&amp;quot;&lt;br /&gt;
|- align=&amp;quot;center&amp;quot;&lt;br /&gt;
| style=&amp;quot;background-color: #efefef;&amp;quot; | &#039;&#039;&#039;Title&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;background-color: #efefef;&amp;quot; | &#039;&#039;&#039;Abstract - links to details/bugs/etc&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;background-color: #efefef;&amp;quot; | &#039;&#039;&#039;Reporter&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;background-color: #efefef;&amp;quot; | &#039;&#039;&#039;Mentor(s)&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;background-color: #efefef;&amp;quot; | &#039;&#039;&#039;Comments&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Internal streamed audio player for Firefox&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |This tool will play streamed audio files in firefox itself.( For example .ra files). Currently there must be external player (like real player) to play them. &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | maxaeran&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |When user clicks to play a streamed audio file, I am suggesting two methods to play it. First one is view the downloader and supply a Firefox’s “internal player” to play it. Second one is supply a player options within tool bar. I don’t know the possibility of this project. Please comment on this.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
maxaeran: how does this fit with the WHAT-WG Audio object proposals? - Gerv&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Extension for bookmarking and sharing scripts and extensions.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |An extension tying into a web-based tool - a &amp;quot;del.icio.us for extensions&amp;quot; that also allows users to load their preferred extensions on any firefox browser in seconds.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Hivemya&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |JS coded extension (w/ Open ID based accounts?). SQL-based bookmark accounts directly linking to XPIs, with support for RSS. Is extension auto-installation possible through RSS/JSON subscription? &amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Who regularly wants to &amp;quot;load their preferred extensions on any firefox browser in seconds&amp;quot;? - Gerv&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
That would be a side-benefit. Basically, it&#039;s adding the same batch-installation that is currently being discussed in the Google Greasemonkey group, but for extensions at large. Once enough extensions are bookmarked and tagged, the social bookmarking system could be integrated into the Mozilla Add-Ons site. &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
I think it&#039;s an *important* idea because the quantity of extensions is going to increase ten-fold over the next year, and therefore there is much more of a need for (1) attention agents and (2) spam filters; both needs can be solved through a social bookmarking system. - Hivemya&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Firefox Tab Grouping&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | This feature will group the logically related tabs into related groups in Firefox into similar logical groups. &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | maxaeran&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |These groups can be made by user or it can be done automatically ( can be configured )&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;This proposal is too vague - Gerv&lt;br /&gt;
|-&lt;br /&gt;
| valing=&amp;quot;top&amp;quot; | Allow the option of passing URL to helper application instead of downloading&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | See {{bug|225882}} and {{bug|137339}}&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Metalink&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | A simple XML format for downloads ({{bug|331979}}) that lists mirrors and checksums, along with other useful metadata such as mirror location. Listing multiple URLs for a file increases availability while the checksums guarantee integrity and let downloads be repaired automatically. You can also filter downloads by location and other things. This is currently supported by over ten download managers.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Antini&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Auto verify MD5/SHA1 hashes &amp;amp; PGP signatures&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Automatically verifying MD5/SHA1 hashes, and optionally PGP signatures, of downloads. When you have downloaded a file, the download manager should try to download filename.md5, filename.sha, filename.asc and run the associated tool on the downloaded file to verify. Mark the entry as red or something in the download manager, and change the Open link to Info link, if the file did not verify. The Info link would open a page explaining what is wrong. It could perhaps have a open or preferably just delete file button. More difficult case would be to get the md5/sha1 signature if it is just embedded on the page where the download link is, but you could try some heuristics... (see also bug 292481).&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | HeikkiToivonen&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | PGP signature support would probably be easiest to build on top of Enigmail extension. See Metalink which supports associating MD5/SHA1 hashes and PGP signatures with files, and [http://microformats.org/wiki/hash-examples hash microformat] for embedding within a page.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Making three extra 404 hits on a website for each file downloaded is not a friendly thing to do (remember favicon.ico) - Gerv&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Internal audio&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [https://bugzilla.mozilla.org/show_bug.cgi?id=92110 Allow Firefox to play WAV and AIFF audio files internally]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | schapel&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | This would probably need to be done following the [http://www.whatwg.org/specs/web-apps/current-work/#sound WHAT-WG specs for the Audio() object] - Gerv &lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Memory Manager&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Try to implement an internal memory manager. It should, for example, pre-allocate about 10% of system ram memory and try to operate within that memory. All calls to &amp;quot;free()&amp;quot; should release memory to this global memory pool and all calls to &amp;quot;malloc()&amp;quot; must allocate memory from this memory pool. If properly implemented, we can even reduce the overheads that may arise due to such an implementation.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [[User:Shyamk|Shyam]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | A repost of the idea I posted [http://wiki.mozilla.org/Firefox/Feature_Brainstorming:Performance Here] (Firefox3 Brainstorming).&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Shyam: what qualifications do you have to mentor this project? - Gerv &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Grev: Replied to you by e-mail, and updated this [http://wiki.mozilla.org/Community:SummerOfCode07:Mentors wiki]. Needless to say, I {can/would like} to get dirty in implementing this along with the student (In case of time constraints).&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Shyam: mentors need to have Mozilla community experience. A mentor is not a co-worker by another name :-) - Gerv. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt; Grev: Point taken ! Removed my name from mentor column. I can help the student who comes in to work on this as an outside contributor, and not as GSoC student, as I just graduated :-(&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Image type finder&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Implement an image type finder as described in [https://bugzilla.mozilla.org/show_bug.cgi?id=18574#c672 this Bugzilla comment]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | schapel&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Remote Cookies&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Write a Firefox extension that stores/retrieves cookies on a server instead of in the local cookies.txt file. This will enable Firefox users to use the same cookies on all their computers and Firefox profiles.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [[User:ericjung|Eric H. Jung]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [[User:ericjung|Eric H. Jung]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Think of never having to authenticate against all of your websites again! If the student runs out of time, I will write code to keep the server contents encrypted and the SSL delivery/retrieval mechanism. Student needs to write the GUI and web progress listener hooks.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Why just cookies? Why not full remote profiles? Do you have an algorithm for handling merge conflicts? How does this relate to Google Sync? - Gerv&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Broken Add-on Detector&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Let the user show a problem with the application which happens only with extensions enabled (fine in -safe-mode) and let the application search for the broken add-on/conflicting add-ons itself.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [[User:Archaeopteryx|Archaeopteryx]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | The user recognizes a problem with the application (Fx, Tb, ...) which does not happen in safe mode, so a wizard will demand him to perform the steps to reproduce in normal mode and for comparing in safe mode and try to find the problematic extension by disabling an extension, starting the app and testing and continue with the next extension. Basically, I think about the red code (broken translations and so on) or obvious problems with doubling the event handler which let the tab control keys jump two tabs instead of one. Finally, the problematic extension(s) should be disabled and the user informed about this action.&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | E-mail send/receive progress dialog&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Write a Thunderbird extension that displays a dialog showing the progress of e-mail send/receive, showing the total number of mails to process, their size and a progress bar.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [[User:piecu|Bartosz Piec]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Look at Microsoft Outlook or Outlook Express for an example dialog&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;What benefits does having such a dialog give us? - Gerv&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Bugzilla: Duplicate Bug Detection&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Implement a system in Bugzilla that detects automatically that the user has likely entered a bug that is a duplicate of another bug, and display a list of bugs that this bug might be a duplicate of.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [[User:MaxKanatAlexander|mkanat]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [https://launchpad.net/malone Malone] can do this now, although  I&#039;m not certain its code is actually open source. (Anyhow, GPL&#039;ed code can&#039;t be included in Bugzilla, which uses the MPL.)&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;quot;Search as you Type in addressbar&amp;quot; extension&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | This extension will search in local bookmark and History &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | jigar shah&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [[User:jigarashah|jigar shah]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Most of the time user want to find a page on a particular website; say mozilla developer, He goes to that website and browses through all available links. If when he starts typing in addressbar he gets suggestions based on his bookmarks and History it will reduce his search time. This is easy to do in Firefox 3 since there are plans to add SQLLite in FF3. Don&#039;t know about possibilities for FF2.&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | ODF stylesheet support&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Extension using XSLT stylesheets to make ODF documents viewable in-browser&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Gerv&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [https://addons.mozilla.org/firefox/1888/ ODFReader] already exists, although it&#039;s quite simple, for OpenDocument Text only, and requires a stylesheet whose licensing isn&#039;t quite compatible with that of Mozilla. This project would enhance ODT support, and perhaps add support for ODS (spreadsheet) and ODP (presentation), such that these types could be reliably viewed in a pleasant (if not 100% accurate) way directly in the browser. A &amp;quot;Save&amp;quot; link or button would also be provided, for the potentially confused.&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Firefox 2 Go.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Write an extension that will allow users to sign into firefox anywhere in the world and have their history,bookmarks,browser settings, plugins(firefox profiles) automaticaly loaded into the browser. They will basicaly have a browser that will go anywhere they do. Ofcourse when they sign off everything will be removed if they wish. &lt;br /&gt;
&lt;br /&gt;
Please post comments on this idea&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Peter Kemp (BCIT Student)&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Looking for mentor&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Why should you be limited to surfing your way and style only at home! What if you could travel anywear in the world, to any computer and your browser would be right there for you. It would Supply you all of your bookmarks, your browser settings, your history and even the plugins that you use everyday.&lt;br /&gt;
&lt;br /&gt;
No long is firefox just a browser, but a travel companion.&lt;br /&gt;
&lt;br /&gt;
Firefox, you travel, we follow.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Comment(jigar): &lt;br /&gt;
Google Browser Sync extension already exist for this purpose [http://www.google.com/tools/firefox/browsersync/ Google Browser Sync]&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Such an extension would have value if it were open source and usable with any storage backend, not just Google&#039;s - Gerv&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Comment(Peter Kemp): &lt;br /&gt;
Thank you jigar, I didnt know about the browsersync. &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
(To Gerv):For the storage backend i was thinking about implementing it via XML files. What is your opinion for a backend storage ? Thank you&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | SVG as an image format&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | One of the possibilities that having a native SVG implementation in the browser provides is being able to use SVG in contexts where normally a raster image would be used, such as &amp;amp;lt;html:img&amp;amp;gt; and CSS properties that accept images.&lt;br /&gt;
&lt;br /&gt;
Someone interested in taking this on as a SoC project would need to be pretty familiar with the Mozilla codebase, as this involves getting bits of code that weren&#039;t originally planned to work with one another to play nicely together.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | tor&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | tor&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Merge the two existing French spelling dictionaries&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | There are currently [https://addons.mozilla.org/thunderbird/dictionaries/?lang=fr two French spelling dictionaries] for MySpell. The first was made available from a former ISpell dictionary, and it was later &amp;quot;enhanced&amp;quot; by another group wanting to support new spellings only (1990 reform), although those are not mandatory. As a result, we have two dictionaries, but none of practical use (the first is outdated, the other is underlining perfectly valid words).&lt;br /&gt;
&lt;br /&gt;
A possible implementation of this project would be to take the new dictionary, re-add the hundreds of words that were removed from the old one, and enhance it in other ways (for example, HunSpell allows you to remove some words from the spelling suggestions without underlining them). It might looks like a trivial task, but it is not the case. There were structural changes in the affix dictionary file which can&#039;t be resolved by a simple diff. &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Benoit / [http://frenchmozilla.sourceforge.net/ The FrenchMozilla team]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Create a new French dictionary (HunSpell) from scratch&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | No matter how good the French spelling dictionary may become, it can&#039;t be shipped with Firefox or Thunderbird because of licensing issues (It&#039;s GPL only, Mozilla products are tri-licensed)[http://frenchmozilla.sourceforge.net/blog/index.php/2006/02/02/21-correction-orthographique-et-logiciels-mozilla explanation in French].&lt;br /&gt;
This proposal is to build a new French dictionary from scratch, taking advantage of the new features in [http://sourceforge.net/docman/display_doc.php?docid=29374&amp;amp;group_id=143754 HunSpell] &lt;br /&gt;
&lt;br /&gt;
Someone interested in taking this on as a SoC project would probably need to have a strong background in linguistics or a similar field.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Benoit / [http://frenchmozilla.sourceforge.net/ The FrenchMozilla team]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Index visited pages. Allow query on it.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | This will go into the places work. Reported as an enhancement for firefox3. {{bug|342913}}&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [http://wiki.mozilla.org/User:Mindboggler Kunal]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
	<entry>
		<id>https://wiki.mozilla.org/index.php?title=Community:SummerOfCode07:Brainstorming&amp;diff=52875</id>
		<title>Community:SummerOfCode07:Brainstorming</title>
		<link rel="alternate" type="text/html" href="https://wiki.mozilla.org/index.php?title=Community:SummerOfCode07:Brainstorming&amp;diff=52875"/>
		<updated>2007-03-24T14:34:41Z</updated>

		<summary type="html">&lt;p&gt;Mindboggler: /* Suggestion List */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Projects with a confirmed mentor and approved by the Mozilla project SoC administrator will be moved to [[Community:SummerOfCode07]]. Potential students should look at that page to find project ideas for which we&#039;d like submissions.&lt;br /&gt;
&lt;br /&gt;
==Ground Rules==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Be specific&#039;&#039;&#039;. It&#039;s hard to understand the impact of, or the size of, vague proposals.&lt;br /&gt;
* &#039;&#039;&#039;Consider size&#039;&#039;&#039;. The student has eight weeks to design, code, test and document the proposal. It needs to fill, but not overfill, that time.&lt;br /&gt;
* &#039;&#039;&#039;Do your research&#039;&#039;&#039;. Support the idea with well-researched links.&lt;br /&gt;
* &#039;&#039;&#039;Don&#039;t morph other people&#039;s ideas&#039;&#039;&#039;. If you have a related idea, place it next to the existing one, or add a comment. &lt;br /&gt;
* &#039;&#039;&#039;Insert only your own name into the Mentor column&#039;&#039;&#039;, and then only if you are willing to take on the responsibility. Potential mentors [[Community:SummerOfCode07:Mentors|sign up here]].&lt;br /&gt;
&lt;br /&gt;
([http://weblogs.mozillazine.org/gerv/archives/2006/05/making_a_soc_project_list.html More thoughts on making a good list])&lt;br /&gt;
&lt;br /&gt;
==Suggestion List==&lt;br /&gt;
&lt;br /&gt;
Last year&#039;s ideas: [[Community:SummerOfCode06|General]], [[Thunderbird:Summer_Of_Code_2006|Thunderbird]]&lt;br /&gt;
&lt;br /&gt;
Please use this format for submitting ideas.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;3&amp;quot; width=&amp;quot;100%&amp;quot; valign=&amp;quot;top&amp;quot;&lt;br /&gt;
|- align=&amp;quot;center&amp;quot;&lt;br /&gt;
| style=&amp;quot;background-color: #efefef;&amp;quot; | &#039;&#039;&#039;Title&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;background-color: #efefef;&amp;quot; | &#039;&#039;&#039;Abstract - links to details/bugs/etc&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;background-color: #efefef;&amp;quot; | &#039;&#039;&#039;Reporter&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;background-color: #efefef;&amp;quot; | &#039;&#039;&#039;Mentor(s)&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;background-color: #efefef;&amp;quot; | &#039;&#039;&#039;Comments&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Internal streamed audio player for Firefox&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |This tool will play streamed audio files in firefox itself.( For example .ra files). Currently there must be external player (like real player) to play them. &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | maxaeran&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |When user clicks to play a streamed audio file, I am suggesting two methods to play it. First one is view the downloader and supply a Firefox’s “internal player” to play it. Second one is supply a player options within tool bar. I don’t know the possibility of this project. Please comment on this.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
maxaeran: how does this fit with the WHAT-WG Audio object proposals? - Gerv&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Extension for bookmarking and sharing scripts and extensions.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |An extension tying into a web-based tool - a &amp;quot;del.icio.us for extensions&amp;quot; that also allows users to load their preferred extensions on any firefox browser in seconds.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Hivemya&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |JS coded extension (w/ Open ID based accounts?). SQL-based bookmark accounts directly linking to XPIs, with support for RSS. Is extension auto-installation possible through RSS/JSON subscription? &amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Who regularly wants to &amp;quot;load their preferred extensions on any firefox browser in seconds&amp;quot;? - Gerv&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
That would be a side-benefit. Basically, it&#039;s adding the same batch-installation that is currently being discussed in the Google Greasemonkey group, but for extensions at large. Once enough extensions are bookmarked and tagged, the social bookmarking system could be integrated into the Mozilla Add-Ons site. &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
I think it&#039;s an *important* idea because the quantity of extensions is going to increase ten-fold over the next year, and therefore there is much more of a need for (1) attention agents and (2) spam filters; both needs can be solved through a social bookmarking system. - Hivemya&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Firefox Tab Grouping&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | This feature will group the logically related tabs into related groups in Firefox into similar logical groups. &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | maxaeran&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |These groups can be made by user or it can be done automatically ( can be configured )&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;This proposal is too vague - Gerv&lt;br /&gt;
|-&lt;br /&gt;
| valing=&amp;quot;top&amp;quot; | Allow the option of passing URL to helper application instead of downloading&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | See {{bug|225882}} and {{bug|137339}}&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Metalink&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | A simple XML format for downloads ({{bug|331979}}) that lists mirrors and checksums, along with other useful metadata such as mirror location. Listing multiple URLs for a file increases availability while the checksums guarantee integrity and let downloads be repaired automatically. You can also filter downloads by location and other things. This is currently supported by over ten download managers.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Antini&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Auto verify MD5/SHA1 hashes &amp;amp; PGP signatures&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Automatically verifying MD5/SHA1 hashes, and optionally PGP signatures, of downloads. When you have downloaded a file, the download manager should try to download filename.md5, filename.sha, filename.asc and run the associated tool on the downloaded file to verify. Mark the entry as red or something in the download manager, and change the Open link to Info link, if the file did not verify. The Info link would open a page explaining what is wrong. It could perhaps have a open or preferably just delete file button. More difficult case would be to get the md5/sha1 signature if it is just embedded on the page where the download link is, but you could try some heuristics... (see also bug 292481).&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | HeikkiToivonen&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | PGP signature support would probably be easiest to build on top of Enigmail extension. See Metalink which supports associating MD5/SHA1 hashes and PGP signatures with files, and [http://microformats.org/wiki/hash-examples hash microformat] for embedding within a page.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Making three extra 404 hits on a website for each file downloaded is not a friendly thing to do (remember favicon.ico) - Gerv&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Internal audio&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [https://bugzilla.mozilla.org/show_bug.cgi?id=92110 Allow Firefox to play WAV and AIFF audio files internally]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | schapel&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | This would probably need to be done following the [http://www.whatwg.org/specs/web-apps/current-work/#sound WHAT-WG specs for the Audio() object] - Gerv &lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Memory Manager&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Try to implement an internal memory manager. It should, for example, pre-allocate about 10% of system ram memory and try to operate within that memory. All calls to &amp;quot;free()&amp;quot; should release memory to this global memory pool and all calls to &amp;quot;malloc()&amp;quot; must allocate memory from this memory pool. If properly implemented, we can even reduce the overheads that may arise due to such an implementation.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [[User:Shyamk|Shyam]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | A repost of the idea I posted [http://wiki.mozilla.org/Firefox/Feature_Brainstorming:Performance Here] (Firefox3 Brainstorming).&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Shyam: what qualifications do you have to mentor this project? - Gerv &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Grev: Replied to you by e-mail, and updated this [http://wiki.mozilla.org/Community:SummerOfCode07:Mentors wiki]. Needless to say, I {can/would like} to get dirty in implementing this along with the student (In case of time constraints).&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Shyam: mentors need to have Mozilla community experience. A mentor is not a co-worker by another name :-) - Gerv. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt; Grev: Point taken ! Removed my name from mentor column. I can help the student who comes in to work on this as an outside contributor, and not as GSoC student, as I just graduated :-(&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Image type finder&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Implement an image type finder as described in [https://bugzilla.mozilla.org/show_bug.cgi?id=18574#c672 this Bugzilla comment]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | schapel&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Remote Cookies&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Write a Firefox extension that stores/retrieves cookies on a server instead of in the local cookies.txt file. This will enable Firefox users to use the same cookies on all their computers and Firefox profiles.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [[User:ericjung|Eric H. Jung]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [[User:ericjung|Eric H. Jung]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Think of never having to authenticate against all of your websites again! If the student runs out of time, I will write code to keep the server contents encrypted and the SSL delivery/retrieval mechanism. Student needs to write the GUI and web progress listener hooks.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Why just cookies? Why not full remote profiles? Do you have an algorithm for handling merge conflicts? How does this relate to Google Sync? - Gerv&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Broken Add-on Detector&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Let the user show a problem with the application which happens only with extensions enabled (fine in -safe-mode) and let the application search for the broken add-on/conflicting add-ons itself.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [[User:Archaeopteryx|Archaeopteryx]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | The user recognizes a problem with the application (Fx, Tb, ...) which does not happen in safe mode, so a wizard will demand him to perform the steps to reproduce in normal mode and for comparing in safe mode and try to find the problematic extension by disabling an extension, starting the app and testing and continue with the next extension. Basically, I think about the red code (broken translations and so on) or obvious problems with doubling the event handler which let the tab control keys jump two tabs instead of one. Finally, the problematic extension(s) should be disabled and the user informed about this action.&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | E-mail send/receive progress dialog&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Write a Thunderbird extension that displays a dialog showing the progress of e-mail send/receive, showing the total number of mails to process, their size and a progress bar.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [[User:piecu|Bartosz Piec]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Look at Microsoft Outlook or Outlook Express for an example dialog&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;What benefits does having such a dialog give us? - Gerv&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Bugzilla: Duplicate Bug Detection&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Implement a system in Bugzilla that detects automatically that the user has likely entered a bug that is a duplicate of another bug, and display a list of bugs that this bug might be a duplicate of.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [[User:MaxKanatAlexander|mkanat]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [https://launchpad.net/malone Malone] can do this now, although  I&#039;m not certain its code is actually open source. (Anyhow, GPL&#039;ed code can&#039;t be included in Bugzilla, which uses the MPL.)&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;quot;Search as you Type in addressbar&amp;quot; extension&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | This extension will search in local bookmark and History &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | jigar shah&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [[User:jigarashah|jigar shah]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Most of the time user want to find a page on a particular website; say mozilla developer, He goes to that website and browses through all available links. If when he starts typing in addressbar he gets suggestions based on his bookmarks and History it will reduce his search time. This is easy to do in Firefox 3 since there are plans to add SQLLite in FF3. Don&#039;t know about possibilities for FF2.&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | ODF stylesheet support&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Extension using XSLT stylesheets to make ODF documents viewable in-browser&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Gerv&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | [https://addons.mozilla.org/firefox/1888/ ODFReader] already exists, although it&#039;s quite simple, for OpenDocument Text only, and requires a stylesheet whose licensing isn&#039;t quite compatible with that of Mozilla. This project would enhance ODT support, and perhaps add support for ODS (spreadsheet) and ODP (presentation), such that these types could be reliably viewed in a pleasant (if not 100% accurate) way directly in the browser. A &amp;quot;Save&amp;quot; link or button would also be provided, for the potentially confused.&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Firefox 2 Go.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Write an extension that will allow users to sign into firefox anywhere in the world and have their history,bookmarks,browser settings, plugins(firefox profiles) automaticaly loaded into the browser. They will basicaly have a browser that will go anywhere they do. Ofcourse when they sign off everything will be removed if they wish. &lt;br /&gt;
&lt;br /&gt;
Please post comments on this idea&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Peter Kemp (BCIT Student)&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Looking for mentor&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Why should you be limited to surfing your way and style only at home! What if you could travel anywear in the world, to any computer and your browser would be right there for you. It would Supply you all of your bookmarks, your browser settings, your history and even the plugins that you use everyday.&lt;br /&gt;
&lt;br /&gt;
No long is firefox just a browser, but a travel companion.&lt;br /&gt;
&lt;br /&gt;
Firefox, you travel, we follow.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Comment(jigar): &lt;br /&gt;
Google Browser Sync extension already exist for this purpose [http://www.google.com/tools/firefox/browsersync/ Google Browser Sync]&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Such an extension would have value if it were open source and usable with any storage backend, not just Google&#039;s - Gerv&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Comment(Peter Kemp): &lt;br /&gt;
Thank you jigar, I didnt know about the browsersync. &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
(To Gerv):For the storage backend i was thinking about implementing it via XML files. What is your opinion for a backend storage ? Thank you&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | SVG as an image format&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | One of the possibilities that having a native SVG implementation in the browser provides is being able to use SVG in contexts where normally a raster image would be used, such as &amp;amp;lt;html:img&amp;amp;gt; and CSS properties that accept images.&lt;br /&gt;
&lt;br /&gt;
Someone interested in taking this on as a SoC project would need to be pretty familiar with the Mozilla codebase, as this involves getting bits of code that weren&#039;t originally planned to work with one another to play nicely together.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | tor&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | tor&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Merge the two existing French spelling dictionaries&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | There are currently [https://addons.mozilla.org/thunderbird/dictionaries/?lang=fr two French spelling dictionaries] for MySpell. The first was made available from a former ISpell dictionary, and it was later &amp;quot;enhanced&amp;quot; by another group wanting to support new spellings only (1990 reform), although those are not mandatory. As a result, we have two dictionaries, but none of practical use (the first is outdated, the other is underlining perfectly valid words).&lt;br /&gt;
&lt;br /&gt;
A possible implementation of this project would be to take the new dictionary, re-add the hundreds of words that were removed from the old one, and enhance it in other ways (for example, HunSpell allows you to remove some words from the spelling suggestions without underlining them). It might looks like a trivial task, but it is not the case. There were structural changes in the affix dictionary file which can&#039;t be resolved by a simple diff. &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Benoit / [http://frenchmozilla.sourceforge.net/ The FrenchMozilla team]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Create a new French dictionary (HunSpell) from scratch&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | No matter how good the French spelling dictionary may become, it can&#039;t be shipped with Firefox or Thunderbird because of licensing issues (It&#039;s GPL only, Mozilla products are tri-licensed)[http://frenchmozilla.sourceforge.net/blog/index.php/2006/02/02/21-correction-orthographique-et-logiciels-mozilla explanation in French].&lt;br /&gt;
This proposal is to build a new French dictionary from scratch, taking advantage of the new features in [http://sourceforge.net/docman/display_doc.php?docid=29374&amp;amp;group_id=143754 HunSpell] &lt;br /&gt;
&lt;br /&gt;
Someone interested in taking this on as a SoC project would probably need to have a strong background in linguistics or a similar field.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Benoit / [http://frenchmozilla.sourceforge.net/ The FrenchMozilla team]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | Index visited pages. Allow query on it.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | https://bugzilla.mozilla.org/show_bug.cgi?id=342913&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | http://wiki.mozilla.org/User:Mindboggler&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Mindboggler</name></author>
	</entry>
</feed>