Example #1
0
		public override void Respond(IHttpContext context)
		{
			JArray itemsToLoad;
			if(context.Request.HttpMethod == "POST")
				itemsToLoad = context.ReadJsonArray();
			else
				itemsToLoad = new JArray(context.Request.QueryString.GetValues("id"));
			var result = new MultiLoadResult();
			var loadedIds = new HashSet<string>();
			var includes = context.Request.QueryString.GetValues("include") ?? new string[0];
			var transactionInformation = GetRequestTransaction(context);
			Database.TransactionalStorage.Batch(actions =>
			{
				var addIncludesCommand = new AddIncludesCommand(Database, transactionInformation, result.Includes.Add, includes, loadedIds);
				foreach (JToken item in itemsToLoad)
				{
					var value = item.Value<string>();
					if(loadedIds.Add(value)==false)
						continue;
					var documentByKey = actions.Documents.DocumentByKey(value,
                        transactionInformation);
					if (documentByKey == null)
						continue;
					result.Results.Add(documentByKey.ToJson());

					addIncludesCommand.Execute(documentByKey.DataAsJson);
				}
            });
			context.WriteJson(result);
		}
Example #2
0
        private HttpResponseMessage GetIndexQueryResult(string index, CancellationToken token)
        {
            Etag indexEtag;
            var  msg         = GetEmptyMessage();
            var  queryResult = ExecuteQuery(index, out indexEtag, msg, token);

            if (queryResult == null)
            {
                return(msg);
            }

            var includes  = GetQueryStringValues("include") ?? new string[0];
            var loadedIds = new HashSet <string>(
                queryResult.Results
                .Where(x => x != null && x["@metadata"] != null)
                .Select(x => x["@metadata"].Value <string>("@id"))
                .Where(x => x != null)
                );
            var command = new AddIncludesCommand(Database, GetRequestTransaction(),
                                                 (etag, doc) => queryResult.Includes.Add(doc), includes, loadedIds);

            foreach (var result in queryResult.Results)
            {
                command.Execute(result);
            }
            command.AlsoInclude(queryResult.IdsToInclude);

            return(GetMessageWithObject(queryResult, queryResult.NonAuthoritativeInformation ? HttpStatusCode.NonAuthoritativeInformation : HttpStatusCode.OK, indexEtag));
        }
Example #3
0
		public override void Respond(IHttpContext context)
		{
			RavenJArray itemsToLoad;
			if(context.Request.HttpMethod == "POST")
				itemsToLoad = context.ReadJsonArray();
			else
				itemsToLoad = new RavenJArray(context.Request.QueryString.GetValues("id"));
			var result = new MultiLoadResult();
			var loadedIds = new HashSet<string>();
			var includes = context.Request.QueryString.GetValues("include") ?? new string[0];
			var transactionInformation = GetRequestTransaction(context);
		    var includedEtags = new List<byte>();
			Database.TransactionalStorage.Batch(actions =>
			{
				foreach (RavenJToken item in itemsToLoad)
				{
					var value = item.Value<string>();
					if(loadedIds.Add(value)==false)
						continue;
					var documentByKey = Database.Get(value, transactionInformation);
					if (documentByKey == null)
						continue;
					result.Results.Add(documentByKey.ToJson());

					if (documentByKey.Etag != null)
					{
						includedEtags.AddRange(documentByKey.Etag.Value.ToByteArray());
					}
					includedEtags.Add((documentByKey.NonAuthoritativeInformation ?? false) ? (byte)0 : (byte)1);
				}

				var addIncludesCommand = new AddIncludesCommand(Database, transactionInformation, (etag, includedDoc) =>
				{
					includedEtags.AddRange(etag.ToByteArray());
					result.Includes.Add(includedDoc);
				}, includes, loadedIds);

				foreach (var item in result.Results.Where(item => item != null))
				{
					addIncludesCommand.Execute(item);
				}
			});

			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.WriteETag(computedEtag);
			context.WriteJson(result);
		}
