Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

updated to be compatible with OData 6.0.0 #2

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -250,3 +250,4 @@ paket-files/
# JetBrains Rider
.idea/
*.sln.iml
/azure-documentdb-odata-sql/nuget.exe
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ The above query will then be translated to DocumentDB SQL:
```
SELECT TOP 5 c.revenue FROM c WHERE CONTAINS(c.englishName,'Limited') ORDER BY c.countryCode DESC
```
Note: requires Microsoft.AspNet.OData 6.1.0.0 and .NET Framework 4.62

### Supported OData to DocumentDB SQL mappings:

Expand All @@ -32,7 +33,17 @@ SELECT TOP 5 c.revenue FROM c WHERE CONTAINS(c.englishName,'Limited') ORDER BY c

[$orderby](http://docs.oasis-open.org/odata/odata/v4.0/errata03/os/complete/part1-protocol/odata-v4.0-errata03-os-part1-protocol-complete.html#_The_$select_System_1) => ORDER BY

[$count](http://docs.oasis-open.org/odata/odata/v4.0/errata03/os/complete/part1-protocol/odata-v4.0-errata03-os-part1-protocol-complete.html#_The_$inlinecount_System) => COUNT(1)

### Built-in Operators
Items/any(d:d/Quantity gt 100) => JOIN a in c.Items WHERE a.Quantity > 100
Note: If more objects in 'Items' qualify for the expression, duplicate results may result. e.g.
SELECT value c FROM c
JOIN a IN c.sub
WHERE a.v=false might return c twice, while c exists once, because the join in 'sub' has two hits

#### Built-in Query Functions

contains()(field, 'value') => CONTAINS(c.field, 'value')

startswith()(field, 'value') => STARTSWITH(c.field, 'value')
Expand All @@ -53,6 +64,10 @@ trim(field) => LTRIM(RTRIM(c.englishName))

concat(field,'value') => CONCAT(c.englishName,'value')

geo.distance(field, geography'POINT(30 10)') => ST_DISTANCE(c.location,{"type":"Point","coordinates":[30,10]})

geo.intersects(field, geography'POLYGON((30 10, 10 20, 20 40, 40 40, 30 10))') => ST_INTERSECTS(c.area,{"type":"Polygon","coordinates":[[[30,10],[10.20],[20,40],[40,40],[30,10]]]})

## Installing

The nuget package of this project is published on Nuget.org [Download Page](https://www.nuget.org/packages/Microsoft.Azure.Documents.OData.Sql/). To install in Visual Studio Package Manager Console, run command:
Expand Down Expand Up @@ -93,3 +108,5 @@ The options can be combined with bit operators such as ```(TranslateOptions.SELE
## Authors

* **Ziyou Zheng** - Microsoft Universal Store Team -
* **Egbert Nierop** - Free Lance developer - Added any functionality 2017 oct 13. note: all-functionality not supported.
* **ntanaka** - Added $count, geography'POINT and geography'POLYGON( translations
101 changes: 85 additions & 16 deletions azure-documentdb-odata-sql-samples/ODataToSqlSamples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

using Microsoft.Azure.Documents.OData.Sql;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Web.OData.Extensions;

namespace azure_documentdb_odata_sql_tests
{
Expand Down Expand Up @@ -40,8 +41,13 @@ public static void ClassInitialize(TestContext testContext)
[TestInitialize()]
public void TestInitialize()
{
httpRequestMessage = new HttpRequestMessage();
httpRequestMessage.Method = HttpMethod.Get;
httpRequestMessage = new HttpRequestMessage
{
Method = HttpMethod.Get
};
var config = new System.Web.Http.HttpConfiguration();
config.EnableDependencyInjection();
httpRequestMessage.SetConfiguration(config);
}

[TestMethod]
Expand Down Expand Up @@ -76,7 +82,26 @@ public void TranslateSelectWithEnumSample()
var sqlQuery = oDataToSqlTranslator.Translate(oDataQueryOptions, TranslateOptions.SELECT_CLAUSE);
Assert.AreEqual("SELECT c.enumNumber, c.id FROM c ", sqlQuery);
}
[TestMethod]
public void TranslateAnySample()
{
httpRequestMessage.RequestUri = new Uri("http://localhost/User?$filter=companies/any(p: p/id eq 'abc' or p/name eq 'blaat')");
var oDataQueryOptions = new ODataQueryOptions(oDataQueryContext, httpRequestMessage);

var oDataToSqlTranslator = new ODataToSqlTranslator(new SQLQueryFormatter());
var sqlQuery = oDataToSqlTranslator.Translate(oDataQueryOptions, TranslateOptions.SELECT_CLAUSE | TranslateOptions.WHERE_CLAUSE);
Assert.AreEqual("SELECT VALUE c FROM c JOIN a IN c.companies WHERE a.id = 'abc' OR a.name = 'blaat'", sqlQuery);
}
[TestMethod]
public void TranslateAnySampleWithMultipleClauses()
{
httpRequestMessage.RequestUri = new Uri("http://localhost/User?$filter=(companies/any(p: p/id eq 'abc' or p/name eq 'blaat')) and customers/any(x: x/customer_name eq 'jaap')");
var oDataQueryOptions = new ODataQueryOptions(oDataQueryContext, httpRequestMessage);

var oDataToSqlTranslator = new ODataToSqlTranslator(new SQLQueryFormatter());
var sqlQuery = oDataToSqlTranslator.Translate(oDataQueryOptions, TranslateOptions.SELECT_CLAUSE | TranslateOptions.WHERE_CLAUSE);
Assert.AreEqual("SELECT VALUE c FROM c JOIN a IN c.companies JOIN b IN c.customers WHERE a.id = 'abc' OR a.name = 'blaat' AND b.customer_name = 'jaap'", sqlQuery);
}
[TestMethod]
public void TranslateSelectAllTopSample()
{
Expand Down Expand Up @@ -107,7 +132,18 @@ public void TranslateWhereSample()

var oDataToSqlTranslator = new ODataToSqlTranslator(new SQLQueryFormatter());
var sqlQuery = oDataToSqlTranslator.Translate(oDataQueryOptions, TranslateOptions.WHERE_CLAUSE);
Assert.AreEqual("WHERE c.englishName = 'Microsoft' AND c.intField <= 5 ", sqlQuery);
Assert.AreEqual("WHERE c.englishName = 'Microsoft' AND c.intField <= 5", sqlQuery);
}

[TestMethod]
public void TranslateWhereSampleWithGUID()
{
httpRequestMessage.RequestUri = new Uri("http://localhost?$filter=id eq 2ED27DF5-F505-4A06-B168-7321C6B4AD0C");
var oDataQueryOptions = new ODataQueryOptions(oDataQueryContext, httpRequestMessage);

var oDataToSqlTranslator = new ODataToSqlTranslator(new SQLQueryFormatter());
var sqlQuery = oDataToSqlTranslator.Translate(oDataQueryOptions, TranslateOptions.WHERE_CLAUSE);
Assert.AreEqual("WHERE c.id = '2ed27df5-f505-4a06-b168-7321c6b4ad0c'", sqlQuery);
}

[TestMethod]
Expand All @@ -118,7 +154,7 @@ public void TranslateWhereWithEnumSample()

var oDataToSqlTranslator = new ODataToSqlTranslator(new SQLQueryFormatter());
var sqlQuery = oDataToSqlTranslator.Translate(oDataQueryOptions, TranslateOptions.WHERE_CLAUSE);
Assert.AreEqual("WHERE c.enumNumber = 'ONE' AND c.intField <= 5 ", sqlQuery);
Assert.AreEqual("WHERE c.enumNumber = 'ONE' AND c.intField <= 5", sqlQuery);
}

[TestMethod]
Expand All @@ -129,7 +165,7 @@ public void TranslateWhereWithNextedFieldsSample()

var oDataToSqlTranslator = new ODataToSqlTranslator(new SQLQueryFormatter());
var sqlQuery = oDataToSqlTranslator.Translate(oDataQueryOptions, TranslateOptions.WHERE_CLAUSE);
Assert.AreEqual("WHERE c.parent.child = 'childValue' AND c.intField <= 5 ", sqlQuery);
Assert.AreEqual("WHERE c.parent.child = 'childValue' AND c.intField <= 5", sqlQuery);
}

[TestMethod]
Expand All @@ -140,7 +176,7 @@ public void TranslateAdditionalWhereSample()

var oDataToSqlTranslator = new ODataToSqlTranslator(new SQLQueryFormatter());
var sqlQuery = oDataToSqlTranslator.Translate(oDataQueryOptions, TranslateOptions.WHERE_CLAUSE, "c.dataType = 'MockOpenType'");
Assert.AreEqual("WHERE c.dataType = 'MockOpenType' AND c.englishName = 'Microsoft' AND c.intField <= 5 ", sqlQuery);
Assert.AreEqual("WHERE c.dataType = 'MockOpenType' AND c.englishName = 'Microsoft' AND c.intField <= 5", sqlQuery);
}

[TestMethod]
Expand All @@ -151,7 +187,7 @@ public void TranslateSelectWhereSample()

var oDataToSqlTranslator = new ODataToSqlTranslator(new SQLQueryFormatter());
var sqlQuery = oDataToSqlTranslator.Translate(oDataQueryOptions, TranslateOptions.SELECT_CLAUSE | TranslateOptions.WHERE_CLAUSE, "c.dataType = 'MockOpenType'");
Assert.AreEqual("SELECT * FROM c WHERE c.dataType = 'MockOpenType' AND c.englishName = 'Microsoft' ", sqlQuery);
Assert.AreEqual("SELECT * FROM c WHERE c.dataType = 'MockOpenType' AND c.englishName = 'Microsoft'", sqlQuery);
}

[TestMethod]
Expand Down Expand Up @@ -184,7 +220,7 @@ public void TranslateContainsSample()

var oDataToSqlTranslator = new ODataToSqlTranslator(new SQLQueryFormatter());
var sqlQuery = oDataToSqlTranslator.Translate(oDataQueryOptions, TranslateOptions.ALL & ~TranslateOptions.TOP_CLAUSE);
Assert.AreEqual("SELECT * FROM c WHERE CONTAINS(c.englishName,'Microsoft') ", sqlQuery);
Assert.AreEqual("SELECT * FROM c WHERE CONTAINS(c.englishName,'Microsoft')", sqlQuery);
}

[TestMethod]
Expand All @@ -195,7 +231,7 @@ public void TranslateStartswithSample()

var oDataToSqlTranslator = new ODataToSqlTranslator(new SQLQueryFormatter());
var sqlQuery = oDataToSqlTranslator.Translate(oDataQueryOptions, TranslateOptions.ALL & ~TranslateOptions.TOP_CLAUSE);
Assert.AreEqual("SELECT * FROM c WHERE STARTSWITH(c.englishName,'Microsoft') ", sqlQuery);
Assert.AreEqual("SELECT * FROM c WHERE STARTSWITH(c.englishName,'Microsoft')", sqlQuery);
}

[TestMethod]
Expand All @@ -206,7 +242,7 @@ public void TranslateEndswithSample()

var oDataToSqlTranslator = new ODataToSqlTranslator(new SQLQueryFormatter());
var sqlQuery = oDataToSqlTranslator.Translate(oDataQueryOptions, TranslateOptions.ALL & ~TranslateOptions.TOP_CLAUSE);
Assert.AreEqual("SELECT * FROM c WHERE ENDSWITH(c.englishName,'Microsoft') ", sqlQuery);
Assert.AreEqual("SELECT * FROM c WHERE ENDSWITH(c.englishName,'Microsoft')", sqlQuery);
}

[TestMethod]
Expand All @@ -217,7 +253,7 @@ public void TranslateUpperAndLowerSample()

var oDataToSqlTranslator = new ODataToSqlTranslator(new SQLQueryFormatter());
var sqlQuery = oDataToSqlTranslator.Translate(oDataQueryOptions, TranslateOptions.ALL & ~TranslateOptions.TOP_CLAUSE);
Assert.AreEqual("SELECT * FROM c WHERE UPPER(c.englishName) = 'MICROSOFT' OR LOWER(c.englishName) = 'microsoft' ", sqlQuery);
Assert.AreEqual("SELECT * FROM c WHERE UPPER(c.englishName) = 'MICROSOFT' OR LOWER(c.englishName) = 'microsoft'", sqlQuery);
}

[TestMethod]
Expand All @@ -228,7 +264,7 @@ public void TranslateLengthSample()

var oDataToSqlTranslator = new ODataToSqlTranslator(new SQLQueryFormatter());
var sqlQuery = oDataToSqlTranslator.Translate(oDataQueryOptions, TranslateOptions.ALL & ~TranslateOptions.TOP_CLAUSE);
Assert.AreEqual("SELECT * FROM c WHERE LENGTH(c.englishName) >= 10 AND LENGTH(c.englishName) < 15 ", sqlQuery);
Assert.AreEqual("SELECT * FROM c WHERE LENGTH(c.englishName) >= 10 AND LENGTH(c.englishName) < 15", sqlQuery);
}

[TestMethod]
Expand All @@ -239,7 +275,7 @@ public void TranslateIndexOfSample()

var oDataToSqlTranslator = new ODataToSqlTranslator(new SQLQueryFormatter());
var sqlQuery = oDataToSqlTranslator.Translate(oDataQueryOptions, TranslateOptions.ALL & ~TranslateOptions.TOP_CLAUSE);
Assert.AreEqual("SELECT * FROM c WHERE INDEX_OF(c.englishName,'soft') = 4 ", sqlQuery);
Assert.AreEqual("SELECT * FROM c WHERE INDEX_OF(c.englishName,'soft') = 4", sqlQuery);
}

[TestMethod]
Expand All @@ -250,7 +286,7 @@ public void TranslateSubstringSample()

var oDataToSqlTranslator = new ODataToSqlTranslator(new SQLQueryFormatter());
var sqlQuery = oDataToSqlTranslator.Translate(oDataQueryOptions, TranslateOptions.ALL & ~TranslateOptions.TOP_CLAUSE);
Assert.AreEqual("SELECT * FROM c WHERE SUBSTRING(c.englishName,1,LENGTH(c.englishName)) = 'icrosoft' ", sqlQuery);
Assert.AreEqual("SELECT * FROM c WHERE SUBSTRING(c.englishName,1,LENGTH(c.englishName)) = 'icrosoft'", sqlQuery);
}

[TestMethod]
Expand All @@ -261,7 +297,7 @@ public void TranslateTrimSample()

var oDataToSqlTranslator = new ODataToSqlTranslator(new SQLQueryFormatter());
var sqlQuery = oDataToSqlTranslator.Translate(oDataQueryOptions, TranslateOptions.ALL & ~TranslateOptions.TOP_CLAUSE);
Assert.AreEqual("SELECT * FROM c WHERE LTRIM(RTRIM(c.englishName)) = 'Microsoft' ", sqlQuery);
Assert.AreEqual("SELECT * FROM c WHERE LTRIM(RTRIM(c.englishName)) = 'Microsoft'", sqlQuery);
}

[TestMethod]
Expand All @@ -272,7 +308,7 @@ public void TranslateConcatSample()

var oDataToSqlTranslator = new ODataToSqlTranslator(new SQLQueryFormatter());
var sqlQuery = oDataToSqlTranslator.Translate(oDataQueryOptions, TranslateOptions.ALL & ~TranslateOptions.TOP_CLAUSE);
Assert.AreEqual("SELECT * FROM c WHERE CONCAT(c.englishName,' Canada') = 'Microsoft Canada' ", sqlQuery);
Assert.AreEqual("SELECT * FROM c WHERE CONCAT(c.englishName,' Canada') = 'Microsoft Canada'", sqlQuery);
}

[TestMethod]
Expand All @@ -285,5 +321,38 @@ public void TranslateMasterSample()
var sqlQuery = oDataToSqlTranslator.Translate(oDataQueryOptions, TranslateOptions.ALL, "c._t = 'dataType'");
Assert.AreEqual("SELECT TOP 30 c.id, c.englishName FROM c WHERE c._t = 'dataType' AND c.title = 'title1' AND c.property.field != 'val' OR c.viewedCount >= 5 AND (c.likedCount != 3 OR c.enumNumber = 'TWO') ORDER BY c._lastClientEditedDateTime ASC, c.createdDateTime DESC ", sqlQuery);
}

[TestMethod]
public void TranslateCountSample()
{
httpRequestMessage.RequestUri = new Uri("http://localhost/User?$count=true&$filter=englishName eq 'Microsoft'");
var oDataQueryOptions = new ODataQueryOptions(oDataQueryContext, httpRequestMessage);

var oDataToSqlTranslator = new ODataToSqlTranslator(new SQLQueryFormatter());
var sqlQuery = oDataToSqlTranslator.Translate(oDataQueryOptions, TranslateOptions.SELECT_CLAUSE | TranslateOptions.WHERE_CLAUSE);
Assert.AreEqual("SELECT VALUE COUNT(1) FROM c WHERE c.englishName = 'Microsoft'", sqlQuery);
}

[TestMethod]
public void TranslateGeoDistanceSample()
{
httpRequestMessage.RequestUri = new Uri("http://localhost/User?$filter=geo.distance(location, geography'POINT(31.9 -4.8)') lt 100");
var oDataQueryOptions = new ODataQueryOptions(oDataQueryContext, httpRequestMessage);

var oDataToSqlTranslator = new ODataToSqlTranslator(new SQLQueryFormatter());
var sqlQuery = oDataToSqlTranslator.Translate(oDataQueryOptions, TranslateOptions.ALL & ~TranslateOptions.TOP_CLAUSE);
Assert.AreEqual("SELECT * FROM c WHERE ST_DISTANCE(c.location,{\"type\":\"Point\",\"coordinates\":[31.9,-4.8]}) < 100", sqlQuery);
}

[TestMethod]
public void TranslateGeoIntersectsSample()
{
httpRequestMessage.RequestUri = new Uri("http://localhost/User?$filter=geo.intersects(area, geography'POLYGON((31.8 -5, 32 -5, 32 -4.7, 31.8 -4.7, 31.8 -5))')");
var oDataQueryOptions = new ODataQueryOptions(oDataQueryContext, httpRequestMessage);

var oDataToSqlTranslator = new ODataToSqlTranslator(new SQLQueryFormatter());
var sqlQuery = oDataToSqlTranslator.Translate(oDataQueryOptions, TranslateOptions.ALL & ~TranslateOptions.TOP_CLAUSE);
Assert.AreEqual("SELECT * FROM c WHERE ST_INTERSECTS(c.area,{\"type\":\"Polygon\",\"coordinates\":[[[31.8,-5.0],[32.0,-5.0],[32.0,-4.7],[31.8,-4.7],[31.8,-5.0]]]})", sqlQuery);
}
}
}
75 changes: 75 additions & 0 deletions azure-documentdb-odata-sql-samples/app.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.1.1" newVersion="4.1.1.1" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.IO" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Xml.ReaderWriter" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Reflection" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.Extensions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.Tracing" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.InteropServices" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.DependencyInjection.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.1.1.0" newVersion="1.1.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.DependencyInjection" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.1.1.0" newVersion="1.1.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.OData.Core" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.3.1.10814" newVersion="7.3.1.10814" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.OData.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.3.1.10814" newVersion="7.3.1.10814" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Spatial" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.3.1.10814" newVersion="7.3.1.10814" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Loading