<commit_before>﻿/*
 * Copyright (c) 2013 Algolia
 * http://www.algolia.com/
 * Based on the first version developed by <NAME> under the same license:
 *  https://github.com/cmaneu/algoliasearch-client-csharp
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Algolia.Search
{
    /// <summary>
    ///  Contains all the functions related to one index
    /// You should use AlgoliaClient.initIndex(indexName) to instanciate this object.
    /// </summary>
    public class Index
    {
        private AlgoliaClient  _client;
        private string         _indexName;
        private string         _urlIndexName;

        /// <summary>
        /// Index initialization (You should not call this constructor yourself)
        /// </summary>
        public Index(AlgoliaClient client, string indexName)
        {
            _client = client;
            _indexName = indexName;
            _urlIndexName = Uri.EscapeDataString(indexName);
        }

        /// <summary>
        /// Add an object in this index.
        /// </summary>
        /// <param name="content">Represent your object that you want to add in the index.</param>
        /// <param name="objectId">Optional. an objectID you want to attribute to this object (if the attribute already exist the old object will be overwrite)</param>
        /// <returns>An object that contains an "objectID" attribute.</returns>
        public Task<JObject> AddObject(object content, string objectId = null)
        {
            if (string.IsNullOrWhiteSpace(objectId))
            {
                return _client.ExecuteRequest("POST", string.Format("/1/indexes/{0}", _urlIndexName), content);
            }
            else
            {
                return _client.ExecuteRequest("PUT", string.Format("/1/indexes/{0}/{1}", _urlIndexName, Uri.EscapeDataString(objectId)), content);
            }
        }

        /// <summary>
        /// Add several objects.
        /// </summary>
        /// <param name="objects">contains an array of objects to add. If the object contains an objectID.</param>
        /// <returns>return an object containing an "objectIDs" attribute (array of string)</returns>
        public Task<JObject> AddObjects(IEnumerable<object> objects)
        {
            List<object> requests = new List<object>();
            foreach (object obj in objects) {
                Dictionary<string, object> request = new Dictionary<string, object>();
                request["action"] = "addObject";
                request["body"] = obj;
                requests.Add(request);
            }
            Dictionary<string, object> batch = new Dictionary<string, object>();
            batch["requests"] = requests;
            return _client.ExecuteRequest("POST", string.Format("/1/indexes/{0}/batch", _urlIndexName), batch);
        }

        /// <summary>
        /// Get an object from this index
        /// </summary>
        /// <param name="objectID">the unique identifier of the object to retrieve</param>
        /// <param name="attributesToRetrieve"> if set, contains the list of attributes to retrieve</param>
        /// <returns></returns>
        public Task<JObject> GetObject(string objectID, IEnumerable<string> attributesToRetrieve = null)
        {
            if (attributesToRetrieve == null)
            {
                return _client.ExecuteRequest("GET", string.Format("/1/indexes/{0}/{1}", _urlIndexName, Uri.EscapeDataString(objectID)));
            }
            else
            {
                string attributes = "";
                foreach (string attr in attributesToRetrieve)
                {
                    if (attributes.Length > 0)
                        attributes += ",";
                    attributes += Uri.EscapeDataString(attr);
                }
                return _client.ExecuteRequest("GET", string.Format("/1/indexes/{0}/{1}?attributes={2}", _urlIndexName, Uri.EscapeDataString(objectID), attributes));
            }
        }

        /// <summary>
        /// Update partially an object (only update attributes passed in argument).
        /// </summary>
        /// <param name="partialObject">contains the object attributes to override, the object must contains an objectID attribute</param>
        /// <returns>return an object containing an "updatedAt" attribute.</returns>
        public Task<JObject> PartialUpdateObject(JObject partialObject)
        {
            if (partialObject["objectID"] == null)
            {
                throw new AlgoliaException("objectID is missing");
            }
            string objectID = (string)partialObject["objectID"];
            return _client.ExecuteRequest("POST", string.Format("/1/indexes/{0}/{1}/partial", _urlIndexName, Uri.EscapeDataString(objectID)), partialObject);
        }

        /// <summary>
        /// Partially Override the content of several objects
        /// </summary>
        /// <param name="objects">contains an array of objects to update (each object must contains a objectID attribute).</param>
        /// <returns>return an object containing an "objectIDs" attribute (array of string)</returns>
        public Task<JObject> PartialUpdateObjects(IEnumerable<JObject> objects)
        {
            List<object> requests = new List<object>();
            foreach (JObject obj in objects)
            {
                Dictionary<string, object> request = new Dictionary<string, object>();
                request["action"] = "partialUpdateObject";
                if (obj["objectID"] == null)
                {
                    throw new AlgoliaException("objectID is missing");
                }
                request["objectID"] = obj["objectID"];
                request["body"] = obj;
                requests.Add(request);
            }
            Dictionary<string, object> batch = new Dictionary<string, object>();
            batch["requests"] = requests;
            return _client.ExecuteRequest("POST", string.Format("/1/indexes/{0}/batch", _urlIndexName), batch);
        }

        /// <summary>
        /// Override the content of object.
        /// </summary>
        /// <param name="obj">contains the object to save, the object must contains an objectID attribute.</param>
        /// <returns>return an object containing an "updatedAt" attribute.</returns>
        public Task<JObject> SaveObject(JObject obj)
        {
            if (obj["objectID"] == null)
            {
                throw new AlgoliaException("objectID is missing");
            }
            string objectID = (string)obj["objectID"];
            return _client.ExecuteRequest("PUT", string.Format("/1/indexes/{0}/{1}", _urlIndexName, Uri.EscapeDataString(objectID)), obj);
        }

        /// <summary>
        /// Override the content of several objects.
        /// </summary>
        /// <param name="objects">contains an array of objects to update (each object must contains a objectID attribute).</param>
        /// <returns>return an object containing an "objectIDs" attribute (array of string)</returns>
        public Task<JObject> SaveObjects(IEnumerable<JObject> objects)
        {
            List<object> requests = new List<object>();
            foreach (JObject obj in objects)
            {
                Dictionary<string, object> request = new Dictionary<string, object>();
                request["action"] = "updateObject";
                if (obj["objectID"] == null)
                {
                    throw new AlgoliaException("objectID is missing");
                }
                request["objectID"] = obj["objectID"];
                request["body"] = obj;
                requests.Add(request);
            }
            Dictionary<string, object> batch = new Dictionary<string, object>();
            batch["requests"] = requests;
            return _client.ExecuteRequest("POST", string.Format("/1/indexes/{0}/batch", _urlIndexName), batch);
        }

        /// <summary>
        /// Delete an object from the index.
        /// </summary>
        /// <param name="objectID">the unique identifier of object to delete.</param>
        /// <returns>return an object containing a "deletedAt" attribute.</returns>
        public Task<JObject> DeleteObject(string objectID)
        {
            if (string.IsNullOrWhiteSpace(objectID))
                throw new ArgumentOutOfRangeException("objectID", "objectID is required.");
            return _client.ExecuteRequest("DELETE", string.Format("/1/indexes/{0}/{1}", _urlIndexName, Uri.EscapeDataString(objectID)));
        }

        /// <summary>
        /// Delete several objects.
        /// </summary>
        /// <param name="objects">contains an array of objectIDs to delete.</param>
        public Task<JObject> DeleteObjects(IEnumerable<String> objects)
        {
            List<object> requests = new List<object>();
            foreach (object id in objects)
            {
                Dictionary<string, object> request = new Dictionary<string, object>();
                Dictionary<string, object> obj = new Dictionary<string, object>();
                obj["objectID"] = id;
                request["action"] = "deleteObject";
                request["body"] = obj;
                requests.Add(request);
            }
            Dictionary<string, object> batch = new Dictionary<string, object>();
            batch["requests"] = requests;
            return _client.ExecuteRequest("POST", string.Format("/1/indexes/{0}/batch", _urlIndexName), batch);
        }


        /// <summary>
        /// Search inside the index.
        /// </summary>
        public Task<JObject> Search(Query q)
        {
            string paramsString = q.GetQueryString();
            if (paramsString.Length > 0)
                return _client.ExecuteRequest("GET", string.Format("/1/indexes/{0}?{1}", _urlIndexName, paramsString));
            else
                return _client.ExecuteRequest("GET", string.Format("/1/indexes/{0}", _urlIndexName));

        }

        /// <summary>
        /// Wait the publication of a task on the server. 
        /// All server task are asynchronous and you can check with this method that the task is published.
        /// </summary>
        /// <param name="taskID">the id of the task returned by server.</param>
        async public Task WaitTask(string taskID)
        {
            while (true)
            {
                JObject obj = await _client.ExecuteRequest("GET", string.Format("/1/indexes/{0}/task/{1}", _urlIndexName, taskID));
                string status = (string)obj["status"];
                if (status.Equals("published"))
                    return;
                await TaskEx.Delay(1000);
            }
        }

        /// <summary>
        /// Get settings of this index.
        /// </summary>
        /// <returns>an object containing settings.</returns>
        public Task<JObject> GetSettings()
        {
            return _client.ExecuteRequest("GET", string.Format("/1/indexes/{0}/settings", _urlIndexName));<commit_msg>Fix query with huge parametters<commit_after>﻿/*
 * Copyright (c) 2013 Algolia
 * http://www.algolia.com/
 * Based on the first version developed by <NAME> under the same license:
 *  https://github.com/cmaneu/algoliasearch-client-csharp
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Algolia.Search
{
    /// <summary>
    ///  Contains all the functions related to one index
    /// You should use AlgoliaClient.initIndex(indexName) to instanciate this object.
    /// </summary>
    public class Index
    {
        private AlgoliaClient  _client;
        private string         _indexName;
        private string         _urlIndexName;

        /// <summary>
        /// Index initialization (You should not call this constructor yourself)
        /// </summary>
        public Index(AlgoliaClient client, string indexName)
        {
            _client = client;
            _indexName = indexName;
            _urlIndexName = Uri.EscapeDataString(indexName);
        }

        /// <summary>
        /// Add an object in this index.
        /// </summary>
        /// <param name="content">Represent your object that you want to add in the index.</param>
        /// <param name="objectId">Optional. an objectID you want to attribute to this object (if the attribute already exist the old object will be overwrite)</param>
        /// <returns>An object that contains an "objectID" attribute.</returns>
        public Task<JObject> AddObject(object content, string objectId = null)
        {
            if (string.IsNullOrWhiteSpace(objectId))
            {
                return _client.ExecuteRequest("POST", string.Format("/1/indexes/{0}", _urlIndexName), content);
            }
            else
            {
                return _client.ExecuteRequest("PUT", string.Format("/1/indexes/{0}/{1}", _urlIndexName, Uri.EscapeDataString(objectId)), content);
            }
        }

        /// <summary>
        /// Add several objects.
        /// </summary>
        /// <param name="objects">contains an array of objects to add. If the object contains an objectID.</param>
        /// <returns>return an object containing an "objectIDs" attribute (array of string)</returns>
        public Task<JObject> AddObjects(IEnumerable<object> objects)
        {
            List<object> requests = new List<object>();
            foreach (object obj in objects) {
                Dictionary<string, object> request = new Dictionary<string, object>();
                request["action"] = "addObject";
                request["body"] = obj;
                requests.Add(request);
            }
            Dictionary<string, object> batch = new Dictionary<string, object>();
            batch["requests"] = requests;
            return _client.ExecuteRequest("POST", string.Format("/1/indexes/{0}/batch", _urlIndexName), batch);
        }

        /// <summary>
        /// Get an object from this index
        /// </summary>
        /// <param name="objectID">the unique identifier of the object to retrieve</param>
        /// <param name="attributesToRetrieve"> if set, contains the list of attributes to retrieve</param>
        /// <returns></returns>
        public Task<JObject> GetObject(string objectID, IEnumerable<string> attributesToRetrieve = null)
        {
            if (attributesToRetrieve == null)
            {
                return _client.ExecuteRequest("GET", string.Format("/1/indexes/{0}/{1}", _urlIndexName, Uri.EscapeDataString(objectID)));
            }
            else
            {
                string attributes = "";
                foreach (string attr in attributesToRetrieve)
                {
                    if (attributes.Length > 0)
                        attributes += ",";
                    attributes += Uri.EscapeDataString(attr);
                }
                return _client.ExecuteRequest("GET", string.Format("/1/indexes/{0}/{1}?attributes={2}", _urlIndexName, Uri.EscapeDataString(objectID), attributes));
            }
        }

        /// <summary>
        /// Update partially an object (only update attributes passed in argument).
        /// </summary>
        /// <param name="partialObject">contains the object attributes to override, the object must contains an objectID attribute</param>
        /// <returns>return an object containing an "updatedAt" attribute.</returns>
        public Task<JObject> PartialUpdateObject(JObject partialObject)
        {
            if (partialObject["objectID"] == null)
            {
                throw new AlgoliaException("objectID is missing");
            }
            string objectID = (string)partialObject["objectID"];
            return _client.ExecuteRequest("POST", string.Format("/1/indexes/{0}/{1}/partial", _urlIndexName, Uri.EscapeDataString(objectID)), partialObject);
        }

        /// <summary>
        /// Partially Override the content of several objects
        /// </summary>
        /// <param name="objects">contains an array of objects to update (each object must contains a objectID attribute).</param>
        /// <returns>return an object containing an "objectIDs" attribute (array of string)</returns>
        public Task<JObject> PartialUpdateObjects(IEnumerable<JObject> objects)
        {
            List<object> requests = new List<object>();
            foreach (JObject obj in objects)
            {
                Dictionary<string, object> request = new Dictionary<string, object>();
                request["action"] = "partialUpdateObject";
                if (obj["objectID"] == null)
                {
                    throw new AlgoliaException("objectID is missing");
                }
                request["objectID"] = obj["objectID"];
                request["body"] = obj;
                requests.Add(request);
            }
            Dictionary<string, object> batch = new Dictionary<string, object>();
            batch["requests"] = requests;
            return _client.ExecuteRequest("POST", string.Format("/1/indexes/{0}/batch", _urlIndexName), batch);
        }

        /// <summary>
        /// Override the content of object.
        /// </summary>
        /// <param name="obj">contains the object to save, the object must contains an objectID attribute.</param>
        /// <returns>return an object containing an "updatedAt" attribute.</returns>
        public Task<JObject> SaveObject(JObject obj)
        {
            if (obj["objectID"] == null)
            {
                throw new AlgoliaException("objectID is missing");
            }
            string objectID = (string)obj["objectID"];
            return _client.ExecuteRequest("PUT", string.Format("/1/indexes/{0}/{1}", _urlIndexName, Uri.EscapeDataString(objectID)), obj);
        }

        /// <summary>
        /// Override the content of several objects.
        /// </summary>
        /// <param name="objects">contains an array of objects to update (each object must contains a objectID attribute).</param>
        /// <returns>return an object containing an "objectIDs" attribute (array of string)</returns>
        public Task<JObject> SaveObjects(IEnumerable<JObject> objects)
        {
            List<object> requests = new List<object>();
            foreach (JObject obj in objects)
            {
                Dictionary<string, object> request = new Dictionary<string, object>();
                request["action"] = "updateObject";
                if (obj["objectID"] == null)
                {
                    throw new AlgoliaException("objectID is missing");
                }
                request["objectID"] = obj["objectID"];
                request["body"] = obj;
                requests.Add(request);
            }
            Dictionary<string, object> batch = new Dictionary<string, object>();
            batch["requests"] = requests;
            return _client.ExecuteRequest("POST", string.Format("/1/indexes/{0}/batch", _urlIndexName), batch);
        }

        /// <summary>
        /// Delete an object from the index.
        /// </summary>
        /// <param name="objectID">the unique identifier of object to delete.</param>
        /// <returns>return an object containing a "deletedAt" attribute.</returns>
        public Task<JObject> DeleteObject(string objectID)
        {
            if (string.IsNullOrWhiteSpace(objectID))
                throw new ArgumentOutOfRangeException("objectID", "objectID is required.");
            return _client.ExecuteRequest("DELETE", string.Format("/1/indexes/{0}/{1}", _urlIndexName, Uri.EscapeDataString(objectID)));
        }

        /// <summary>
        /// Delete several objects.
        /// </summary>
        /// <param name="objects">contains an array of objectIDs to delete.</param>
        public Task<JObject> DeleteObjects(IEnumerable<String> objects)
        {
            List<object> requests = new List<object>();
            foreach (object id in objects)
            {
                Dictionary<string, object> request = new Dictionary<string, object>();
                Dictionary<string, object> obj = new Dictionary<string, object>();
                obj["objectID"] = id;
                request["action"] = "deleteObject";
                request["body"] = obj;
                requests.Add(request);
            }
            Dictionary<string, object> batch = new Dictionary<string, object>();
            batch["requests"] = requests;
            return _client.ExecuteRequest("POST", string.Format("/1/indexes/{0}/batch", _urlIndexName), batch);
        }


        /// <summary>
        /// Search inside the index.
        /// </summary>
        public Task<JObject> Search(Query q)
        {
            string paramsString = q.GetQueryString();
            if (paramsString.Length > 0)
            {
                Dictionary<string, object> body = new Dictionary<string, object>();
                body["params"] = paramsString;
                return _client.ExecuteRequest("POST", string.Format("/1/indexes/{0}/query", _urlIndexName), body);
            }
            else
            {
                return _client.ExecuteRequest("GET", string.Format("/1/indexes/{0}", _urlIndexName));
            }
        }

        /// <summary>
        /// Wait the publication of a task on the server. 
        /// All server task are asynchronous and you can check with this method that the task is published.
        /// </summary>
        /// <param name="taskID">the id of the task returned by server.</param>
        async public Task WaitTask(string taskID)
        {
            while (true)
            {
                JObject obj = await _client.ExecuteRequest("GET", string.Format("/1/indexes/{0}/task/{1}", _urlIndexName, taskID));
                string status = (string)obj["status"];
                if (status.Equals("published"))
                    return;
                await TaskEx.Delay(1000);
            }
        }

        /// <summary>
        /// Get settings of this index.
        /// </summary>
        /// <returns>an object containing settings.</returns>
        public Task<JObject> GetSettings()
        {
            return _client.ExecuteRequest("GET", string.Format("/1/indexes/{0}/settings", _urlIndexName));
{'fraction_non_alphanumeric': 0.11706805437895841, 'fraction_numerical': 0.003913025938924523, 'mean_word_length': 2.0944950692797404, 'pattern_counts': {'":': 0, '<': 231, '<?xml version=': 0, '>': 235, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '80707', 'n_tokens_mistral': 6146, 'n_tokens_neox': 5940, 'n_words': 1845}