<h3 dir="auto">Background</h3>
<p dir="auto">The most important thing about specifying analyzers is the analyzer used at index time needs to be basically the same as the analyzer used at query time.  If completely different analyzers were to be used, you would either produce terms that could never be found at query time, or query for terms that could never exist in the index.  The API for specifying analyzers on fields does allow to set index and query analyzers separately.  This is to allow things like synonyms where you may want to only add synonyms at index time (yields cheaper queries later), or just add them within the query (more flexible since synonyms can be changed dynamically).</p>
<h3 dir="auto"><code class="notranslate">_analyzer</code> today</h3>
<p dir="auto">Today we have multiple ways to specify which analyzer will be used on text fields.  At indexing time, the order to check is as follows:</p>
<ol dir="auto">
<li>analyzer for the field</li>
<li><code class="notranslate">_analyzer</code> (proposed to remove here)</li>
<li>type level default analyzer (will be removed in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="51556074" data-permission-text="Title is private" data-url="https://github.com/elastic/elasticsearch/issues/8874" data-hovercard-type="issue" data-hovercard-url="/elastic/elasticsearch/issues/8874/hovercard" href="https://github.com/elastic/elasticsearch/issues/8874">#8874</a>)</li>
<li>index level default analyzer</li>
</ol>
<p dir="auto"><code class="notranslate">_analyzer</code> is a special field in the document which specifies the name of an analyzer to use as the default for that document.  This means that the same field for one document can use a completely different analyzer than another document.  The typical use case for this is working with documents in many languages, where each document contains a field specifying which language its main data is in (e.g. subject and body fields). Then at query time, either a single query is used with a “magic” analyzer over that field, or a conjunction of queries that use every analyzer the data may have been indexed with.</p>
<h3 dir="auto">Problems with <code class="notranslate">_analyzer</code></h3>
<p dir="auto">The typical use case for <code class="notranslate">_analyzer</code> has many problems:</p>
<ul dir="auto">
<li>If using the single analyzer at query time approach, the “magic” analyzer is never good enough.  It cannot possibly cover all the terms that may have been produced by each languages' analyzer, so some terms are never matchable.  For example, “die” in German would be a stop word, while the same word in English is simply a regular word.  Either the magic analyzer removes “die” (in which case English documents about dying can not be found) or it includes it, and German documents that contained it can not match (since the indexing process removed that term).</li>
<li>Scoring will be skewed. Text relevance models rely on term statistics to weigh the importance of a term for a given document versus the importance of that term for the entire index.  Because some words may analyze to the same term in different languages, the frequencies of terms can be skewed, which will distort where documents matching these words appear in query results.</li>
<li>Mappings code is already complicated, and this feature further complicates following the logic of where Analyzers are set in code.</li>
<li>Having multiple ways to set the analyzer used at index or query time is also confusing on users, as they have to decide which way is “better”.</li>
</ul>
<h3 dir="auto">Proposal</h3>
<p dir="auto">I propose to remove <code class="notranslate">_analyzer</code>, and the associated “analyzer” setting of the match query.  Removing this, along with the type level default in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="51556074" data-permission-text="Title is private" data-url="https://github.com/elastic/elasticsearch/issues/8874" data-hovercard-type="issue" data-hovercard-url="/elastic/elasticsearch/issues/8874/hovercard" href="https://github.com/elastic/elasticsearch/issues/8874">#8874</a> will simplify specifying analyzers considerably.  There would be no loss of end functionality, since better results can be achieved with multiple fields.</p>
<h3 dir="auto">Alternatives to <code class="notranslate">_analyzer</code></h3>
<ul dir="auto">
<li>One alternative to dealing with multiple languages is to use n-grams. While this can itself be tricky to deal with, it is worth mentioning.</li>
<li>An alternative that is more directly in line with the current uses of _analyzer is having one field per language.  This requires slight modifications to client code for indexing (copy data into the field for the appropriate language, instead of specifying the language in a field) and querying (query the appropriate language field, instead of selecting an analyzer for that language.  This will produce better results since documents from other languages cannot accidentally appear in the results, and relevance results should be as expected for that language.</li>
</ul>
<h2 dir="auto">ES with Multi-Cluster Search Support</h2>
<p dir="auto">It'd be nice to be able to query across multiple clusters and get their aggregated results.</p>
<p dir="auto">Our initial motivation is to view Kibana results across multiple clusters. See: <a href="https://github.com/elasticsearch/kibana/issues/22" data-hovercard-type="issue" data-hovercard-url="/elastic/kibana/issues/22/hovercard">Enhancement: Allow conntections to multiple ES backends from a single Kibana instance</a>.</p>
<p dir="auto">However, since we also use/query ES directly, a proxy would work far better than changing Kibana.</p>
<p dir="auto">In the discussion above, it was decided (by Shay) that the place to do it would be at the ES level.</p>
<p dir="auto">The following is a proposal to achieve it at the ES level. This is some very early planning and I've decided to post it early to make sure I'm not duplicating work or am on a totally wrong path (which is totally possible). Any feedback/comments are appreciated.</p>
<h2 dir="auto">Proposal</h2>
<p dir="auto">The general plan is to make a query only node (termed 'search load balancer' in <code class="notranslate">elasticsearch.yml</code>) and have a list of cluster names that we want to query.</p>
<p dir="auto">During a search, the node will query from each of the shards in each of the clusters and aggregate the results.</p>
<h2 dir="auto">Details</h2>
<ul dir="auto">
<li>Make a query only node
<ul dir="auto">
<li>In <code class="notranslate">elasticsearch.yml</code>
<ul dir="auto">
<li><code class="notranslate">node.master: false</code></li>
<li><code class="notranslate">node.data:   false</code></li>
</ul>
</li>
<li>I'm hoping this means that we can isolate code changes to only the search portion.</li>
</ul>
</li>
<li>Accommodate multiple clusters
<ul dir="auto">
<li>Have <code class="notranslate">ZenDiscover</code> reach out to all the listed clusters to get their state.</li>
<li><code class="notranslate">ClusterService</code> will contain a map of <code class="notranslate">ClusterName -&gt; ClusterState</code>.
<ul dir="auto">
<li>To maintain the interface, <code class="notranslate">clusterService.state()</code> will just return the first cluster.</li>
<li>We can have another interface that will allow us to get the the cluster map.</li>
</ul>
</li>
<li><code class="notranslate">MulticastZenPing</code> will have to listen for changes in the listed clusters and update the corresponding cluster state.</li>
</ul>
</li>
<li>Searching across multiple clusters
<ul dir="auto">
<li>I've only looked at <code class="notranslate">TransportSearchTypeAction</code> and <code class="notranslate">TransportSearchQueryThenFetchAction</code> so far.</li>
<li>In the <code class="notranslate">BaseAsyncAction</code>, we'll need to get all the relevant shards across each cluster and query them (<code class="notranslate">sendExecuteFirstPhase</code>).</li>
<li>We change <code class="notranslate">expectedSuccessfulOps</code> and <code class="notranslate">expectedTotalOps</code> to the multi-cluster counts so that in <code class="notranslate">onFirstPhaseResult</code> we know when to move on (<code class="notranslate">innerMoveToSecondPhase</code>).</li>
<li>In <code class="notranslate">moveToSecondPhase</code>, we again use the metadata from each cluster and do the actual fetch of the documents.</li>
<li>Finally in <code class="notranslate">innerFinishHim</code>, we merge all the results with the <code class="notranslate">SearchPhaseController</code> and return the response via the normally.</li>
</ul>
</li>
</ul>
<p dir="auto">Thanks!</p>