Example #4
0
        /// <summary>
        /// Gets the results for the specified ids.
        /// </summary>
        /// <param name="ids">The ids.</param>
        /// <param name="includes">The includes.</param>
        /// <param name="metadataOnly">Load just the document metadata</param>
        /// <returns></returns>
        public MultiLoadResult Get(string[] ids, string[] includes, bool metadataOnly = false)
        {
            CurrentOperationContext.Headers.Value = OperationsHeaders;

            // metadata only is not supported for embedded

            var multiLoadResult = new MultiLoadResult
            {
                Results = ids
                          .Select(id => database.Get(id, TransactionInformation))
                          .ToArray()
                          .Select(x => x == null ? null : x.ToJson())
                          .ToList(),
            };

            if (includes != null)
            {
                var includeCmd = new AddIncludesCommand(database, TransactionInformation, (etag, doc) => multiLoadResult.Includes.Add(doc), includes,
                                                        new HashSet <string>(ids));
                foreach (var jsonDocument in multiLoadResult.Results)
                {
                    includeCmd.Execute(jsonDocument);
                }
            }

            return(multiLoadResult);
        }
Example #5
0
        /// <summary>
        /// Queries the specified index.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <param name="query">The query.</param>
        /// <param name="includes">The includes are ignored for this implementation.</param>
        /// <param name="metadataOnly">Load just the document metadata</param>
        /// <param name="indexEntriesOnly">Include index entries</param>
        public QueryResult Query(string index, IndexQuery query, string[] includes, bool metadataOnly = false, bool indexEntriesOnly = false)
        {
            query.PageSize = Math.Min(query.PageSize, database.Configuration.MaxPageSize);
            CurrentOperationContext.Headers.Value = OperationsHeaders;

            // metadataOnly is not supported for embedded

            // indexEntriesOnly is not supported for embedded

            QueryResultWithIncludes queryResult;

            if (index.StartsWith("dynamic/", StringComparison.InvariantCultureIgnoreCase) || index.Equals("dynamic", StringComparison.InvariantCultureIgnoreCase))
            {
                string entityName = null;
                if (index.StartsWith("dynamic/"))
                {
                    entityName = index.Substring("dynamic/".Length);
                }
                queryResult = database.ExecuteDynamicQuery(entityName, query.Clone());
            }
            else
            {
                queryResult = database.Query(index, query.Clone());
            }
            EnsureLocalDate(queryResult.Results);

            var loadedIds = new HashSet <string>(
                queryResult.Results
                .Where(x => x["@metadata"] != null)
                .Select(x => x["@metadata"].Value <string>("@id"))
                .Where(x => x != null)
                );

            if (includes != null)
            {
                var includeCmd = new AddIncludesCommand(database, TransactionInformation,
                                                        (etag, doc) => queryResult.Includes.Add(doc), includes, loadedIds);

                foreach (var result in queryResult.Results)
                {
                    includeCmd.Execute(result);
                }

                includeCmd.AlsoInclude(queryResult.IdsToInclude);

                EnsureLocalDate(queryResult.Includes);
            }

            return(queryResult);
        }
Example #6
0
        /// <summary>
        /// Gets the results for the specified ids.
        /// </summary>
        /// <param name="ids">The ids.</param>
        /// <param name="includes">The includes.</param>
        /// <returns></returns>
        public MultiLoadResult Get(string[] ids, string[] includes)
        {
            CurrentOperationContext.Headers.Value = OperationsHeaders;

            var multiLoadResult = new MultiLoadResult
            {
                Results = ids
                          .Select(id => database.Get(id, TransactionInformation))
                          .Where(document => document != null)
                          .ToArray()
                          .Select(x => EnsureLocalDate(x).ToJson())
                          .ToList(),
            };
            var includeCmd = new AddIncludesCommand(database, TransactionInformation, (etag, doc) => multiLoadResult.Includes.Add(doc), includes, new HashSet <string>(ids));

            foreach (var jsonDocument in multiLoadResult.Results)
            {
                includeCmd.Execute(jsonDocument);
            }
            return(multiLoadResult);
        }
