Select distinct on DocumentDB and C# - c#

I am having a challenge retrieving data from my DocumentDB using C#.
I get the expected results when I run the query below in my Azure DocumentDB data explorer. Ignore the use of contains query, it's a dynamically crafted query
select distinct c.model from c where ( CONTAINS (c.manufacturer, \"BMW\") )
Attempting to run the same query on c# gives me an error
var dblink = UriFactory.CreateDocumentCollectionUri(ConfigurationManager.AppSettings["DocumentDbDatabase"], "Conversation");
var models = _dbclient.CreateDocumentQuery<dynamic>(dblink, "select distinct c.model from c where ( CONTAINS (c.manufacturer, "BMW") )").ToList();
The error I get is incorrect syntax near distinct
Where is the error emanating from?

You may want to upgrade to the latest .Net SDK (v1.22.0).
The syntax error here is from the SDK rather than the query service, given that support for DISTINCT was added recently. Upgrading to the latest SDK should fix the issue.

Related

Azure Cosmos with .NET SDK 3.6 throws Group By is not supported on LINQ query

According to the documentation group by is supported since the .NET SDK 3.3 I've written some testing code in LINQPad to query my local Azure Cosmos Emulator using LINQ, but it's throwing the following error message:
Method 'GroupBy' is not supported., Windows/10.0.18362 cosmos-netstandard-sdk/3.4.2
I'm using the NuGet package Microsoft.Azure.Cosmos version 3.6.0.
My query looks like this:
var query = container.GetItemLinqQueryable<MyObject>()
.GroupBy(a => a.Id)
.ToFeedIterator();
(Please note, I realize the query is non-sensical, it's just by example)
Executing similar SQL in the web interface works just fine:
SELECT
o.id
FROM
MyObjects o
GROUP BY
o.id
If I execute SQL through the C# SDK, that also seems to work fine:
var queryDef = new QueryDefinition("SELECT i.id FROM items i GROUP BY i.id");
var query = container.GetItemQueryIterator<ToDoItem>(queryDef);
var items = new List<ToDoItem>();
while (query.HasMoreResults)
{
items.AddRange(await query.ReadNextAsync());
}
items.Dump();
The linked document describes the GROUP BY support on the SQL Query engine, that is why running a query with GROUP BY on the Portal or in the SDK as text works.
LINQ support is separate and currently not there for GroupBy (it does not translate to the SQL Query text).
I have added an Issue on GitHub that will be used to track progress on it.

CosmosDB Linq query with contains is not of type IDocumentQuery

I have a project working woth CosmosDB. At first I used the preview for EFCore, but it really isn't mature enough so I decided to opt with Cosmonaut instead. I have a linq statement which basically looks if two properties contains a list of substrings - basically I'm trying to do something like:
SELECT * FROM c WHERE CONTAINS(c.Name, ListOfNames) AND CONTAINS(c.Producer, ListOfProducers);
Or a huge ass bunch of:
foreach(var name in nameList) {
foreach(var producer in producerList){
SELECT * FROM c WHERE c.Name == searchedName AND c.Producer == searchedProducer;
}
}
This worked with the EFCore SQL adapter with the following Linq Query:
public async void Search(List<string> producers, List<string> names){
await _store.Entity.Where(x => producers.Any(p => x.Producer.Contains(p)) && names.Any(w => x.Name.Contains(w))).ToListAsync()
}
However, this with the cosmonaut library (which wraps the DocumentDB client from cosmos) gives the following exception:
Input is not of type IDocumentQuery
Looking at the specs for Contains I can do this:
USE AdventureWorks2012;
GO
SELECT Name
FROM Production.Product
WHERE CONTAINS(Name, '"chain*" OR "full*"');
GO
But doing the following in the CosmosDB data explorer yields 0 results:
SELECT * FROM c WHERE CONTAINS(c.Name, '"Test" OR "Test2"')
Whereas a regular contains does:
SELECT * FROM c WHERE CONTAINS(c.Name, "Test")
Maybe my strategy is just wrong. The main reason I want to combine it is to have better performance. My search domain is around 100.000 documents where I have a list of up to a 1000 of producer + names. So basically I want to see if I can find the given producer + name combination in my document list.
Best Regards
First of all you should not mix T-SQL and Cosmos SQL API. Cosmos db has sql-like query syntax but it doesn't support T-SQL(it's for MS SQL).
Secondly, CONTAINS in Cosmos SQL API is a string operator so you cannot use it for arrays.
I think you're looking for IN keyword.
So actually you need next query:
SELECT * FROM c WHERE (c.Name IN("Test", "Test2")) AND (c.Producer IN("Producer1", "Producer2"))
I have not used cosmonaut library but in Microsoft LINQ provider for Document DB your query should look like this:
var data = yourQueryable.Where(x => producers.Contains(x.Producer) && names.Contains(x.Name)).ToList();

OData and Cosmos DB

I'm looking to implement OData v4 for as a querying tool in an ASP.NET Core application I'm working on, and our backing persistence store is Cosmos DB. So far, I haven't figured out a way to make OData queries run against the DocumentQuery IQueryable interface without encountering some sort of exception or error.
I was wanting to know if there was a "clean" way to utilize OData against Cosmos Document DB (not the Table API), and if so, how? So far, all I've found is an unofficial library that's using Framework v4.6, but nothing official, and all documentation I've found about implementing OData has almost exclusively been ran against Entity Framework or in-memeory data store.
I know this isn't exactly the most insightful answer to my question, but the correct answer here is really to just not attempt to do this. If you're ever in a position where someone asks you to try to force together two technologies that don't really go together, say no and deal with the consequences.
OData integration with Cosmos is not that hard if you're using the SQL api and you only need the basic stuff like $orderby, $top and $skip. It's a matter of generating correct SQL.
If you need more than that it gets a bit harder. Anyway, I did some simple testning with this NuGet lib. It seems to work from my simple tests at least.
var oDataToSqlTranslator = new ODataToSqlTranslator(new SQLQueryFormatter());
var select = oDataToSqlTranslator.Translate(odataQueryOptions, TranslateOptions.SELECT_CLAUSE);
var where = oDataToSqlTranslator.Translate(odataQueryOptions, TranslateOptions.WHERE_CLAUSE);
var order = oDataToSqlTranslator.Translate(odataQueryOptions, TranslateOptions.ORDERBY_CLAUSE);
var top = oDataToSqlTranslator.Translate(odataQueryOptions, TranslateOptions.TOP_CLAUSE);
var all = oDataToSqlTranslator.Translate(odataQueryOptions, TranslateOptions.ALL);
log.LogInformation("SQL select => " + select);
log.LogInformation("SQL where => " + where);
log.LogInformation("SQL order => " + order);
log.LogInformation("SQL all => " + all);
Given this URL as input:
http://localhost:7071/api/v1/invoices/customer/20?$top=2&$select=CustomerId&$filter=InvoiceNumber eq 'xxx'&orderby=brand
The logs shows this:
SQL select => SELECT c.CustomerId FROM c
SQL where => WHERE c.InvoiceNumber = 'xxx'
SQL order => ORDER BY c.InvoiceNumber ASC
SQL all => SELECT TOP 2 c.CustomerId FROM c WHERE c.InvoiceNumber = 'xxx' ORDER BY c.InvoiceNumber ASC
How about something like - convert OData to SQL, and then execute it on the CosmosDB
https://github.com/Azure/azure-odata-sql-js

SQLite & LinQ Take function

I am using SQLite (install through Nuget Package Manager). I had been trying
to use Linq for Paging.
The error that I got: SQL logic errornear ".": syntax error'
The Sql query code at "tQuery" that I observed during debug is
{SELECT TOP (2) [t0].[R_IDs]......FROM [TTable].
The Top Keyword is not Supported in Sqllite, correct? It is suppose to be Limit and is at the end of of the Query Statement.
I had been searching around for answer.
Any one ever had this problem?
Table <T_Table> MyT = db.GetTable<T_Table>();
var tQuery = (from my_t in MyT
select my_t).Take(5);

Lightswitch - C# LINQ Preprocess Query

I'm pretty new to LINQ and I'm having trouble restricting the number of values (strings in "yyyy-mm" format) from my "Availabilities" entity which are shown on a Lightswitch screen, based on a Query ("LINQ_Query"). The idea is to show only "Availabilities" which are recent - for example where int.Parse(av.Availability.Substring(0,4)) > 2013
The reason I'm doing this is that "Availability" entity contains strings and not all of them are in "yyyy-MM" format - there are also entries like "Available", "Delayed", "2014" etc. I want to filter only the ones which have a length of 7 and ideally are more recent than 2013-12 and not in the future (Availability > current date).
In SQL I have achieved this with the following code:
SELECT Availability FROM Availabilities where LEN(Availability)=7 and LEFT(Availability,4) > 2013 and availability<=CONVERT(varchar(7),GETDATE(),126) order by Availability desc
The C# code I use for my PreprocessQuery is the following and it compiles with no errors:
partial void LINQ_Query_PreprocessQuery(ref IQueryable<Availabilities> query)
{
query = from av in query
where av.Availability.Length==7 && int.Parse(av.Availability.Substring(0,4)) > 2013
select av;
}
However, when I deploy and test there are the dreaded red Xs on the screen in question. I enabled tracing and I see the error is:
[Microsoft.LightSwitch.DataService][Application:Error][LightSwitchApplication.ApplicationData:LINQ_Query] An exception has occurred: Microsoft.LightSwitch.DataServiceOperationException: LINQ to Entities does not recognize the method 'Int32 Parse(System.String)' method, and this method cannot be translated into a store expression.
Is there a workaround for this? I was thinking of executing a separate "IDataServiceQueryable" query and then pointing the Preprocess IQueryable query to the results of the "IDataServiceQueryable" one. Is there any chance this or another type of query would work with int.Parse or DataTime.Parse/ParseExact?
Thanks in advance!
P.S. Some more details about my setup: I'm testing a Web Lightswitch 2011 application, hosted on my PC as IIS Server. The Database is hosted on a SQL Server 2008 Express x64 SP3 also on my PC, which leads me to anothe question - since LINQ to Entities doesn't support the functions I need, can I create a LINQ to SQL query on the Availabilities table?
Try splitting them up and removing the cast
partial void LINQ_Query_PreprocessQuery(ref IQueryable<Availabilities> query)
{
query = query.Where(x=>x.Availability.Length==7);
query = query.Where(x=>x.Availability.Substring(0,4) == "2013";
}
Ctype or Parse in Linq could be trouble (if it would fail for one, it fails for all)
If it works, join the 2 queries again.

Categories

Resources