RavenDB: Can't retrieve individual Ranged Facets - ravendb

I have some Ranged Facets defined in a FacetSetup document. I like having the ability to retrieve individual Facets from a FacetSetup (by specifying them instead of the Id of the FacetSetup in my call to ToFacets()), so I tried to do that with these Ranged Facets but have been unsuccessful so far.
Here is my failing test. Any tips?
using Raven.Abstractions.Data;
using Raven.Abstractions.Indexing;
using Raven.Client;
using Raven.Client.Embedded;
using Raven.Tests.Helpers;
using System.Collections.Generic;
using System.Linq;
using Xunit;
namespace RavenDBTests
{
public class RangedFacetTests : RavenTestBase
{
[Fact]
public void RangedFacetTest()
{
using (EmbeddableDocumentStore documentStore = NewDocumentStore())
{
// create and store an index
Dictionary<string, string> analyzers = new Dictionary<string, string>();
analyzers.Add("MyProperty", "");
Dictionary<string, SortOptions> sortOptions = new Dictionary<string, SortOptions>();
sortOptions.Add("MyProperty", SortOptions.Long);
IndexDefinition indexDefinition = new IndexDefinition()
{
Analyzers = analyzers,
SortOptions = sortOptions,
Map = #"from d in docs
select new
{
MyProperty = d.MyProperty
}",
};
documentStore.DatabaseCommands.PutIndex("MyIndex", indexDefinition);
using (IDocumentSession documentSession = documentStore.OpenSession())
{
// store some sample documents
documentSession.Store(new { MyProperty = 10 });
documentSession.Store(new { MyProperty = 25 });
documentSession.Store(new { MyProperty = 100 });
// store a facetsetup with one ranged facet
documentSession.Store(new FacetSetup
{
Id = "facets/MyFacetSetup",
Facets = new List<Facet>()
{
new Facet()
{
Mode = FacetMode.Ranges,
Name = "MyProperty_Range",
Ranges = new List<string>()
{
"[0x0000000000000001 TO 0x0000000000000032]"
}
}
}
}, "facets/MyFacetSetup");
documentSession.SaveChanges();
}
// let that process
WaitForIndexing(documentStore);
using (IDocumentSession documentSession = documentStore.OpenSession())
{
// retrieve ALL facets
FacetResults facetResults1 = documentSession.Query<dynamic>("MyIndex").ToFacets("facets/MyFacetSetup");
Xunit.Assert.True(facetResults1.Results.Values.First().Values.First().Hits > 0);
// retrieve SPECIFIED facets
FacetResults facetResults2 = documentSession.Query<dynamic>("MyIndex").ToFacets(new List<Facet>()
{
new Facet()
{
Mode = FacetMode.Ranges,
Name = "MyProperty_Range"
}
},
0,
null);
// this fails: why can't I specify the ranged facet?
Xunit.Assert.True(facetResults2.Results.Values.First().Values.First().Hits > 0);
}
}
}
}
}

You don't specify what the actual ranges are in the code.
In RavenDB, you have two ways to create facets. One is to specify the facet doc id, and the second is to actually pass the facets.
In this case, you are passing a range facets without any ranges, so it returns no results.
Use this code:
FacetResults facetResults2 = documentSession.Query<dynamic>("MyIndex").ToFacets(new List<Facet>()
{
new Facet()
{
Mode = FacetMode.Ranges,
Name = "MyProperty_Range",
Ranges = new List<string>()
{
"[0x0000000000000001 TO 0x0000000000000032]"
}
}
},
0,
null);

Related

Why are my swagger docs showing 'additionalProperties = false' for my custom schema filter?

I have this SchemaFilter in my swagger config
public class SmartEnumSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (!TryGetSmartEnumValues(context.Type, out var values))
{
return;
}
var openApiInts = new OpenApiArray();
openApiInts.AddRange(values.Select(x => new OpenApiInteger(x.Value)));
schema.Type = "integer";
schema.Enum = openApiInts;
schema.Properties = null;
schema.Description = string.Join(", ", values.Select(v => $"{v.Value}: {v.Name}"));
}
}
It is working well, but for some reason this "additional properties" always appears in the docs:
But only for this particular schema - all the other schemas don't have it. Is there some way of removing it?
I tried setting both
schema.AdditionalProperties = null;
schema.AdditionalPropertiesAllowed = false;
but it made no difference

How to convert camel case format my data?