Example #7
0
		private void GetIndexQueryRessult(IHttpContext context, string index)
		{
			Guid indexEtag;

			QueryResult queryResult = ExecuteQuery(context, index, out indexEtag);

			if (queryResult == null)
				return;

			var includes = context.Request.QueryString.GetValues("include") ?? new string[0];
			var loadedIds = new HashSet<string>(
				queryResult.Results
					.Where(x => x["@metadata"] != null)
					.Select(x => x["@metadata"].Value<string>("@id"))
					.Where(x => x != null)
				);
			var command = new AddIncludesCommand(Database, GetRequestTransaction(context),
			                                     (etag, doc) => queryResult.Includes.Add(doc), includes, loadedIds);
			foreach (var result in queryResult.Results)
			{
				command.Execute(result);
			}
			
			context.Response.AddHeader("ETag", indexEtag.ToString());
			context.WriteJson(queryResult);
		}
Example #8
0
        private void GetIndexQueryResult(IHttpContext context, string index)
        {
            Etag indexEtag;
            QueryResultWithIncludes queryResult;
	        using (var cts = new CancellationTokenSource())
	        {
		        cts.TimeoutAfter(Settings.DatbaseOperationTimeout);
		        queryResult = ExecuteQuery(context, index, cts.Token, out indexEtag);
	        }
	        if (queryResult == null)
                return;

            var includes = context.Request.QueryString.GetValues("include") ?? new string[0];
            var loadedIds = new HashSet<string>(
                queryResult.Results
                    .Where(x => x["@metadata"] != null)
                    .Select(x => x["@metadata"].Value<string>("@id"))
                    .Where(x => x != null)
                );
            var command = new AddIncludesCommand(Database, GetRequestTransaction(context),
                                                 (etag, doc) => queryResult.Includes.Add(doc), includes, loadedIds);
            foreach (var result in queryResult.Results)
            {
                command.Execute(result);
            }
            command.AlsoInclude(queryResult.IdsToInclude);

            context.WriteETag(indexEtag);
            if (queryResult.NonAuthoritativeInformation)
                context.SetStatusToNonAuthoritativeInformation();

            context.WriteJson(queryResult);
        }
