Beispiel #1
0
 private static void AssignParameters(Similarity.Net.MoreLikeThis mlt, MoreLikeThisQueryParameters parameters)
 {
     if (parameters.Boost != null)
     {
         mlt.SetBoost(parameters.Boost.Value);
     }
     if (parameters.MaximumNumberOfTokensParsed != null)
     {
         mlt.SetMaxNumTokensParsed(parameters.MaximumNumberOfTokensParsed.Value);
     }
     if (parameters.MaximumNumberOfTokensParsed != null)
     {
         mlt.SetMaxNumTokensParsed(parameters.MaximumNumberOfTokensParsed.Value);
     }
     if (parameters.MaximumQueryTerms != null)
     {
         mlt.SetMaxQueryTerms(parameters.MaximumQueryTerms.Value);
     }
     if (parameters.MaximumWordLength != null)
     {
         mlt.SetMaxWordLen(parameters.MaximumWordLength.Value);
     }
     if (parameters.MinimumDocumentFrequency != null)
     {
         mlt.SetMinDocFreq(parameters.MinimumDocumentFrequency.Value);
     }
     if (parameters.MinimumTermFrequency != null)
     {
         mlt.SetMinTermFreq(parameters.MinimumTermFrequency.Value);
     }
     if (parameters.MinimumWordLength != null)
     {
         mlt.SetMinWordLen(parameters.MinimumWordLength.Value);
     }
 }
Beispiel #2
0
        private IEnumerable <JsonDocument> GetJsonDocuments(
            MoreLikeThisQueryParameters parameters, IndexSearcher searcher, Index index,
            string indexName, IEnumerable <ScoreDoc> hits, int baseDocId)
        {
            if (string.IsNullOrEmpty(parameters.DocumentId) == false)
            {
                var documentIds = hits
                                  .Where(hit => hit.Doc != baseDocId)
                                  .Select(hit => searcher.Doc(hit.Doc).Get(Constants.DocumentIdFieldName))
                                  .Where(x => x != null)
                                  .Distinct();

                return(documentIds
                       .Select(docId => Database.Get(docId, null))
                       .Where(it => it != null)
                       .ToArray());
            }

            var fields = searcher.Doc(baseDocId).GetFields().Cast <AbstractField>().Select(x => x.Name).Distinct().ToArray();
            var etag   = Database.GetIndexEtag(indexName, null);

            return(hits
                   .Where(hit => hit.Doc != baseDocId)
                   .Select(hit => new JsonDocument
            {
                DataAsJson = Index.CreateDocumentFromFields(searcher.Doc(hit.Doc), new FieldsToFetch(fields, AggregationOperation.None,
                                                                                                     index.IsMapReduce ? Constants.ReduceKeyFieldName : Constants.DocumentIdFieldName)),
                Etag = etag
            })
                   .ToArray());
        }