I am executiong sql queries from different data sources. And I am getting the results as dynamic objects like following.
private IEnumerable<IDictionary<string, object>> GetDataFromPostgresql()
{
var dataList = new List<IDictionary<string, object>>();
IDictionary<string, object> obj = new ExpandoObject();
obj.Add("first_name", "john");
obj.Add("last_name", "smith");
obj.Add("age", 25);
dataList.Add(new Dictionary<string, object>(obj));
return dataList;
}
private IEnumerable<IDictionary<string, object>> GetDataFromOracle()
{
var dataList = new List<IDictionary<string, object>>();
IDictionary<string, object> obj = new ExpandoObject();
obj.Add("FIRST_NAME", "john");
obj.Add("LAST_NAME", "smith");
obj.Add("AGE", 25);
dataList.Add(new Dictionary<string, object>(obj));
return dataList;
}
private IEnumerable<IDictionary<string, object>> GetDataFromMssql()
{
var dataList = new List<IDictionary<string, object>>();
IDictionary<string, object> obj = new ExpandoObject();
obj.Add("FirstName", "john");
obj.Add("LastName", "smith");
obj.Add("Age", 25);
dataList.Add(new Dictionary<string, object>(obj));
return dataList;
}
I am using these data collections in my asp.net core controller.
[HttpGet]
[Route("")]
public IActionResult Get()
{
var dataList = GetDataFromPostgresql();
return Ok(dataList);
}
But I want to serialize responses a standart camel case format.
{ "firstName": "john", "lastName": "smith", "age": 25 }
So how can I do this?
PascalCase means it has a capitalized first letter, but if this kind of outcome that you are after, { "firstName": "john", "lastName": "smith", "age": 25 }, that's called camelCase.
I am not familiar with nowadays asp.net-core, but I am sure the RegExp would be the same as this JavaScript one.
function toUpperCase(raw, letter) {
return letter.toUpperCase();
}
function asCamelCase(str) {
// put an underscore between camel cases
return str.replace(/([a-z])([A-Z])/g, '$1_$2')
// make the whole string lower case
.toLowerCase()
// capitalize every letter after the underscore
.replace(/_+([^_]|$)/g, toUpperCase);
}
Above example would produce (try by copying and pasting it in your browser console) always firstName, given the following inputs:
asCamelCase("FirstName"); // firstName
asCamelCase("FIRST_NAME"); // firstName
asCamelCase("first_name"); // firstName
I hope this helped 👋
Try to use below code to change the key in Dictionary to CamelCase
[HttpGet]
[Route("")]
public IActionResult Get()
{
var dataList = GetDataFromPostgresql();
var newDataList = new List<IDictionary<string, object>>();
foreach (var data in dataList)
{
var newData = new Dictionary<string, object>();
foreach (var key in data.Keys)
{
var newKey = "";
var value = data[key];
var index = key.IndexOf('_');
if(index < 0)//for GetDataFromMssql()
{
newKey = Char.ToLowerInvariant(key[0]) + key.Substring(1);
}else
{
newKey = key.ToLowerInvariant().Replace("_", string.Empty).Replace(" ", string.Empty);
newKey = newKey.Replace(newKey[index], Char.ToUpperInvariant(newKey[index]));
}
newData.Add(newKey, value);
}
newDataList.Add(newData);
}
return Ok(newDataList);
}
If you have complex value like "first_name_hello_test", you could use below workaround:
[HttpGet]
[Route("")]
public IActionResult Get()
{
var dataList = GetDataFromPostgresql();
var newDataList = new List<IDictionary<string, object>>();
foreach (var data in dataList)
{
var newData = new Dictionary<string, object>();
foreach (var key in data.Keys)
{
var newKey = "";
var value = data[key];
var indexList = new List<int>();
var index = key.IndexOf('_');
if (index < 0)
{
newKey = Char.ToLowerInvariant(key[0]) + key.Substring(1);
}else
{
while (index >= 0)
{
indexList.Add(index);
index = key.IndexOf('_', index + 1);
}
newKey = key.ToLowerInvariant();
foreach(var i in indexList)
{
newKey = newKey.Replace(newKey[i+1], Char.ToUpperInvariant(newKey[i+1]));
}
newKey = newKey.Replace("_", string.Empty).Replace(" ", string.Empty);
}
newData.Add(newKey, value);
}
newDataList.Add(newData);
}
return Ok(newDataList);
}

How to use Scroll while passing raw json Query to ElasticSearch using NEST