Example #9
0
        public MoreLikeThisQueryResult ExecuteMoreLikeThisQuery(MoreLikeThisQuery query, TransactionInformation transactionInformation, int pageSize = 25)
        {
            if (query == null)
            {
                throw new ArgumentNullException("query");
            }

            var index = database.IndexStorage.GetIndexInstance(query.IndexName);

            if (index == null)
            {
                throw new InvalidOperationException("The index " + query.IndexName + " cannot be found");
            }

            if (string.IsNullOrEmpty(query.DocumentId) && query.MapGroupFields.Count == 0)
            {
                throw new InvalidOperationException("The document id or map group fields are mandatory");
            }

            IndexSearcher searcher;

            using (database.IndexStorage.GetCurrentIndexSearcher(index.indexId, out searcher))
            {
                var documentQuery = new BooleanQuery();

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

                foreach (string key in query.MapGroupFields.Keys)
                {
                    documentQuery.Add(new TermQuery(new Term(key, query.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)
                {
                    throw new InvalidOperationException("Document " + query.DocumentId + " could not be found");
                }

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

                AssignParameters(mlt, query);

                if (string.IsNullOrWhiteSpace(query.StopWordsDocumentId) == false)
                {
                    var stopWordsDoc = database.Documents.Get(query.StopWordsDocumentId, null);
                    if (stopWordsDoc == null)
                    {
                        throw new InvalidOperationException("Stop words document " + query.StopWordsDocumentId + " could not be found");
                    }

                    var stopWordsSetup = stopWordsDoc.DataAsJson.JsonDeserialization <StopWordsSetup>();
                    if (stopWordsSetup.StopWords != null)
                    {
                        var stopWords = stopWordsSetup.StopWords;
                        var ht        = new HashSet <string>(StringComparer.InvariantCultureIgnoreCase);
                        foreach (var stopWord in stopWords)
                        {
                            ht.Add(stopWord);
                        }
                        mlt.SetStopWords(ht);
                    }
                }

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

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

                    var mltQuery = mlt.Like(td.ScoreDocs[0].Doc);
                    var tsdc     = TopScoreDocCollector.Create(pageSize, true);


                    if (string.IsNullOrWhiteSpace(query.AdditionalQuery) == false)
                    {
                        var additionalQuery = QueryBuilder.BuildQuery(query.AdditionalQuery, perFieldAnalyzerWrapper);
                        mltQuery = new BooleanQuery
                        {
                            { mltQuery, Occur.MUST },
                            { additionalQuery, Occur.MUST },
                        };
                    }

                    searcher.Search(mltQuery, tsdc);
                    var hits          = tsdc.TopDocs().ScoreDocs;
                    var jsonDocuments = GetJsonDocuments(query, searcher, index, query.IndexName, hits, td.ScoreDocs[0].Doc);

                    var result = new MultiLoadResult();

                    var includedEtags = new List <byte>(jsonDocuments.SelectMany(x => x.Etag.ToByteArray()));
                    includedEtags.AddRange(database.Indexes.GetIndexEtag(query.IndexName, null).ToByteArray());
                    var loadedIds          = new HashSet <string>(jsonDocuments.Select(x => x.Key));
                    var addIncludesCommand = new AddIncludesCommand(database, transactionInformation, (etag, includedDoc) =>
                    {
                        includedEtags.AddRange(etag.ToByteArray());
                        result.Includes.Add(includedDoc);
                    }, query.Includes ?? new string[0], loadedIds);

                    idsToLoad = new HashSet <string>();

                    database.TransactionalStorage.Batch(actions =>
                    {
                        documentRetriever = new DocumentRetriever(database.Configuration, actions, database.ReadTriggers, query.TransformerParameters, idsToLoad);

                        using (new CurrentTransformationScope(database, documentRetriever))
                        {
                            foreach (var document in ProcessResults(query, jsonDocuments, database.WorkContext.CancellationToken))
                            {
                                result.Results.Add(document);
                                addIncludesCommand.Execute(document);
                            }
                        }
                    });

                    addIncludesCommand.AlsoInclude(idsToLoad);

                    var  computeHash  = Encryptor.Current.Hash.Compute16(includedEtags.ToArray());
                    Etag computedEtag = Etag.Parse(computeHash);

                    return(new MoreLikeThisQueryResult
                    {
                        Etag = computedEtag,
                        Result = result,
                    });
                }
                finally
                {
                    if (perFieldAnalyzerWrapper != null)
                    {
                        perFieldAnalyzerWrapper.Close();
                    }
                    foreach (var action in toDispose)
                    {
                        action();
                    }
                }
            }
        }
        private async Task <HttpResponseMessage> GetQueriesResponse(bool isGet)
        {
            RavenJArray itemsToLoad;

            if (isGet == false)
            {
                try
                {
                    itemsToLoad = await ReadJsonArrayAsync().ConfigureAwait(false);
                }
                catch (InvalidOperationException e)
                {
                    if (Log.IsDebugEnabled)
                    {
                        Log.DebugException("Failed to deserialize query request.", e);
                    }
                    return(GetMessageWithObject(new
                    {
                        Message = "Could not understand json, please check its validity."
                    }, (HttpStatusCode)422)); //http code 422 - Unprocessable entity
                }
                catch (InvalidDataException e)
                {
                    if (Log.IsDebugEnabled)
                    {
                        Log.DebugException("Failed to deserialize query request.", e);
                    }
                    return(GetMessageWithObject(new
                    {
                        e.Message
                    }, (HttpStatusCode)422)); //http code 422 - Unprocessable entity
                }

                AddRequestTraceInfo(sb =>
                {
                    foreach (var item in itemsToLoad)
                    {
                        sb.Append("\t").Append(item).AppendLine();
                    }
                });
            }
            else
            {
                itemsToLoad = new RavenJArray(GetQueryStringValues("id").Cast <object>());
            }

            var result                 = new MultiLoadResult();
            var loadedIds              = new HashSet <string>();
            var includedIds            = new HashSet <string>();
            var includes               = GetQueryStringValues("include") ?? new string[0];
            var transformer            = GetQueryStringValue("transformer") ?? GetQueryStringValue("resultTransformer");
            var transformerParameters  = this.ExtractTransformerParameters();
            var transactionInformation = GetRequestTransaction();
            var includedEtags          = new List <byte>();

            if (string.IsNullOrEmpty(transformer) == false)
            {
                var transformerDef = Database.IndexDefinitionStorage.GetTransformer(transformer);
                if (transformerDef == null)
                {
                    return(GetMessageWithObject(new { Error = "No such transformer: " + transformer }, HttpStatusCode.BadRequest));
                }
                includedEtags.AddRange(transformerDef.GetHashCodeBytes());
            }

            Database.TransactionalStorage.Batch(actions =>
            {
                foreach (RavenJToken item in itemsToLoad)
                {
                    var value = item.Value <string>();
                    if (loadedIds.Add(value) == false)
                    {
                        continue;
                    }
                    var documentByKey = string.IsNullOrEmpty(transformer)
                                        ? Database.Documents.Get(value, transactionInformation)
                                        : Database.Documents.GetWithTransformer(value, transformer, transactionInformation, transformerParameters, out includedIds);
                    if (documentByKey == null)
                    {
                        if (ClientIsV3OrHigher(Request))
                        {
                            result.Results.Add(null);
                        }
                        continue;
                    }
                    result.Results.Add(documentByKey.ToJson());

                    if (documentByKey.Etag != null)
                    {
                        includedEtags.AddRange(documentByKey.Etag.ToByteArray());
                    }

                    includedEtags.Add((documentByKey.NonAuthoritativeInformation ?? false) ? (byte)0 : (byte)1);
                }

                var addIncludesCommand = new AddIncludesCommand(Database, transactionInformation, (etag, includedDoc) =>
                {
                    includedEtags.AddRange(etag.ToByteArray());
                    result.Includes.Add(includedDoc);
                }, includes, loadedIds);

                foreach (var item in result.Results.Where(item => item != null))
                {
                    addIncludesCommand.Execute(item);
                }
            });


            foreach (var includedId in includedIds)
            {
                var doc = Database.Documents.Get(includedId, transactionInformation);
                if (doc == null)
                {
                    continue;
                }
                includedEtags.AddRange(doc.Etag.ToByteArray());
                result.Includes.Add(doc.ToJson());
            }

            var  computeHash  = Encryptor.Current.Hash.Compute16(includedEtags.ToArray());
            Etag computedEtag = Etag.Parse(computeHash);

            if (MatchEtag(computedEtag))
            {
                return(GetEmptyMessage(HttpStatusCode.NotModified));
            }

            var msg = GetMessageWithObject(result);

            WriteETag(computedEtag, msg);

            AddRequestTraceInfo(sb => sb.AppendFormat("Results count: {0}, includes count: {1}", result.Results.Count, result.Includes.Count).AppendLine());

            return(msg);
        }
Example #11
0
        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, 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);
            }
        }