Beispiel #3
0
        public static MoreLikeThisQueryParameters GetParametersFromPath(string path, NameValueCollection query)
        {
            var results = new MoreLikeThisQueryParameters
            {
                IndexName = query.Get("index"),
                Fields    = query.GetValues("fields"),
                Boost     = query.Get("boost").ToNullableBool(),
                MaximumNumberOfTokensParsed = query.Get("maxNumTokens").ToNullableInt(),
                MaximumQueryTerms           = query.Get("maxQueryTerms").ToNullableInt(),
                MaximumWordLength           = query.Get("maxWordLen").ToNullableInt(),
                MinimumDocumentFrequency    = query.Get("minDocFreq").ToNullableInt(),
                MinimumTermFrequency        = query.Get("minTermFreq").ToNullableInt(),
                MinimumWordLength           = query.Get("minWordLen").ToNullableInt(),
                StopWordsDocumentId         = query.Get("stopWords"),
            };

            var keyValues = query.Get("docid").Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);

            foreach (var keyValue in keyValues)
            {
                var split = keyValue.IndexOf('=');

                if (split >= 0)
                {
                    results.MapGroupFields.Add(keyValue.Substring(0, split), keyValue.Substring(split + 1));
                }
                else
                {
                    results.DocumentId = keyValue;
                }
            }

            return(results);
        }
        private static string GetRequestUri(string index, MoreLikeThisQueryParameters parameters)
        {
            var uri = new StringBuilder();

            uri.AppendFormat("/morelikethis/{0}/{1}?", Uri.EscapeUriString(index), Uri.EscapeUriString(parameters.DocumentId));
            if (parameters.Fields != null)
            {
                foreach (var field in parameters.Fields)
                {
                    uri.AppendFormat("fields={0}&", field);
                }
            }
            if (parameters.Boost != null && parameters.Boost != MoreLikeThisQueryParameters.DefaultBoost)
            {
                uri.Append("boost=true&");
            }
            if (parameters.MaximumQueryTerms != null &&
                parameters.MaximumQueryTerms != MoreLikeThisQueryParameters.DefaultMaximumQueryTerms)
            {
                uri.AppendFormat("maxQueryTerms={0}&", parameters.MaximumQueryTerms);
            }
            if (parameters.MaximumNumberOfTokensParsed != null &&
                parameters.MaximumNumberOfTokensParsed != MoreLikeThisQueryParameters.DefaultMaximumNumberOfTokensParsed)
            {
                uri.AppendFormat("maxNumTokens={0}&", parameters.MaximumNumberOfTokensParsed);
            }
            if (parameters.MaximumWordLength != null &&
                parameters.MaximumWordLength != MoreLikeThisQueryParameters.DefaultMaximumWordLength)
            {
                uri.AppendFormat("maxWordLen={0}&", parameters.MaximumWordLength);
            }
            if (parameters.MinimumDocumentFrequency != null &&
                parameters.MinimumDocumentFrequency != MoreLikeThisQueryParameters.DefaltMinimumDocumentFrequency)
            {
                uri.AppendFormat("minDocFreq={0}&", parameters.MinimumDocumentFrequency);
            }
            if (parameters.MinimumTermFrequency != null &&
                parameters.MinimumTermFrequency != MoreLikeThisQueryParameters.DefaultMinimumTermFrequency)
            {
                uri.AppendFormat("minTermFreq={0}&", parameters.MinimumTermFrequency);
            }
            if (parameters.MinimumWordLength != null &&
                parameters.MinimumWordLength != MoreLikeThisQueryParameters.DefaultMinimumWordLength)
            {
                uri.AppendFormat("minWordLen={0}&", parameters.MinimumWordLength);
            }
            if (parameters.StopWordsDocumentId != null)
            {
                uri.AppendFormat("stopWords={0}&", parameters.StopWordsDocumentId);
            }
            return(uri.ToString());
        }
Beispiel #5
0
        public void Can_encode_decode_request_by_documentId()
        {
            var parameters = new MoreLikeThisQueryParameters();

            parameters.IndexName                = "dataIndex";
            parameters.DocumentId               = "foo/1";
            parameters.Fields                   = new[] { "Body" };
            parameters.MinimumWordLength        = 3;
            parameters.MinimumDocumentFrequency = 1;
            parameters.Boost = true;

            var uri = parameters.GetRequestUri(parameters.IndexName);

            Assert.Equal("/morelikethis/?index=dataIndex&docid=foo%2F1&fields=Body&boost=true&minDocFreq=1&minWordLen=3&", uri);

            var path              = uri.Substring(0, uri.IndexOf('?'));
            var queryString       = HttpUtility.ParseQueryString(uri.Substring(uri.IndexOf('?')));
            var decodedParameters = MoreLikeThisQueryParameters.GetParametersFromPath(path, queryString);

            Assert.Equal("dataIndex", decodedParameters.IndexName);
            Assert.Equal(JsonConvert.SerializeObject(parameters), JsonConvert.SerializeObject(decodedParameters));
        }