var query = #"
{
""query"": {
""match_all"": { }
}
}";
Func<SearchRequestParameters, SearchRequestParameters> requestParameters = a =>
a.SearchType(SearchType.Scan).Scroll(TimeSpan.FromSeconds(60));
var searchResult = await client.LowLevel.SearchAsync<SearchResponse<T>>(indexName, mappingName, query , requestParameters)
if (searchResult.Body.IsValid)
{
var scrollNo = 0;
var results = await client.ScrollAsync<T>("10s", searchResult.Body.ScrollId);
while (results.Documents.Any())
{
documents.AddRange(results.Documents);
scrollNo++;
results = await client.ScrollAsync<T>("10s", results.ScrollId);
return new Customresponse<T>
{
Documents = documents,
total = result.Body.Total
};
}
Would like to pull all data using scroll while passing raw json query. but scroll is not working properly while passing json raw query. Can anyone help on this ?.
Your example is nearly there but not quite; you're missing a closing brace for the while loop to collect all documents before returning the custom response.
Here's an example I just ran on the Stackoverflow data set, to return all questions tagged with nest
private IElasticClient _client;
void Main()
{
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var defaultIndex = "default-index";
var connectionSettings = new ConnectionSettings(pool);
_client = new ElasticClient(connectionSettings);
var query = #"
{
""query"": {
""term"": {
""tags"": {
""value"": ""nest""
}
}
}
}";
var result = RunScrollAsync(query).Result.Dump();
}
private async Task<Customresponse<Question>> RunScrollAsync(string query)
{
var scrollTime = "10s";
// omit the .SearchType(Scan) which is deprecated. Not
// specifying means the first response contains the first set
// of documents
var esResponse = await _client.LowLevel.SearchAsync<SearchResponse<Question>>(
"posts",
"question",
query, r => r.Scroll(TimeSpan.FromSeconds(10))).ConfigureAwait(false);
if (esResponse.Body.IsValid && esResponse.Body.Documents.Any())
{
// assume you have less than 2,147,483,647 documents to return?
var documents = new List<Question>((int)esResponse.Body.Total);
documents.AddRange(esResponse.Body.Documents);
var scrollNo = 0;
var response = await _client.ScrollAsync<Question>(scrollTime, esResponse.Body.ScrollId).ConfigureAwait(false);;
// keep scrolling until no more documents are returned
while (response.Documents.Any())
{
documents.AddRange(response.Documents);
scrollNo++;
response = await _client.ScrollAsync<Question>(scrollTime, response.ScrollId).ConfigureAwait(false);;
}
return new Customresponse<Question>
{
Documents = documents,
total = response.Total
};
}
// return an empty result.
// Or throw an exception, or log - whatever you need to do
return new Customresponse<Question>
{
Documents = Enumerable.Empty<Question>(),
total = 0
};
}
public class Customresponse<T>
{
public IEnumerable<T> Documents { get; set; }
public long total { get; set; }
}
This returns all 342 questions, with a total of 342 (Data set is from June 2016).

Passing list of object to Web API using RestSharp Client

I'm trying to send list of objects from MVC to WEBAPI using below methods. API is able to able receive the list from controller but, value of each item in the list is either empty/null on API side.
Can anyone please help me to fix this?
Controller Method:
private List<FCM.Models.Facility> GetFacilityDetails()
{
var url = "http://localhost:64664/";
var facilies = new List<Facility>();
facilies.Add( new Facility{ FCLT_ID = 100, FCLT_NM = "Facility 100" });
facilies.Add( new Facility{ FCLT_ID = 200, FCLT_NM = "Facility 200" });
facilies.Add( new Facility{ FCLT_ID = 300, FCLT_NM = "Facility 300" });
var json = JsonConvert.SerializeObject(facilies);
var _client = new RestClient(url);
var request = new RestRequest("api/facility/details", Method.GET) { RequestFormat = DataFormat.Json };
facilies.ForEach(fclt =>
request.AddParameter("facilites", fclt, ParameterType.GetOrPost));
var response = _client.Execute<List<FCM.Models.Facility>>(request);
if (response.Data == null)
{
throw new Exception(response.ErrorMessage);
}
return response.Data;
}
WebAPI method:
[Route("api/facility/details")]
public IEnumerable<Facility> GetFullAddress([FromUri] IEnumerable<Facility> facilities)
{
return null;
}
Like the comment suggested you maybe want to issue a POST request instead, but if you would like to send an array with a GETrequest you could do it like this (with System.Net.Http.HttpClient):
Add a Format method to you Facility class:
public class Facility
{
public int FCLT_ID { get; set; }
public string FCLT_NM { get; set; }
public string Format(int index)
{
return $"[{index}].FCLT_ID={FCLT_ID}&[{index}].FCLT_NM={FCLT_NM}";
}
}
Define a class which can format the array values:
public class FacilityList : List<Facility>
{
public string Format()
{
var builder = new StringBuilder();
for (var i = 0; i < Count; i++)
{
builder.Append(this[i].Format(i));
if(i != Count -1)
{
builder.Append("&");
}
}
return builder.ToString();
}
}
And then issue the request:
var client = new HttpClient()
{
BaseAddress = new Uri("http://localhost:64664/"),
DefaultRequestHeaders = {Accept = {new MediaTypeWithQualityHeaderValue("application/json")}}
};
var facilities = new FacilityList
{
new Facility {FCLT_ID = 100, FCLT_NM = "Facility 100"},
new Facility {FCLT_ID = 200, FCLT_NM = "Facility 200"},
new Facility {FCLT_ID = 300, FCLT_NM = "Facility 300"}
};
var format = facilities.Format();
var response = client.GetAsync("api/facility/details?" + format).GetAwaiter().GetResult();
var result = JsonConvert.DeserializeObject<IEnumerable<Facility>>(response.Content.ReadAsStringAsync().GetAwaiter().GetResult());
This will bind to your controller action:
[Route("api/facility/details")]
public IHttpActionResult Get([FromUri] IEnumerable<Facility> facilities)
{
// Do stuff..
return Ok(facilities);
}

RavenDB StartsWith Variable on left-hand-side of Query

I have a collection of docs with a Url property that looks like:
{ Url: www.test.com }
{ Url: www.test.com/abc }
{ Url: www.test.com/abc/xyz }
I want to query the docs with www.test.com/abc such that I am returned all the documents that match the url on everything up-to-and-including the query url. i.e. the results would return:
{ Url: www.test.com }
{ Url: www.test.com/abc }
Below is the query I have written to accomplish this. As you can see, it relies on the 'Starts With' to be performed on the queryTerm, as opposed to being performed on a property of the document (the left-hand-side of the query).
var queryTerm = "www.test.com/abc";
pages = session.Query<MyDocument>()
.Where(x => queryTerm.StartsWith(x.Url));
This does not work in Raven; I get an 'Expression type not supported' error. Any ideas?
Raven doesn't appear to support that in its expression. Interestingly, the query x => queryTerm.StartsWith(x.Url) can be exploded into something along the lines of:
x => x.Url == queryTerm ||
x.Url == queryTerm.Remove(queryTerm.Length - 1) ||
x.Url == queryTerm.Remove(queryTerm.Length - 2) ||
x.Url == queryTerm.Remove(queryTerm.Length - 3) ||
x.Url == queryTerm.Remove(queryTerm.Length - 4) ||
etc.
Which can be codified as:
var terms = Enumerable.Range(0, queryTerm.Length)
.Skip(1)
.Select(i => queryTerm.Remove(i));
pages = session.Query<MyDocument>()
.Where(x => terms.Contains(x.Url));
Except Raven doesn't supports Contains() like that either. So I coded this up:
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Raven.Client;
using Raven.Client.Embedded;
using Raven.Client.Linq;
namespace RavenTest
{
[TestFixture]
public class HowToDoBackwardsStartsWithInRaven
{
private readonly string[] _testUrls = new[]
{
"www.bad.com",
"www.test.com",
"www.test.com/abc",
"www.test.com/abc/xyz"
};
private readonly string _queryTerm = "www.test.com/abc";
private readonly string[] _expectedResults = new[]
{
"www.test.com",
"www.test.com/abc"
};
[Test]
public void FailsWithContains()
{
List<MyDocument> results;
using (var store = InitStore())
{
LoadTestData(store);
using (var session = store.OpenSession())
{
var terms = GetAllStartingSubstrings(_queryTerm);
results = session.Query<MyDocument>()
.Where(x => terms.Contains(x.Url))
.ToList();
}
}
Assert.That(results.Select(p => p.Url), Is.EquivalentTo(_expectedResults));
}
[Test]
public void SucceedsWithSuperWhere()
{
List<MyDocument> results;
using (var store = InitStore())
{
LoadTestData(store);
using (var session = store.OpenSession())
{
var terms = GetAllStartingSubstrings(_queryTerm);
var query = session.Query<MyDocument>()
.Where(x => x.Url.Length <= _queryTerm.Length);
foreach (var term in terms)
query = query.Where(x => x.Url == term ||
x.Url.Length != term.Length);
results = query.ToList();
}
}
Assert.That(results.Select(p => p.Url), Is.EquivalentTo(_expectedResults));
}
private static IDocumentStore InitStore()
{
var store = new EmbeddableDocumentStore
{
RunInMemory = true,
};
return store.Initialize();
}
private static string[] GetAllStartingSubstrings(string queryTerm)
{
return Enumerable.Range(0, queryTerm.Length)
.Skip(1)
.Select(i => queryTerm.Remove(i))
.ToArray();
}
private void LoadTestData(IDocumentStore store)
{
using (var session = store.OpenSession())
{
foreach (var testUrl in _testUrls)
session.Store(new MyDocument {Url = testUrl});
session.SaveChanges();
}
}
public class MyDocument
{
public string Id { get; set; }
public string Url { get; set; }
}
}
}
Now... I have no idea how efficient the indexing will be on that but the test does pass.