Example #12
0
		private void OnGet(IHttpContext context, string index)
		{
			var definition = context.Request.QueryString["definition"];
		    if ("yes".Equals(definition, StringComparison.InvariantCultureIgnoreCase))
		    {
		    	var indexDefinition = Database.GetIndexDefinition(index);
				if(indexDefinition == null)
				{
					context.SetStatusToNotFound();
					return;
				}
		    	context.WriteJson(new {Index = indexDefinition});
		    }
		    else
            {
				var indexQuery = context.GetIndexQueryFromHttpContext(Database.Configuration.MaxPageSize);
            	var queryResult = Database.Query(index, indexQuery);
            	var includes = context.Request.QueryString.GetValues("include") ?? new string[0];
            	var loadedIds = new HashSet<string>(
            		queryResult.Results
            			.Where(x => x["@metadata"] != null)
            			.Select(x => x["@metadata"].Value<string>("@id"))
            			.Where(x => x != null)
            		);
            	var command = new AddIncludesCommand(Database, GetRequestTransaction(context), queryResult.Includes.Add,includes, loadedIds);
            	foreach (var result in queryResult.Results)
            	{
            		command.Execute(result);
            	}
            	context.WriteJson(queryResult);
			}
		}
        public MoreLikeThisQueryResult ExecuteMoreLikeThisQuery(MoreLikeThisQuery query, TransactionInformation transactionInformation, int pageSize = 25, string[] include = null)
        {
            if (query == null)
            {
                throw new ArgumentNullException("query");
            }

            var index = database.IndexStorage.GetIndexInstance(query.IndexName);

            if (index == null)
            {
                throw new InvalidOperationException("The index " + query.IndexName + " cannot be found");
            }

            if (string.IsNullOrEmpty(query.DocumentId) && query.MapGroupFields.Count == 0)
            {
                throw new InvalidOperationException("The document id or map group fields are mandatory");
            }

            IndexSearcher searcher;

            using (database.IndexStorage.GetCurrentIndexSearcher(query.IndexName, out searcher))
            {
                var documentQuery = new BooleanQuery();

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

                foreach (string key in query.MapGroupFields.Keys)
                {
                    documentQuery.Add(new TermQuery(new Term(key, query.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)
                {
                    throw new InvalidOperationException("Document " + query.DocumentId + " could not be found");
                }

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

                AssignParameters(mlt, query);

                if (string.IsNullOrWhiteSpace(query.StopWordsDocumentId) == false)
                {
                    var stopWordsDoc = database.Get(query.StopWordsDocumentId, null);
                    if (stopWordsDoc == null)
                    {
                        throw new InvalidOperationException("Stop words document " + query.StopWordsDocumentId + " could not be found");
                    }

                    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 = query.Fields ?? GetFieldNames(ir);
                mlt.SetFieldNames(fieldNames);

                var toDispose = new List <Action>();
                RavenPerFieldAnalyzerWrapper 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(pageSize, true);
                    searcher.Search(mltQuery, tsdc);
                    var hits          = tsdc.TopDocs().ScoreDocs;
                    var jsonDocuments = GetJsonDocuments(query, searcher, index, query.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(query.IndexName, null).ToByteArray());
                    var loadedIds          = new HashSet <string>(jsonDocuments.Select(x => x.Key));
                    var addIncludesCommand = new AddIncludesCommand(database, transactionInformation, (etag, includedDoc) =>
                    {
                        includedEtags.AddRange(etag.ToByteArray());
                        result.Includes.Add(includedDoc);
                    }, include ?? new string[0], loadedIds);

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

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

                    return(new MoreLikeThisQueryResult
                    {
                        Etag = computedEtag,
                        Result = result,
                    });
                }
                finally
                {
                    if (perFieldAnalyzerWrapper != null)
                    {
                        perFieldAnalyzerWrapper.Close();
                    }
                    foreach (var action in toDispose)
                    {
                        action();
                    }
                }
            }
        }
        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())),
                                      BooleanClause.Occur.MUST);
                }

                foreach (string key in parameters.MapGroupFields.Keys)
                {
                    documentQuery.Add(new TermQuery(new Term(key, parameters.MapGroupFields[key])),
                                      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();
                    }
                }
            }
        }