Beispiel #6
0
        public override void Respond(IHttpContext context)
        {
            var parameters = MoreLikeThisQueryParameters.GetParametersFromPath(context.GetRequestUrl(), context.Request.QueryString);

            var index = Database.IndexStorage.GetIndexInstance(parameters.IndexName);

            if (index == null)
            {
                context.SetStatusToNotFound();
                context.WriteJson(new { Error = "The index " + parameters.IndexName + " cannot be found" });
                return;
            }

            if (string.IsNullOrEmpty(parameters.DocumentId) && parameters.MapGroupFields.Count == 0)
            {
                context.SetStatusToBadRequest();
                context.WriteJson(new { Error = "The document id or map group fields are mandatory" });
                return;
            }

            PerformSearch(context, parameters.IndexName, index, parameters);
        }
        public void Can_encode_decode_request_on_index_grouping()
        {
            var parameters = new MoreLikeThisQueryParameters();

            parameters.IndexName = "dataIndex";
            parameters.MapGroupFields.Add("foo", "bar");
            parameters.MapGroupFields.Add("be", "bop");
            parameters.Fields                   = new[] { "Body" };
            parameters.MinimumWordLength        = 3;
            parameters.MinimumDocumentFrequency = 1;
            parameters.Boost = true;

            var uri = parameters.GetRequestUri(parameters.IndexName);

            Assert.Equal("/morelikethis/?index=dataIndex&docid=foo%3Dbar%3Bbe%3Dbop&fields=Body&boost=true&minDocFreq=1&minWordLen=3&", uri);

            var path = uri.Substring(0, uri.IndexOf('?'));

            var decodedParameters = MoreLikeThisQueryParameters.GetParametersFromPath(uri);

            Assert.Equal("dataIndex", decodedParameters.IndexName);
            Assert.Equal(JsonConvert.SerializeObject(parameters), JsonConvert.SerializeObject(decodedParameters));
        }
		public override void Respond(IHttpContext context)
		{
			var match = urlMatcher.Match(context.GetRequestUrl());
			var indexName = match.Groups[1].Value;

			var parameters = new MoreLikeThisQueryParameters
								 {
									 DocumentId = match.Groups[2].Value,
									 Fields = context.Request.QueryString.GetValues("fields"),
                                     Boost = context.Request.QueryString.Get("boost").ToNullableBool(),
                                     MaximumNumberOfTokensParsed = context.Request.QueryString.Get("maxNumTokens").ToNullableInt(),
                                     MaximumQueryTerms = context.Request.QueryString.Get("maxQueryTerms").ToNullableInt(),
									 MaximumWordLength = context.Request.QueryString.Get("maxWordLen").ToNullableInt(),
									 MinimumDocumentFrequency = context.Request.QueryString.Get("minDocFreq").ToNullableInt(),
									 MinimumTermFrequency = context.Request.QueryString.Get("minTermFreq").ToNullableInt(),
									 MinimumWordLength = context.Request.QueryString.Get("minWordLen").ToNullableInt(),
                                     StopWordsDocumentId = context.Request.QueryString.Get("stopWords"),
		                         };
            
			var indexDefinition = Database.IndexDefinitionStorage.GetIndexDefinition(indexName);
			if (indexDefinition == null)
			{
				context.SetStatusToNotFound();
				context.WriteJson(new { Error = "The index " + indexName + " cannot be found" });
				return;
			}

			if (string.IsNullOrEmpty(parameters.DocumentId))
			{
				context.SetStatusToBadRequest();
				context.WriteJson(new { Error = "The document id is mandatory" });
				return;
			}

			PerformSearch(context, indexName, indexDefinition, parameters);
		}
        public static T[] MoreLikeThis <T>(this ISyncAdvancedSessionOperation advancedSession, string index, MoreLikeThisQueryParameters parameters)
        {
            var cmd = advancedSession.DocumentStore.DatabaseCommands as ServerClient;

            if (cmd == null)
            {
                throw new NotImplementedException("Embedded client isn't supported by the MoreLikeThis bundle");
            }


            var inMemoryDocumentSessionOperations = ((InMemoryDocumentSessionOperations)advancedSession);

            // /morelikethis/(index-name)/(ravendb-document-id)?fields=(fields)
            EnsureIsNotNullOrEmpty(index, "index");

            inMemoryDocumentSessionOperations.IncrementRequestCount();
            var             multiLoadOperation = new MultiLoadOperation(inMemoryDocumentSessionOperations, cmd.DisableAllCaching, null, null);
            MultiLoadResult multiLoadResult;

            do
            {
                multiLoadOperation.LogOperation();
                using (multiLoadOperation.EnterMultiLoadContext())
                {
                    var result = cmd.ExecuteGetRequest(parameters.GetRequestUri(index));

                    multiLoadResult = ((RavenJObject)result).Deserialize <MultiLoadResult>(inMemoryDocumentSessionOperations.Conventions);
                }
            } while (multiLoadOperation.SetResult(multiLoadResult));

            return(multiLoadOperation.Complete <T>());
        }
        public static T[] MoreLikeThis <T, TIndexCreator>(this ISyncAdvancedSessionOperation advancedSession, MoreLikeThisQueryParameters parameters) where TIndexCreator : AbstractIndexCreationTask, new()
        {
            var indexCreator = new TIndexCreator();

            return(MoreLikeThis <T>(advancedSession, indexCreator.IndexName, parameters));
        }
		private void PerformSearch(IHttpContext context, string indexName, Index index, MoreLikeThisQueryParameters parameters)
		{
			IndexSearcher searcher;
			using (Database.IndexStorage.GetCurrentIndexSearcher(indexName, out searcher))
			{
				var documentQuery = new BooleanQuery();

				if (!string.IsNullOrEmpty(parameters.DocumentId))
				{
					documentQuery.Add(new TermQuery(new Term(Constants.DocumentIdFieldName, parameters.DocumentId)),
					                  Lucene.Net.Search.BooleanClause.Occur.MUST);
				}

				foreach (string key in parameters.MapGroupFields.Keys)
				{
					documentQuery.Add(new TermQuery(new Term(key, parameters.MapGroupFields[key])),
					                  Lucene.Net.Search.BooleanClause.Occur.MUST);
				}

				var td = searcher.Search(documentQuery, 1);

				// get the current Lucene docid for the given RavenDB doc ID
				if (td.ScoreDocs.Length == 0)
				{
					context.SetStatusToNotFound();
					context.WriteJson(new {Error = "Document " + parameters.DocumentId + " could not be found"});
					return;
				}

				var ir = searcher.GetIndexReader();
				var mlt = new RavenMoreLikeThis(ir);

				AssignParameters(mlt, parameters);

				if (!string.IsNullOrWhiteSpace(parameters.StopWordsDocumentId))
				{
					var stopWordsDoc = Database.Get(parameters.StopWordsDocumentId, null);
					if (stopWordsDoc == null)
					{
						context.SetStatusToNotFound();
						context.WriteJson(
							new
							{
								Error = "Stop words document " + parameters.StopWordsDocumentId + " could not be found"
							});
						return;
					}
					var stopWords = stopWordsDoc.DataAsJson.JsonDeserialization<StopWordsSetup>().StopWords;
					mlt.SetStopWords(new Hashtable(stopWords.ToDictionary(x => x.ToLower())));
				}

				var fieldNames = parameters.Fields ?? GetFieldNames(ir);
				mlt.SetFieldNames(fieldNames);

				var toDispose = new List<Action>();
				PerFieldAnalyzerWrapper perFieldAnalyzerWrapper = null;
				try
				{
					perFieldAnalyzerWrapper = index.CreateAnalyzer(new LowerCaseKeywordAnalyzer(), toDispose, true);
					mlt.SetAnalyzer(perFieldAnalyzerWrapper);

					var mltQuery = mlt.Like(td.ScoreDocs[0].doc);
					var tsdc = TopScoreDocCollector.create(context.GetPageSize(Database.Configuration.MaxPageSize), true);
					searcher.Search(mltQuery, tsdc);
					var hits = tsdc.TopDocs().ScoreDocs;
					var jsonDocuments = GetJsonDocuments(parameters, searcher, indexName, hits, td.ScoreDocs[0].doc);

					var result = new MultiLoadResult();

					var includedEtags = new List<byte>(jsonDocuments.SelectMany(x => x.Etag.Value.ToByteArray()));
					includedEtags.AddRange(Database.GetIndexEtag(indexName, null).ToByteArray());
					var loadedIds = new HashSet<string>(jsonDocuments.Select(x => x.Key));
					var addIncludesCommand = new AddIncludesCommand(Database, GetRequestTransaction(context), (etag, includedDoc) =>
					{
						includedEtags.AddRange(etag.ToByteArray());
						result.Includes.Add(includedDoc);
					}, context.Request.QueryString.GetValues("include") ?? new string[0], loadedIds);

					foreach (var jsonDocumet in jsonDocuments)
					{
						result.Results.Add(jsonDocumet.ToJson());
						addIncludesCommand.Execute(jsonDocumet.DataAsJson);
					}

					Guid computedEtag;
					using (var md5 = MD5.Create())
					{
						var computeHash = md5.ComputeHash(includedEtags.ToArray());
						computedEtag = new Guid(computeHash);
					}

					if (context.MatchEtag(computedEtag))
					{
						context.SetStatusToNotModified();
						return;
					}

					context.Response.AddHeader("ETag", computedEtag.ToString());
					context.WriteJson(result);
				}
				finally
				{
					if (perFieldAnalyzerWrapper != null)
						perFieldAnalyzerWrapper.Close();
					foreach (var action in toDispose)
					{
						action();
					}
				}
			}
		}
		private static void AssignParameters(Similarity.Net.MoreLikeThis mlt, MoreLikeThisQueryParameters parameters)
		{
			if (parameters.Boost != null) mlt.SetBoost(parameters.Boost.Value);
			if (parameters.MaximumNumberOfTokensParsed != null)
				mlt.SetMaxNumTokensParsed(parameters.MaximumNumberOfTokensParsed.Value);
			if (parameters.MaximumNumberOfTokensParsed != null) mlt.SetMaxNumTokensParsed(parameters.MaximumNumberOfTokensParsed.Value);
			if (parameters.MaximumQueryTerms != null) mlt.SetMaxQueryTerms(parameters.MaximumQueryTerms.Value);
			if (parameters.MaximumWordLength != null) mlt.SetMaxWordLen(parameters.MaximumWordLength.Value);
			if (parameters.MinimumDocumentFrequency != null) mlt.SetMinDocFreq(parameters.MinimumDocumentFrequency.Value);
			if (parameters.MinimumTermFrequency != null) mlt.SetMinTermFreq(parameters.MinimumTermFrequency.Value);
			if (parameters.MinimumWordLength != null) mlt.SetMinWordLen(parameters.MinimumWordLength.Value);
		}
		private IEnumerable<JsonDocument> GetJsonDocuments(MoreLikeThisQueryParameters parameters, IndexSearcher searcher, string index, IEnumerable<ScoreDoc> hits, int baseDocId)
		{
			if (string.IsNullOrEmpty(parameters.DocumentId) == false)
			{
				var documentIds = hits
					.Where(hit => hit.doc != baseDocId)
					.Select(hit => searcher.Doc(hit.doc).Get(Constants.DocumentIdFieldName))
					.Where(x => x != null)
					.Distinct();

				return documentIds
					.Select(docId => Database.Get(docId, null))
					.Where(it => it != null)
					.ToArray();
			}

			var fields = searcher.Doc(baseDocId).GetFields().Cast<AbstractField>().Select(x=>x.Name()).Distinct().ToArray();
			var etag = Database.GetIndexEtag(index, null);
			return hits
				.Where(hit => hit.doc != baseDocId)
				.Select(hit => new JsonDocument
				{
					DataAsJson = Index.CreateDocumentFromFields(searcher.Doc(hit.doc), fields),
					Etag = etag
				})
				.ToArray();

		}
		private void PerformSearch(IHttpContext context, string indexName, IndexDefinition indexDefinition, MoreLikeThisQueryParameters parameters)
		{
			IndexSearcher searcher;
			using (Database.IndexStorage.GetCurrentIndexSearcher(indexName, out searcher))
			{
				var td = searcher.Search(new TermQuery(new Term(Constants.DocumentIdFieldName, parameters.DocumentId)), 1);
				// get the current Lucene docid for the given RavenDB doc ID
				if (td.ScoreDocs.Length == 0)
				{
					context.SetStatusToNotFound();
					context.WriteJson(new { Error = "Document " + parameters.DocumentId + " could not be found" });
					return;
				}
				var ir = searcher.GetIndexReader();
				var mlt = new RavenMoreLikeThis(ir);

				AssignParameters(mlt, parameters);

				if (!string.IsNullOrWhiteSpace(parameters.StopWordsDocumentId))
				{
					var stopWordsDoc = Database.Get(parameters.StopWordsDocumentId, null);
					if (stopWordsDoc == null)
					{
						context.SetStatusToNotFound();
						context.WriteJson(
							new
								{
									Error = "Stop words document " + parameters.StopWordsDocumentId + " could not be found"
								});
						return;
					}
					var stopWords = stopWordsDoc.DataAsJson.JsonDeserialization<StopWordsSetup>().StopWords;
					mlt.SetStopWords(new Hashtable(stopWords.ToDictionary(x => x.ToLower())));
				}

				var fieldNames = parameters.Fields ?? GetFieldNames(ir);
				mlt.SetFieldNames(fieldNames);

				mlt.Analyzers = GetAnalyzers(indexDefinition, fieldNames);

				var mltQuery = mlt.Like(td.ScoreDocs[0].doc);
				var tsdc = TopScoreDocCollector.create(context.GetPageSize(Database.Configuration.MaxPageSize), true);
				searcher.Search(mltQuery, tsdc);
				var hits = tsdc.TopDocs().ScoreDocs;
				var documentIds = hits.Select(hit => searcher.Doc(hit.doc).Get(Constants.DocumentIdFieldName)).Distinct();

				var jsonDocuments =
					documentIds
						.Where(docId => string.Equals(docId, parameters.DocumentId, StringComparison.InvariantCultureIgnoreCase) == false)
						.Select(docId => Database.Get(docId, null))
						.Where(it => it != null)
						.ToArray();

				var result = new MultiLoadResult();

				var includedEtags = new List<byte>(jsonDocuments.SelectMany(x => x.Etag.Value.ToByteArray()));
				includedEtags.AddRange(Database.GetIndexEtag(indexName).ToByteArray());
				var loadedIds = new HashSet<string>(jsonDocuments.Select(x => x.Key));
				var addIncludesCommand = new AddIncludesCommand(Database, GetRequestTransaction(context), (etag, includedDoc) =>
				{
					includedEtags.AddRange(etag.ToByteArray());
					result.Includes.Add(includedDoc);
				}, context.Request.QueryString.GetValues("include") ?? new string[0], loadedIds);

				foreach (var jsonDocumet in jsonDocuments)
				{
					result.Results.Add(jsonDocumet.ToJson());
					addIncludesCommand.Execute(jsonDocumet.DataAsJson);
				}

				Guid computedEtag;
				using (var md5 = MD5.Create())
				{
					var computeHash = md5.ComputeHash(includedEtags.ToArray());
					computedEtag = new Guid(computeHash);
				}

				if (context.MatchEtag(computedEtag))
				{
					context.SetStatusToNotModified();
					return;
				}

				context.Response.AddHeader("ETag", computedEtag.ToString());
				context.WriteJson(result);
			}
		}
		public static MoreLikeThisQueryParameters GetParametersFromPath(string path, NameValueCollection query)
		{
			var results = new MoreLikeThisQueryParameters
			{
				IndexName = query.Get("index"),
				Fields = query.GetValues("fields"),
				Boost = query.Get("boost").ToNullableBool(),
				MaximumNumberOfTokensParsed = query.Get("maxNumTokens").ToNullableInt(),
				MaximumQueryTerms = query.Get("maxQueryTerms").ToNullableInt(),
				MaximumWordLength = query.Get("maxWordLen").ToNullableInt(),
				MinimumDocumentFrequency = query.Get("minDocFreq").ToNullableInt(),
				MinimumTermFrequency = query.Get("minTermFreq").ToNullableInt(),
				MinimumWordLength = query.Get("minWordLen").ToNullableInt(),
				StopWordsDocumentId = query.Get("stopWords"),
			};

			var keyValues = query.Get("docid").Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
			foreach(var keyValue in keyValues)
			{
				var split = keyValue.IndexOf('=');

				if (split >= 0)
				{
					results.MapGroupFields.Add(keyValue.Substring(0, split), keyValue.Substring(split+1));
				} 
				else
				{
					results.DocumentId = keyValue;
				}
			}

			return results;
		}
Beispiel #16
0
        private void PerformSearch(IHttpContext context, string indexName, Index index, MoreLikeThisQueryParameters parameters)
        {
            IndexSearcher searcher;

            using (Database.IndexStorage.GetCurrentIndexSearcher(indexName, out searcher))
            {
                var documentQuery = new BooleanQuery();

                if (!string.IsNullOrEmpty(parameters.DocumentId))
                {
                    documentQuery.Add(new TermQuery(new Term(Constants.DocumentIdFieldName, parameters.DocumentId.ToLowerInvariant())),
                                      Occur.MUST);
                }

                foreach (string key in parameters.MapGroupFields.Keys)
                {
                    documentQuery.Add(new TermQuery(new Term(key, parameters.MapGroupFields[key])),
                                      Occur.MUST);
                }

                var td = searcher.Search(documentQuery, 1);

                // get the current Lucene docid for the given RavenDB doc ID
                if (td.ScoreDocs.Length == 0)
                {
                    context.SetStatusToNotFound();
                    context.WriteJson(new { Error = "Document " + parameters.DocumentId + " could not be found" });
                    return;
                }

                var ir  = searcher.IndexReader;
                var mlt = new RavenMoreLikeThis(ir);

                AssignParameters(mlt, parameters);

                if (!string.IsNullOrWhiteSpace(parameters.StopWordsDocumentId))
                {
                    var stopWordsDoc = Database.Get(parameters.StopWordsDocumentId, null);
                    if (stopWordsDoc == null)
                    {
                        context.SetStatusToNotFound();
                        context.WriteJson(
                            new
                        {
                            Error = "Stop words document " + parameters.StopWordsDocumentId + " could not be found"
                        });
                        return;
                    }
                    var stopWordsSetup = stopWordsDoc.DataAsJson.JsonDeserialization <StopWordsSetup>();
                    if (stopWordsSetup.StopWords != null)
                    {
                        var stopWords = stopWordsSetup.StopWords;
                        var ht        = new Hashtable(StringComparer.InvariantCultureIgnoreCase);
                        foreach (var stopWord in stopWords)
                        {
                            ht[stopWord] = stopWord;
                        }
                        mlt.SetStopWords(ht);
                    }
                }

                var fieldNames = parameters.Fields ?? GetFieldNames(ir);
                mlt.SetFieldNames(fieldNames);

                var toDispose = new List <Action>();
                PerFieldAnalyzerWrapper perFieldAnalyzerWrapper = null;
                try
                {
                    perFieldAnalyzerWrapper = index.CreateAnalyzer(new LowerCaseKeywordAnalyzer(), toDispose, true);
                    mlt.SetAnalyzer(perFieldAnalyzerWrapper);

                    var mltQuery = mlt.Like(td.ScoreDocs[0].Doc);
                    var tsdc     = TopScoreDocCollector.Create(context.GetPageSize(Database.Configuration.MaxPageSize), true);
                    searcher.Search(mltQuery, tsdc);
                    var hits          = tsdc.TopDocs().ScoreDocs;
                    var jsonDocuments = GetJsonDocuments(parameters, searcher, index, indexName, hits, td.ScoreDocs[0].Doc);

                    var result = new MultiLoadResult();

                    var includedEtags = new List <byte>(jsonDocuments.SelectMany(x => x.Etag.Value.ToByteArray()));
                    includedEtags.AddRange(Database.GetIndexEtag(indexName, null).ToByteArray());
                    var loadedIds          = new HashSet <string>(jsonDocuments.Select(x => x.Key));
                    var addIncludesCommand = new AddIncludesCommand(Database, GetRequestTransaction(context), (etag, includedDoc) =>
                    {
                        includedEtags.AddRange(etag.ToByteArray());
                        result.Includes.Add(includedDoc);
                    }, context.Request.QueryString.GetValues("include") ?? new string[0], loadedIds);

                    foreach (var jsonDocumet in jsonDocuments)
                    {
                        result.Results.Add(jsonDocumet.ToJson());
                        addIncludesCommand.Execute(jsonDocumet.DataAsJson);
                    }

                    Guid computedEtag;
                    using (var md5 = MD5.Create())
                    {
                        var computeHash = md5.ComputeHash(includedEtags.ToArray());
                        computedEtag = new Guid(computeHash);
                    }

                    if (context.MatchEtag(computedEtag))
                    {
                        context.SetStatusToNotModified();
                        return;
                    }

                    context.Response.AddHeader("ETag", computedEtag.ToString());
                    context.WriteJson(result);
                }
                finally
                {
                    if (perFieldAnalyzerWrapper != null)
                    {
                        perFieldAnalyzerWrapper.Close();
                    }
                    foreach (var action in toDispose)
                    {
                        action();
                    }
                }
            }
        }