Added Last years labs as starting point
|
@ -17,6 +17,7 @@
|
|||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
build/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
@ -24,8 +25,6 @@ bld/
|
|||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
|
@ -80,8 +79,6 @@ ipch/
|
|||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
|
@ -98,6 +95,7 @@ $tf/
|
|||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
|
@ -140,16 +138,11 @@ publish/
|
|||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
|
@ -170,6 +163,9 @@ csx/
|
|||
ecf/
|
||||
rcf/
|
||||
|
||||
# Microsoft Azure ApplicationInsights config file
|
||||
ApplicationInsights.config
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
|
@ -193,7 +189,7 @@ ClientBin/
|
|||
node_modules/
|
||||
orleans.codegen.cs
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
|
@ -242,7 +238,6 @@ _Pvt_Extensions
|
|||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
// Available variables which can be used inside of strings.
|
||||
// ${workspaceRoot}: the root folder of the team
|
||||
// ${file}: the current opened file
|
||||
// ${fileBasename}: the current opened file's basename
|
||||
// ${fileDirname}: the current opened file's dirname
|
||||
// ${fileExtname}: the current opened file's extension
|
||||
// ${cwd}: the current working directory of the spawned process
|
||||
|
||||
{
|
||||
"command": "marked",
|
||||
"version": "0.1.0",
|
||||
"showOutput": "always",
|
||||
"suppressTaskName": true,
|
||||
"isShellCommand": true,
|
||||
"tasks": [
|
||||
{
|
||||
"taskName": "Compile Azure Search Lab",
|
||||
"args": ["-i", "Labs/Azure Search/hands-on-lab.md", "-o", "Labs/Azure Search/hands-on-lab.html"],
|
||||
"suppressTaskName": true,
|
||||
"isBuildCommand": true
|
||||
},
|
||||
{
|
||||
"taskName": "Compile DocumentDB Lab",
|
||||
"args": ["-i", "Labs/DocumentDB/hands-on-lab.md", "-o", "Labs/DocumentDB/hands-on-lab.html"],
|
||||
"suppressTaskName": true,
|
||||
"isBuildCommand": true
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
Azure Search
|
||||
======
|
||||
|
||||
See the hands-on lab [here](hands-on-lab.md)
|
|
@ -0,0 +1,234 @@
|
|||
<h1 id="using-azure-search-api">Using Azure Search Api</h1>
|
||||
<h2 id="overview">Overview</h2>
|
||||
<p>Azure Search is a fully managed search-as-a-service in the cloud. Azure Search offers scalable full-text search for your apps.</p>
|
||||
<p>We will examine how quick and easy it is to execute searches with Azure Search, and what extra capabilities it offers us.</p>
|
||||
<p>This hands-on lab will step you through the following features:</p>
|
||||
<ol>
|
||||
<li><strong>Text Search</strong> Using Azure Search to search through a collection of documents.</li>
|
||||
<li><strong>Spell Corrected Search</strong> Using Azure Search to automatically correct spelling mistakes.</li>
|
||||
<li><strong>Phonetic Search</strong> Using Azure Search to use "sounds like" searching.</li>
|
||||
<li><strong>Filtering</strong> Using Azure Search faceting features which make filtering simple.</li>
|
||||
<li><strong>Geospatial</strong> Using the built-in geospatial filtering makes it simple to do distance or perimeter searches.</li>
|
||||
</ol>
|
||||
<h3 id="scenario-overview">Scenario Overview</h3>
|
||||
<p>We have built an Azure Search index that contains jobs in New York City for the user to find. </p>
|
||||
<h3 id="about-the-code">About the code</h3>
|
||||
<p>This lab uses a simple Universal Windows Application as a test application. This simple application can be modified
|
||||
in order to test the various features of Azure Search.</p>
|
||||
<p>To begin, open the <code>Azure Search Lab.sln</code> solution in visual studio 2015 and press <code>F5</code> to compile and launch the
|
||||
Windows Universal application.</p>
|
||||
<p>The lab is broken into three <strong>Scenarios</strong>.</p>
|
||||
<blockquote>
|
||||
<p><strong>Note:</strong> The Azure Search index that we will be querying during this lab was created via the Azure Portal.
|
||||
For more information on the Azure Portal refer to the <strong>Appendix</strong> at the bottom of the page.</p>
|
||||
</blockquote>
|
||||
<h2 id="scenario-1">Scenario 1</h2>
|
||||
<p>In this scenario we will introduce <strong>Text Searching</strong> and explain how to use <strong>Spell Corrected Search</strong> and
|
||||
<strong>Phonetic Search</strong>.</p>
|
||||
<h3 id="part-one">Part One</h3>
|
||||
<p>To begin, run the application. You are presented with a blank application with a texbox at the top. Enter "engineer"
|
||||
into this text box and press <code>enter</code>. </p>
|
||||
<p>You will notice that you did not get any results - let's fix this.</p>
|
||||
<p>Navigate to the <code>JobSearchService.cs</code> file.</p>
|
||||
<p><img src="images/azure_search_service.png" alt=""></p>
|
||||
<p>Locate the following method:</p>
|
||||
<pre><code class="lang-csharp">public async Task<DocumentSearchResult<JobResult>> ExecuteSearch(string query, List<FacetGroup> facets = null, PositionDistanceSearch geoSearch = null)
|
||||
{
|
||||
//TO DO - Place holder for search
|
||||
return new DocumentSearchResult<JobResult>();
|
||||
}
|
||||
</code></pre>
|
||||
<p>This is just placeholder code. Modify it so that it looks like this:</p>
|
||||
<pre><code class="lang-csharp">public async Task<DocumentSearchResult<JobResult>> ExecuteSearch(string query, List<FacetGroup> facets = null, PositionDistanceSearch geoSearch = null)
|
||||
{
|
||||
var searchParameters = new SearchParameters()
|
||||
{
|
||||
QueryType = QueryType.Full,
|
||||
SearchMode = SearchMode.All,
|
||||
};
|
||||
using (var indexClient = GetClient())
|
||||
{
|
||||
return await indexClient.Documents.SearchAsync<JobResult>(query, searchParameters);
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>Press <code>F5</code> to run the application, and type "engineer" into the search box. Press <code>enter</code>; you should be presented with a list of results.</p>
|
||||
<p>E.g.</p>
|
||||
<p><img src="images/first_engineer_search.png" alt="Search Results for engineer"></p>
|
||||
<p>Clear the textbox and type "enginer" (<strong>note</strong> the spelling mistake). Press <code>enter</code> to search. </p>
|
||||
<p>Notice that the results are identical - Azure Search has automatically detected the spelling mistake and has included this corrected search term along with the original misspelled word.</p>
|
||||
<p>E.g.</p>
|
||||
<p><img src="images/second_enginer_search.png" alt="Search Results for enginer"></p>
|
||||
<p>Clear the textbox and type "kownsil". Press <code>enter</code> to search. You will see results related to "counsel".</p>
|
||||
<p>Azure Search supports phonetic ("sounds like") searching. With very little work, we have leveraged a very powerful search tool that we can use in our application.</p>
|
||||
<p>You can learn more about how Azure Search enables phoenetic and spelling mistake handling in the <a href="https://azure.microsoft.com/en-us/blog/custom-analyzers-in-azure-search/">Azure Blog</a></p>
|
||||
<p>E.g.</p>
|
||||
<p><img src="images/kownsil_search.png" alt="Search Results for kownsil"></p>
|
||||
<h3 id="part-two">Part Two</h3>
|
||||
<p>A common app feature is the "auto-complete" textbox. Azure Search supports auto-complete suggestions out of the box - let's see how it's done.</p>
|
||||
<p>Stop running the application. Return to Visual Studio and navigate to <code>JobSearchService.cs</code>.</p>
|
||||
<p>Locate the following method stub:</p>
|
||||
<pre><code class="lang-csharp">public async Task<List<string>> ExecuteSuggest(string query)
|
||||
{
|
||||
//TO DO - Place holder for suggest
|
||||
return new List<string>();
|
||||
}
|
||||
</code></pre>
|
||||
<p>Modify the method to look like this:</p>
|
||||
<pre><code class="lang-csharp">public async Task<List<string>> ExecuteSuggest(string query)
|
||||
{
|
||||
using (var indexClient = GetClient())
|
||||
{
|
||||
// Query the Azure Search index for search suggestions
|
||||
SuggestParameters sp = new SuggestParameters()
|
||||
{
|
||||
UseFuzzyMatching = true,
|
||||
Top = 8
|
||||
};
|
||||
var results = await indexClient.Documents.SuggestAsync<JobResult>(query, "sg", sp);
|
||||
|
||||
// Extract the text suggestions from the result set
|
||||
return results.Results.Select(e => e.Text).Distinct().ToList();
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>You will notice that we are passing <code>sg</code> as the second parameter to the <code>SuggestAsync</code> method. This parameter names a "suggestor" that has been pre-created on the Azure Search
|
||||
index that defines which fields should be used to return a set of "suggestions" based on a query.</p>
|
||||
<p>If you would like to learn more about how Azure Search enables search suggestions, please visit the <a href="https://azure.microsoft.com/en-us/blog/azure-search-how-to-add-suggestions-auto-complete-to-your-search-applications/">Azure Blog</a></p>
|
||||
<p>Press <code>F5</code> to run the application. Type "eng" into the search box. As you type you should now see the auto complete list appear and suggest search terms to you.</p>
|
||||
<p>E.g.</p>
|
||||
<p><img src="images/auto_suggest.png" alt="Auto Suggest Results for Eng"></p>
|
||||
<h2 id="scenario-2">Scenario 2</h2>
|
||||
<p>In this scenario we will introduce <strong>Filtering / Faceting</strong>.</p>
|
||||
<h3 id="part-one">Part One</h3>
|
||||
<p>Now that we have basic search functionality, wouldn't it be nice to be able to filter the results further? Luckily this is also very easy with Azure Search using the
|
||||
"facets" filtering feature.</p>
|
||||
<p>Navigate to <code>JobSearchService.cs</code> and locate the <code>ExecuteSearch</code> method that we modified in Scenario 1. It should look like this:</p>
|
||||
<pre><code class="lang-csharp">public async Task<DocumentSearchResult<JobResult>> ExecuteSearch(string query, List<FacetGroup> facets = null, PositionDistanceSearch geoSearch = null)
|
||||
{
|
||||
var searchParameters = new SearchParameters()
|
||||
{
|
||||
QueryType = QueryType.Full,
|
||||
SearchMode = SearchMode.All,
|
||||
};
|
||||
using (var indexClient = GetClient())
|
||||
{
|
||||
return await indexClient.Documents.SearchAsync<JobResult>(query, searchParameters);
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>Add some facets to the query...</p>
|
||||
<pre><code class="lang-csharp">Facets = FacetDefinitions.Select(e => e.Key).ToList()
|
||||
</code></pre>
|
||||
<p>...so the method looks like this:</p>
|
||||
<pre><code class="lang-csharp">public async Task<DocumentSearchResult<JobResult>> ExecuteSearch(string query, List<FacetGroup> facets = null, PositionDistanceSearch geoSearch = null)
|
||||
{
|
||||
var searchParameters = new SearchParameters()
|
||||
{
|
||||
QueryType = QueryType.Full,
|
||||
SearchMode = SearchMode.All,
|
||||
Facets = FacetDefinitions.Select(e => e.Key).ToList()
|
||||
};
|
||||
using (var indexClient = GetClient())
|
||||
{
|
||||
return await indexClient.Documents.SearchAsync<JobResult>(query, searchParameters);
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Note:</strong> <code>FacetDefinitions</code> is a Dictionary already defined with the facet names and user-friendly display strings at the top of the class:</p>
|
||||
<p>This allows you to retreive the number of results available for the categories Agency, Posting_Type and Civil_Service_Title which you can use to further filter the search results. To learn more about how Facets work, please visit <a href="https://azure.microsoft.com/en-us/documentation/articles/search-faceted-navigation/">https://azure.microsoft.com/en-us/documentation/articles/search-faceted-navigation/</a></p>
|
||||
<pre><code class="lang-csharp"> public static Dictionary<string, string> FacetDefinitions = new Dictionary<string, string>()
|
||||
{
|
||||
{"agency", "Agency"},
|
||||
{"posting_type", "Internal/External"},
|
||||
{"civil_service_title", "Common Job Title"}
|
||||
};
|
||||
</code></pre>
|
||||
<p>As you can see we are passing in three facets: <code>agency</code>, <code>posting_type</code> and <code>civil_service_title</code>. Let's run the application and see what this looks like. </p>
|
||||
<p>Press <code>F5</code> to start the application. Type "engineer" into the search box and press <code>enter</code>.</p>
|
||||
<p>E.g.</p>
|
||||
<p><img src="images/engineer_with_facets.png" alt="Auto Suggest Results for Eng"></p>
|
||||
<p>Azure Search now returns a collection of <em>facets</em> which the user can use to refine their query. We're rendering them on the UI as a list of checkboxes. </p>
|
||||
<h3 id="part-two">Part Two</h3>
|
||||
<p>Now that we have a collection of usable facets, we need to create a filter to ask Azure Search to refine our query.</p>
|
||||
<p>Return to Visual Studio and open <code>JobSearchService.cs</code>. Go to the <code>ExecuteSearch</code> function and add this line:</p>
|
||||
<pre><code class="lang-csharp">Filter = CreateFilter(facets, geoSearch)
|
||||
</code></pre>
|
||||
<p>...so that the method looks like this:</p>
|
||||
<pre><code class="lang-csharp">public async Task<DocumentSearchResult<JobResult>> ExecuteSearch(string query, List<FacetGroup> facets = null, PositionDistanceSearch geoSearch = null)
|
||||
{
|
||||
var searchParameters = new SearchParameters()
|
||||
{
|
||||
QueryType = QueryType.Full,
|
||||
SearchMode = SearchMode.All,
|
||||
Facets = FacetDefinitions.Select(e => e.Key).ToList(),
|
||||
Filter = CreateFilter(facets, geoSearch)
|
||||
};
|
||||
using (var indexClient = GetClient())
|
||||
{
|
||||
return await indexClient.Documents.SearchAsync<JobResult>(query, searchParameters);
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><code>CreateFilter</code> is a method that builds up a filter string for Azure Search. It loops through the given list of facets and encodes each one into the filter string.
|
||||
This filter is written in OData expression syntax. </p>
|
||||
<p>Some examples of filters created by this method:</p>
|
||||
<pre><code>(agency eq 'DEPT OF HEALTH/MENTAL HYGIENE') and (posting_type eq 'Internal')
|
||||
</code></pre><pre><code>(agency eq 'DEPT OF HEALTH/MENTAL HYGIENE' or agency eq 'DEPT OF ENVIRONMENT PROTECTION' or agency eq 'DEPT OF DESIGN & CONSTRUCTION' or agency eq 'HOUSING PRESERVATION & DVLPMNT' or agency eq 'DEPT OF INFO TECH & TELECOMM')
|
||||
</code></pre><pre><code>(agency eq 'DEPT OF ENVIRONMENT PROTECTION') and (posting_type eq 'Internal') and (civil_service_title eq 'ASSOCIATE PROJECT MANAGER')
|
||||
</code></pre><p>Press <code>F5</code> to run the application, then execute a search and try out filtering the results by selecting <em>facets</em>.</p>
|
||||
<h2 id="scenario-3">Scenario 3</h2>
|
||||
<p>In this scenario we will introduce <strong>Geospatial</strong> filtering.</p>
|
||||
<p>This allows you to filter results based on their proximity to a specific location or to find all results that are located within a specified boundary.<br>In this example, we will use the location proximity to filter jobs that are located within a certain distance of a specified location.</p>
|
||||
<h3 id="part-one">Part One</h3>
|
||||
<p>Azure Search supports geospatial searching if there is an field in your index of the <code>Edm.GeographyPoint</code> type. Let's see how easy it is to integrate
|
||||
geospatial search into your queries.</p>
|
||||
<p>Press <code>F5</code> to run the application and execute an empty search.</p>
|
||||
<p>Next click on <strong>Map Results</strong> and you should see all the search results plotted on the map.</p>
|
||||
<p>E.g</p>
|
||||
<p><img src="images/map_results.png" alt="Map Results"></p>
|
||||
<h3 id="part-two">Part Two</h3>
|
||||
<p>Lets add the geo spatial search to the filter.</p>
|
||||
<p>Return to Visual Studio and open <code>JobSearchService.cs</code>. Locate the <code>CreateFilter</code> method.</p>
|
||||
<p>Add this section of code before the final <code>return</code>. This code appends the geospatial filter if it has been provided.</p>
|
||||
<pre><code class="lang-csharp">if (geoSearch != null)
|
||||
{
|
||||
if (query.Length > 0)
|
||||
{
|
||||
query.Append(" and ");
|
||||
}
|
||||
var lat = geoSearch.GeoPoint.Position.Latitude;
|
||||
var lon = geoSearch.GeoPoint.Position.Longitude;
|
||||
query.Append($"geo.distance(geo_location, geography'POINT({lon} {lat})') le {geoSearch.Radius}");
|
||||
}
|
||||
</code></pre>
|
||||
<p>Like the <em>facets</em> filter, geospatial filtering is written in OData expression syntax. This is an example of a filter string that allows you to retrieve jobs located within 10KM of a point in New York city :</p>
|
||||
<pre><code>geo.distance(geo_location, geography'POINT(-73.9593882020563 40.7079201657325)') le 10
|
||||
</code></pre><p>Press <code>F5</code> to run the application. Execute an empty search. </p>
|
||||
<p>Next click on the <strong>Map Results</strong> tab. and you should see all the search results plotted on the map.</p>
|
||||
<p>Double click on the map to drop a "pin" and select a search radius from the drop-down menu.</p>
|
||||
<p>E.g.</p>
|
||||
<p><img src="images/map_results_raduis.png" alt="Map Radius"></p>
|
||||
<p>Click on <strong>Filter Results</strong>. You will see that the results have been filtered based on their geographical location!</p>
|
||||
<p>E.g.</p>
|
||||
<p><img src="images/map_filter.png" alt="Map Filter"></p>
|
||||
<h3 id="final-notes">Final Notes</h3>
|
||||
<p>If you would like to learn more about geospatial filtering then check out <a href="https://azure.microsoft.com/en-us/documentation/articles/search-create-geospatial/">Create a geospatial search app using Azure Search</a>.</p>
|
||||
<p>You can also watch <a href="https://channel9.msdn.com/Shows/Data-Exposed/Azure-Search-and-Geospatial-Data">Azure Search and Geospatial Data</a> on Channel 9.</p>
|
||||
<h2 id="further-reading">Further Reading</h2>
|
||||
<p><a href="https://azure.microsoft.com/en-us/documentation/articles/search-howto-stackexchange-data/">https://azure.microsoft.com/en-us/documentation/articles/search-howto-stackexchange-data/</a></p>
|
||||
<p><a href="https://azure.microsoft.com/en-us/documentation/videos/azure-search-101-getting-started-with-azure-search-with-liam-cavanagh/">Azure Search 101 - Getting started with Azure Search with Liam Cavanagh</a></p>
|
||||
<p><a href="https://azure.microsoft.com/en-us/documentation/articles/search-what-is-azure-search/">What is Azure Search</a></p>
|
||||
<p><a href="https://azure.microsoft.com/en-us/documentation/services/search/">Search Documentation</a></p>
|
||||
<h2 id="appendix">Appendix</h2>
|
||||
<p>The Azure Portal was used to create the Azure Search server. The Azure Portal can be found at <a href="https://portal.azure.com/">https://portal.azure.com/</a>.</p>
|
||||
<p>Some features that you can use in Azure Portal with Azure Search include:</p>
|
||||
<h4 id="overview">Overview</h4>
|
||||
<p>Allows you to view database settings and document/collection counts for your server.</p>
|
||||
<p><img src="images/jobs_lab_azure_search1.png" alt=""></p>
|
||||
<h4 id="index-viewer">Index Viewer</h4>
|
||||
<p>Allows you to view your indexes and the fields they include. You can also modify them from here.</p>
|
||||
<p><img src="images/jobs_lab_azure_search2.png" alt=""></p>
|
||||
<h4 id="search-explorer">Search Explorer</h4>
|
||||
<p>Test out your search and filter strings, and view the results.</p>
|
||||
<p><img src="images/jobs_lab_azure_search3.png" alt=""></p>
|
|
@ -0,0 +1,382 @@
|
|||
Using Azure Search Api
|
||||
======================
|
||||
|
||||
## Overview
|
||||
|
||||
Azure Search is a fully managed search-as-a-service in the cloud. Azure Search offers scalable full-text search for your apps.
|
||||
|
||||
We will examine how quick and easy it is to execute searches with Azure Search, and what extra capabilities it offers us.
|
||||
|
||||
This hands-on lab will step you through the following features:
|
||||
|
||||
1. **Text Search** Using Azure Search to search through a collection of documents.
|
||||
2. **Spell Corrected Search** Using Azure Search to automatically correct spelling mistakes.
|
||||
3. **Phonetic Search** Using Azure Search to use "sounds like" searching.
|
||||
4. **Filtering** Using Azure Search faceting features which make filtering simple.
|
||||
5. **Geospatial** Using the built-in geospatial filtering makes it simple to do distance or perimeter searches.
|
||||
|
||||
### Scenario Overview
|
||||
|
||||
We have built an Azure Search index that contains jobs in New York City for the user to find.
|
||||
|
||||
### About the code
|
||||
|
||||
This lab uses a simple Universal Windows Application as a test application. This simple application can be modified
|
||||
in order to test the various features of Azure Search.
|
||||
|
||||
To begin, open the `Azure Search Lab.sln` solution in visual studio 2015 and press `F5` to compile and launch the
|
||||
Windows Universal application.
|
||||
|
||||
The lab is broken into three **Scenarios**.
|
||||
|
||||
> **Note:** The Azure Search index that we will be querying during this lab was created via the Azure Portal.
|
||||
> For more information on the Azure Portal refer to the **Appendix** at the bottom of the page.
|
||||
|
||||
|
||||
## Scenario 1
|
||||
|
||||
In this scenario we will introduce **Text Searching** and explain how to use **Spell Corrected Search** and
|
||||
**Phonetic Search**.
|
||||
|
||||
### Part One
|
||||
|
||||
To begin, run the application. You are presented with a blank application with a texbox at the top. Enter "engineer"
|
||||
into this text box and press `enter`.
|
||||
|
||||
You will notice that you did not get any results - let's fix this.
|
||||
|
||||
Navigate to the `JobSearchService.cs` file.
|
||||
|
||||
![](images/azure_search_service.png)
|
||||
|
||||
Locate the following method:
|
||||
|
||||
```csharp
|
||||
public async Task<DocumentSearchResult<JobResult>> ExecuteSearch(string query, List<FacetGroup> facets = null, PositionDistanceSearch geoSearch = null)
|
||||
{
|
||||
//TO DO - Place holder for search
|
||||
return new DocumentSearchResult<JobResult>();
|
||||
}
|
||||
```
|
||||
|
||||
This is just placeholder code. Modify it so that it looks like this:
|
||||
|
||||
```csharp
|
||||
public async Task<DocumentSearchResult<JobResult>> ExecuteSearch(string query, List<FacetGroup> facets = null, PositionDistanceSearch geoSearch = null)
|
||||
{
|
||||
var searchParameters = new SearchParameters()
|
||||
{
|
||||
QueryType = QueryType.Full,
|
||||
SearchMode = SearchMode.All,
|
||||
};
|
||||
using (var indexClient = GetClient())
|
||||
{
|
||||
return await indexClient.Documents.SearchAsync<JobResult>(query, searchParameters);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Press `F5` to run the application, and type "engineer" into the search box. Press `enter`; you should be presented with a list of results.
|
||||
|
||||
E.g.
|
||||
|
||||
![Search Results for engineer](images/first_engineer_search.png)
|
||||
|
||||
Clear the textbox and type "enginer" (**note** the spelling mistake). Press `enter` to search.
|
||||
|
||||
Notice that the results are identical - Azure Search has automatically detected the spelling mistake and has included this corrected search term along with the original misspelled word.
|
||||
|
||||
E.g.
|
||||
|
||||
![Search Results for enginer](images/second_enginer_search.png)
|
||||
|
||||
Clear the textbox and type "kownsil". Press `enter` to search. You will see results related to "counsel".
|
||||
|
||||
Azure Search supports phonetic ("sounds like") searching. With very little work, we have leveraged a very powerful search tool that we can use in our application.
|
||||
|
||||
You can learn more about how Azure Search enables phoenetic and spelling mistake handling in the [Azure Blog](https://azure.microsoft.com/en-us/blog/custom-analyzers-in-azure-search/)
|
||||
|
||||
E.g.
|
||||
|
||||
![Search Results for kownsil](images/kownsil_search.png)
|
||||
|
||||
### Part Two
|
||||
|
||||
A common app feature is the "auto-complete" textbox. Azure Search supports auto-complete suggestions out of the box - let's see how it's done.
|
||||
|
||||
Stop running the application. Return to Visual Studio and navigate to `JobSearchService.cs`.
|
||||
|
||||
Locate the following method stub:
|
||||
|
||||
```csharp
|
||||
public async Task<List<string>> ExecuteSuggest(string query)
|
||||
{
|
||||
//TO DO - Place holder for suggest
|
||||
return new List<string>();
|
||||
}
|
||||
```
|
||||
|
||||
Modify the method to look like this:
|
||||
|
||||
```csharp
|
||||
public async Task<List<string>> ExecuteSuggest(string query)
|
||||
{
|
||||
using (var indexClient = GetClient())
|
||||
{
|
||||
// Query the Azure Search index for search suggestions
|
||||
SuggestParameters sp = new SuggestParameters()
|
||||
{
|
||||
UseFuzzyMatching = true,
|
||||
Top = 8
|
||||
};
|
||||
var results = await indexClient.Documents.SuggestAsync<JobResult>(query, "sg", sp);
|
||||
|
||||
// Extract the text suggestions from the result set
|
||||
return results.Results.Select(e => e.Text).Distinct().ToList();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You will notice that we are passing `sg` as the second parameter to the `SuggestAsync` method. This parameter names a "suggestor" that has been pre-created on the Azure Search
|
||||
index that defines which fields should be used to return a set of "suggestions" based on a query.
|
||||
|
||||
If you would like to learn more about how Azure Search enables search suggestions, please visit the [Azure Blog](https://azure.microsoft.com/en-us/blog/azure-search-how-to-add-suggestions-auto-complete-to-your-search-applications/)
|
||||
|
||||
Press `F5` to run the application. Type "eng" into the search box. As you type you should now see the auto complete list appear and suggest search terms to you.
|
||||
|
||||
E.g.
|
||||
|
||||
![Auto Suggest Results for Eng](images/auto_suggest.png)
|
||||
|
||||
|
||||
## Scenario 2
|
||||
|
||||
In this scenario we will introduce **Filtering / Faceting**.
|
||||
|
||||
### Part One
|
||||
|
||||
Now that we have basic search functionality, wouldn't it be nice to be able to filter the results further? Luckily this is also very easy with Azure Search using the
|
||||
"facets" filtering feature.
|
||||
|
||||
Navigate to `JobSearchService.cs` and locate the `ExecuteSearch` method that we modified in Scenario 1. It should look like this:
|
||||
|
||||
```csharp
|
||||
public async Task<DocumentSearchResult<JobResult>> ExecuteSearch(string query, List<FacetGroup> facets = null, PositionDistanceSearch geoSearch = null)
|
||||
{
|
||||
var searchParameters = new SearchParameters()
|
||||
{
|
||||
QueryType = QueryType.Full,
|
||||
SearchMode = SearchMode.All,
|
||||
};
|
||||
using (var indexClient = GetClient())
|
||||
{
|
||||
return await indexClient.Documents.SearchAsync<JobResult>(query, searchParameters);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Add some facets to the query...
|
||||
|
||||
```csharp
|
||||
Facets = FacetDefinitions.Select(e => e.Key).ToList()
|
||||
```
|
||||
|
||||
...so the method looks like this:
|
||||
|
||||
```csharp
|
||||
public async Task<DocumentSearchResult<JobResult>> ExecuteSearch(string query, List<FacetGroup> facets = null, PositionDistanceSearch geoSearch = null)
|
||||
{
|
||||
var searchParameters = new SearchParameters()
|
||||
{
|
||||
QueryType = QueryType.Full,
|
||||
SearchMode = SearchMode.All,
|
||||
Facets = FacetDefinitions.Select(e => e.Key).ToList()
|
||||
};
|
||||
using (var indexClient = GetClient())
|
||||
{
|
||||
return await indexClient.Documents.SearchAsync<JobResult>(query, searchParameters);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** `FacetDefinitions` is a Dictionary already defined with the facet names and user-friendly display strings at the top of the class:
|
||||
|
||||
This allows you to retreive the number of results available for the categories Agency, Posting_Type and Civil_Service_Title which you can use to further filter the search results. To learn more about how Facets work, please visit [https://azure.microsoft.com/en-us/documentation/articles/search-faceted-navigation/](https://azure.microsoft.com/en-us/documentation/articles/search-faceted-navigation/)
|
||||
|
||||
```csharp
|
||||
public static Dictionary<string, string> FacetDefinitions = new Dictionary<string, string>()
|
||||
{
|
||||
{"agency", "Agency"},
|
||||
{"posting_type", "Internal/External"},
|
||||
{"civil_service_title", "Common Job Title"}
|
||||
};
|
||||
```
|
||||
|
||||
As you can see we are passing in three facets: `agency`, `posting_type` and `civil_service_title`. Let's run the application and see what this looks like.
|
||||
|
||||
Press `F5` to start the application. Type "engineer" into the search box and press `enter`.
|
||||
|
||||
E.g.
|
||||
|
||||
![Auto Suggest Results for Eng](images/engineer_with_facets.png)
|
||||
|
||||
Azure Search now returns a collection of _facets_ which the user can use to refine their query. We're rendering them on the UI as a list of checkboxes.
|
||||
|
||||
|
||||
### Part Two
|
||||
|
||||
Now that we have a collection of usable facets, we need to create a filter to ask Azure Search to refine our query.
|
||||
|
||||
Return to Visual Studio and open `JobSearchService.cs`. Go to the `ExecuteSearch` function and add this line:
|
||||
|
||||
```csharp
|
||||
Filter = CreateFilter(facets, geoSearch)
|
||||
```
|
||||
|
||||
...so that the method looks like this:
|
||||
|
||||
```csharp
|
||||
public async Task<DocumentSearchResult<JobResult>> ExecuteSearch(string query, List<FacetGroup> facets = null, PositionDistanceSearch geoSearch = null)
|
||||
{
|
||||
var searchParameters = new SearchParameters()
|
||||
{
|
||||
QueryType = QueryType.Full,
|
||||
SearchMode = SearchMode.All,
|
||||
Facets = FacetDefinitions.Select(e => e.Key).ToList(),
|
||||
Filter = CreateFilter(facets, geoSearch)
|
||||
};
|
||||
using (var indexClient = GetClient())
|
||||
{
|
||||
return await indexClient.Documents.SearchAsync<JobResult>(query, searchParameters);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`CreateFilter` is a method that builds up a filter string for Azure Search. It loops through the given list of facets and encodes each one into the filter string.
|
||||
This filter is written in OData expression syntax.
|
||||
|
||||
Some examples of filters created by this method:
|
||||
|
||||
```
|
||||
(agency eq 'DEPT OF HEALTH/MENTAL HYGIENE') and (posting_type eq 'Internal')
|
||||
```
|
||||
```
|
||||
(agency eq 'DEPT OF HEALTH/MENTAL HYGIENE' or agency eq 'DEPT OF ENVIRONMENT PROTECTION' or agency eq 'DEPT OF DESIGN & CONSTRUCTION' or agency eq 'HOUSING PRESERVATION & DVLPMNT' or agency eq 'DEPT OF INFO TECH & TELECOMM')
|
||||
```
|
||||
```
|
||||
(agency eq 'DEPT OF ENVIRONMENT PROTECTION') and (posting_type eq 'Internal') and (civil_service_title eq 'ASSOCIATE PROJECT MANAGER')
|
||||
```
|
||||
|
||||
Press `F5` to run the application, then execute a search and try out filtering the results by selecting _facets_.
|
||||
|
||||
|
||||
## Scenario 3
|
||||
|
||||
In this scenario we will introduce **Geospatial** filtering.
|
||||
|
||||
This allows you to filter results based on their proximity to a specific location or to find all results that are located within a specified boundary.
|
||||
In this example, we will use the location proximity to filter jobs that are located within a certain distance of a specified location.
|
||||
|
||||
### Part One
|
||||
|
||||
Azure Search supports geospatial searching if there is an field in your index of the `Edm.GeographyPoint` type. Let's see how easy it is to integrate
|
||||
geospatial search into your queries.
|
||||
|
||||
Press `F5` to run the application and execute an empty search.
|
||||
|
||||
Next click on **Map Results** and you should see all the search results plotted on the map.
|
||||
|
||||
E.g
|
||||
|
||||
![Map Results](images/map_results.png)
|
||||
|
||||
|
||||
### Part Two
|
||||
|
||||
Lets add the geo spatial search to the filter.
|
||||
|
||||
Return to Visual Studio and open `JobSearchService.cs`. Locate the `CreateFilter` method.
|
||||
|
||||
Add this section of code before the final `return`. This code appends the geospatial filter if it has been provided.
|
||||
|
||||
```csharp
|
||||
if (geoSearch != null)
|
||||
{
|
||||
if (query.Length > 0)
|
||||
{
|
||||
query.Append(" and ");
|
||||
}
|
||||
var lat = geoSearch.GeoPoint.Position.Latitude;
|
||||
var lon = geoSearch.GeoPoint.Position.Longitude;
|
||||
query.Append($"geo.distance(geo_location, geography'POINT({lon} {lat})') le {geoSearch.Radius}");
|
||||
}
|
||||
```
|
||||
|
||||
Like the _facets_ filter, geospatial filtering is written in OData expression syntax. This is an example of a filter string that allows you to retrieve jobs located within 10KM of a point in New York city :
|
||||
|
||||
```
|
||||
geo.distance(geo_location, geography'POINT(-73.9593882020563 40.7079201657325)') le 10
|
||||
```
|
||||
|
||||
Press `F5` to run the application. Execute an empty search.
|
||||
|
||||
Next click on the **Map Results** tab. and you should see all the search results plotted on the map.
|
||||
|
||||
Double click on the map to drop a "pin" and select a search radius from the drop-down menu.
|
||||
|
||||
E.g.
|
||||
|
||||
![Map Radius](images/map_results_raduis.png)
|
||||
|
||||
Click on **Filter Results**. You will see that the results have been filtered based on their geographical location!
|
||||
|
||||
E.g.
|
||||
|
||||
![Map Filter](images/map_filter.png)
|
||||
|
||||
### Final Notes
|
||||
|
||||
If you would like to learn more about geospatial filtering then check out [Create a geospatial search app using Azure Search][geospatial-search].
|
||||
|
||||
You can also watch [Azure Search and Geospatial Data][channel9-geospatial] on Channel 9.
|
||||
|
||||
[geospatial-search]: https://azure.microsoft.com/en-us/documentation/articles/search-create-geospatial/
|
||||
[channel9-geospatial]: https://channel9.msdn.com/Shows/Data-Exposed/Azure-Search-and-Geospatial-Data
|
||||
|
||||
|
||||
## Further Reading
|
||||
|
||||
[https://azure.microsoft.com/en-us/documentation/articles/search-howto-stackexchange-data/](https://azure.microsoft.com/en-us/documentation/articles/search-howto-stackexchange-data/)
|
||||
|
||||
[Azure Search 101 - Getting started with Azure Search with Liam Cavanagh](https://azure.microsoft.com/en-us/documentation/videos/azure-search-101-getting-started-with-azure-search-with-liam-cavanagh/)
|
||||
|
||||
[What is Azure Search](https://azure.microsoft.com/en-us/documentation/articles/search-what-is-azure-search/)
|
||||
|
||||
[Search Documentation](https://azure.microsoft.com/en-us/documentation/services/search/)
|
||||
|
||||
## Appendix
|
||||
|
||||
The Azure Portal was used to create the Azure Search server. The Azure Portal can be found at [https://portal.azure.com/](https://portal.azure.com/).
|
||||
|
||||
Some features that you can use in Azure Portal with Azure Search include:
|
||||
|
||||
|
||||
#### Overview
|
||||
|
||||
Allows you to view database settings and document/collection counts for your server.
|
||||
|
||||
![](images/jobs_lab_azure_search1.png)
|
||||
|
||||
|
||||
#### Index Viewer
|
||||
|
||||
Allows you to view your indexes and the fields they include. You can also modify them from here.
|
||||
|
||||
![](images/jobs_lab_azure_search2.png)
|
||||
|
||||
|
||||
#### Search Explorer
|
||||
|
||||
Test out your search and filter strings, and view the results.
|
||||
|
||||
![](images/jobs_lab_azure_search3.png)
|
После Ширина: | Высота: | Размер: 5.9 KiB |
После Ширина: | Высота: | Размер: 16 KiB |
После Ширина: | Высота: | Размер: 56 KiB |
После Ширина: | Высота: | Размер: 29 KiB |
После Ширина: | Высота: | Размер: 84 KiB |
После Ширина: | Высота: | Размер: 37 KiB |
После Ширина: | Высота: | Размер: 175 KiB |
После Ширина: | Высота: | Размер: 27 KiB |
После Ширина: | Высота: | Размер: 47 KiB |
После Ширина: | Высота: | Размер: 264 KiB |
После Ширина: | Высота: | Размер: 74 KiB |
После Ширина: | Высота: | Размер: 29 KiB |
|
@ -0,0 +1,32 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.23107.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JobSearch", "JobSearch\JobSearch.csproj", "{B8AD9731-00ED-4FCA-B6F3-80E0D686DDD1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{B8AD9731-00ED-4FCA-B6F3-80E0D686DDD1}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{B8AD9731-00ED-4FCA-B6F3-80E0D686DDD1}.Debug|x64.Build.0 = Debug|x64
|
||||
{B8AD9731-00ED-4FCA-B6F3-80E0D686DDD1}.Debug|x64.Deploy.0 = Debug|x64
|
||||
{B8AD9731-00ED-4FCA-B6F3-80E0D686DDD1}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{B8AD9731-00ED-4FCA-B6F3-80E0D686DDD1}.Debug|x86.Build.0 = Debug|x86
|
||||
{B8AD9731-00ED-4FCA-B6F3-80E0D686DDD1}.Debug|x86.Deploy.0 = Debug|x86
|
||||
{B8AD9731-00ED-4FCA-B6F3-80E0D686DDD1}.Release|x64.ActiveCfg = Release|x64
|
||||
{B8AD9731-00ED-4FCA-B6F3-80E0D686DDD1}.Release|x64.Build.0 = Release|x64
|
||||
{B8AD9731-00ED-4FCA-B6F3-80E0D686DDD1}.Release|x64.Deploy.0 = Release|x64
|
||||
{B8AD9731-00ED-4FCA-B6F3-80E0D686DDD1}.Release|x86.ActiveCfg = Release|x86
|
||||
{B8AD9731-00ED-4FCA-B6F3-80E0D686DDD1}.Release|x86.Build.0 = Release|x86
|
||||
{B8AD9731-00ED-4FCA-B6F3-80E0D686DDD1}.Release|x86.Deploy.0 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,8 @@
|
|||
<Application
|
||||
x:Class="JobSearch.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:JobSearch"
|
||||
RequestedTheme="Light">
|
||||
|
||||
</Application>
|
|
@ -0,0 +1,100 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using JobSearch.Views;
|
||||
|
||||
namespace JobSearch
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides application-specific behavior to supplement the default Application class.
|
||||
/// </summary>
|
||||
sealed partial class App : Application
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes the singleton application object. This is the first line of authored code
|
||||
/// executed, and as such is the logical equivalent of main() or WinMain().
|
||||
/// </summary>
|
||||
public App()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
this.Suspending += OnSuspending;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the application is launched normally by the end user. Other entry points
|
||||
/// will be used such as when the application is launched to open a specific file.
|
||||
/// </summary>
|
||||
/// <param name="e">Details about the launch request and process.</param>
|
||||
protected override void OnLaunched(LaunchActivatedEventArgs e)
|
||||
{
|
||||
|
||||
Frame rootFrame = Window.Current.Content as Frame;
|
||||
|
||||
// Do not repeat app initialization when the Window already has content,
|
||||
// just ensure that the window is active
|
||||
if (rootFrame == null)
|
||||
{
|
||||
// Create a Frame to act as the navigation context and navigate to the first page
|
||||
rootFrame = new Frame();
|
||||
|
||||
rootFrame.NavigationFailed += OnNavigationFailed;
|
||||
|
||||
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
|
||||
{
|
||||
//TODO: Load state from previously suspended application
|
||||
}
|
||||
|
||||
// Place the frame in the current Window
|
||||
Window.Current.Content = rootFrame;
|
||||
}
|
||||
|
||||
if (rootFrame.Content == null)
|
||||
{
|
||||
// When the navigation stack isn't restored navigate to the first page,
|
||||
// configuring the new page by passing required information as a navigation
|
||||
// parameter
|
||||
rootFrame.Navigate(typeof(MainPage), e.Arguments);
|
||||
}
|
||||
// Ensure the current window is active
|
||||
Window.Current.Activate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when Navigation to a certain page fails
|
||||
/// </summary>
|
||||
/// <param name="sender">The Frame which failed navigation</param>
|
||||
/// <param name="e">Details about the navigation failure</param>
|
||||
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
|
||||
{
|
||||
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
|
||||
var blah = sender;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when application execution is being suspended. Application state is saved
|
||||
/// without knowing whether the application will be terminated or resumed with the contents
|
||||
/// of memory still intact.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the suspend request.</param>
|
||||
/// <param name="e">Details about the suspend request.</param>
|
||||
private void OnSuspending(object sender, SuspendingEventArgs e)
|
||||
{
|
||||
var deferral = e.SuspendingOperation.GetDeferral();
|
||||
//TODO: Save application state and stop any background activity
|
||||
deferral.Complete();
|
||||
}
|
||||
}
|
||||
}
|
После Ширина: | Высота: | Размер: 733 B |
После Ширина: | Высота: | Размер: 32 KiB |
После Ширина: | Высота: | Размер: 3.5 KiB |
После Ширина: | Высота: | Размер: 1.5 KiB |
Двоичные данные
Labs/Azure Search/src/JobSearch/Assets/Square44x44Logo.targetsize-24_altform-unplated.png
Normal file
После Ширина: | Высота: | Размер: 359 B |
После Ширина: | Высота: | Размер: 713 B |
После Ширина: | Высота: | Размер: 11 KiB |
|
@ -0,0 +1,16 @@
|
|||
<UserControl
|
||||
x:Class="JobSearch.Controls.CustomMapIcon"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:JobSearch.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="20"
|
||||
d:DesignWidth="20">
|
||||
|
||||
<Grid Width="20" Height="20">
|
||||
<Ellipse Fill="White" />
|
||||
<Ellipse Fill="#FF488FF6" Margin="2" />
|
||||
</Grid>
|
||||
</UserControl>
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.Devices.Geolocation;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236
|
||||
|
||||
namespace JobSearch.Controls
|
||||
{
|
||||
public sealed partial class CustomMapIcon : UserControl
|
||||
{
|
||||
public CustomMapIcon()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<UserControl
|
||||
x:Class="JobSearch.Controls.LocationSearchIcon"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:JobSearch.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="20"
|
||||
d:DesignWidth="20">
|
||||
|
||||
<Grid Width="20" Height="20">
|
||||
<Ellipse Fill="White" />
|
||||
<Ellipse Fill="OrangeRed" Margin="2" />
|
||||
</Grid>
|
||||
</UserControl>
|
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.Devices.Geolocation;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236
|
||||
|
||||
namespace JobSearch.Controls
|
||||
{
|
||||
public sealed partial class LocationSearchIcon : UserControl
|
||||
{
|
||||
public LocationSearchIcon()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
public Geopoint GeoPoint { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Devices.Geolocation;
|
||||
|
||||
namespace JobSearch.Extensions
|
||||
{
|
||||
public static class GeopointExtensions
|
||||
{
|
||||
private const double Circle = 2*Math.PI;
|
||||
private const double DegreesToRadian = Math.PI/180.0;
|
||||
private const double RadianToDegrees = 180.0/Math.PI;
|
||||
private const double EarthRadius = 6378137.0;
|
||||
|
||||
public static IList<BasicGeoposition> GetCirclePoints(this Geopoint center,
|
||||
int radius, int nrOfPoints = 50)
|
||||
{
|
||||
var locations = new List<BasicGeoposition>();
|
||||
double latA = center.Position.Latitude*DegreesToRadian;
|
||||
double lonA = center.Position.Longitude*DegreesToRadian;
|
||||
double angularDistance = radius/EarthRadius;
|
||||
|
||||
double sinLatA = Math.Sin(latA);
|
||||
double cosLatA = Math.Cos(latA);
|
||||
double sinDistance = Math.Sin(angularDistance);
|
||||
double cosDistance = Math.Cos(angularDistance);
|
||||
double sinLatAtimeCosDistance = sinLatA*cosDistance;
|
||||
double cosLatAtimeSinDistance = cosLatA*sinDistance;
|
||||
|
||||
double step = Circle/nrOfPoints;
|
||||
for (double angle = 0; angle < Circle; angle += step)
|
||||
{
|
||||
var lat = Math.Asin(sinLatAtimeCosDistance + cosLatAtimeSinDistance*
|
||||
Math.Cos(angle));
|
||||
var dlon = Math.Atan2(Math.Sin(angle)*cosLatAtimeSinDistance,
|
||||
cosDistance - sinLatA*Math.Sin(lat));
|
||||
var lon = ((lonA + dlon + Math.PI)%Circle) - Math.PI;
|
||||
|
||||
locations.Add(new BasicGeoposition
|
||||
{
|
||||
Latitude = lat*RadianToDegrees,
|
||||
Longitude = lon*RadianToDegrees
|
||||
});
|
||||
}
|
||||
return locations;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x64</Platform>
|
||||
<ProjectGuid>{B8AD9731-00ED-4FCA-B6F3-80E0D686DDD1}</ProjectGuid>
|
||||
<OutputType>AppContainerExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>JobSearch</RootNamespace>
|
||||
<AssemblyName>JobSearch</AssemblyName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion>10.0.10240.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion>
|
||||
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<PackageCertificateKeyFile>JobSearch_TemporaryKey.pfx</PackageCertificateKeyFile>
|
||||
<PackageCertificateThumbprint>4261BDAA9AE7A2244F62180A3E28915B7F38EDA2</PackageCertificateThumbprint>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- A reference to the entire .Net Framework and Windows SDK are automatically included -->
|
||||
<None Include="JobSearch_TemporaryKey.pfx" />
|
||||
<None Include="project.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Controls\CustomMapIcon.xaml.cs">
|
||||
<DependentUpon>CustomMapIcon.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Controls\LocationSearchIcon.xaml.cs">
|
||||
<DependentUpon>LocationSearchIcon.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Extensions\GeopointExtensions.cs" />
|
||||
<Compile Include="Properties\Annotations.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SearchModels\FacetGroup.cs" />
|
||||
<Compile Include="SearchModels\FacetSelection.cs" />
|
||||
<Compile Include="SearchModels\JobResult.cs" />
|
||||
<Compile Include="SearchModels\PositionDistanceSearch.cs" />
|
||||
<Compile Include="Services\JobSearchService.cs" />
|
||||
<Compile Include="ViewModels\SearchViewModel.cs" />
|
||||
<Compile Include="Views\MainPage.xaml.cs">
|
||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Search.xaml.cs">
|
||||
<DependentUpon>Search.xaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
<SubType>Designer</SubType>
|
||||
</AppxManifest>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Properties\Default.rd.xml" />
|
||||
<Content Include="Assets\LockScreenLogo.scale-200.png" />
|
||||
<Content Include="Assets\SplashScreen.scale-200.png" />
|
||||
<Content Include="Assets\Square150x150Logo.scale-200.png" />
|
||||
<Content Include="Assets\Square44x44Logo.scale-200.png" />
|
||||
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
|
||||
<Content Include="Assets\StoreLogo.png" />
|
||||
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="App.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<Page Include="Controls\CustomMapIcon.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Controls\LocationSearchIcon.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\MainPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\Search.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
||||
<VisualStudioVersion>14.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" IgnorableNamespaces="uap mp">
|
||||
<Identity Name="391ec110-3db3-422c-b2b4-b488825f7531" Publisher="CN=Microsoft Build" Version="1.0.0.0" />
|
||||
<mp:PhoneIdentity PhoneProductId="391ec110-3db3-422c-b2b4-b488825f7531" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
|
||||
<Properties>
|
||||
<DisplayName>JobSearch</DisplayName>
|
||||
<PublisherDisplayName>MatthewBa</PublisherDisplayName>
|
||||
<Logo>Assets\StoreLogo.png</Logo>
|
||||
</Properties>
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
|
||||
</Dependencies>
|
||||
<Resources>
|
||||
<Resource Language="x-generate" />
|
||||
</Resources>
|
||||
<Applications>
|
||||
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="JobSearch.App">
|
||||
<uap:VisualElements DisplayName="//Build/" Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Square44x44Logo.png" Description="//Build/" BackgroundColor="transparent">
|
||||
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png">
|
||||
</uap:DefaultTile>
|
||||
<uap:SplashScreen Image="Assets\SplashScreen.png" />
|
||||
</uap:VisualElements>
|
||||
</Application>
|
||||
</Applications>
|
||||
<Capabilities>
|
||||
<Capability Name="internetClient" />
|
||||
</Capabilities>
|
||||
</Package>
|
|
@ -0,0 +1,942 @@
|
|||
using System;
|
||||
|
||||
#pragma warning disable 1591
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
||||
// ReSharper disable IntroduceOptionalParameters.Global
|
||||
// ReSharper disable MemberCanBeProtected.Global
|
||||
// ReSharper disable InconsistentNaming
|
||||
// ReSharper disable CheckNamespace
|
||||
|
||||
namespace JobSearch.Annotations
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that the value of the marked element could be <c>null</c> sometimes,
|
||||
/// so the check for <c>null</c> is necessary before its usage.
|
||||
/// </summary>
|
||||
/// <example><code>
|
||||
/// [CanBeNull] public object Test() { return null; }
|
||||
/// public void UseTest() {
|
||||
/// var p = Test();
|
||||
/// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException'
|
||||
/// }
|
||||
/// </code></example>
|
||||
[AttributeUsage(
|
||||
AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property |
|
||||
AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event)]
|
||||
public sealed class CanBeNullAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the value of the marked element could never be <c>null</c>.
|
||||
/// </summary>
|
||||
/// <example><code>
|
||||
/// [NotNull] public object Foo() {
|
||||
/// return null; // Warning: Possible 'null' assignment
|
||||
/// }
|
||||
/// </code></example>
|
||||
[AttributeUsage(
|
||||
AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property |
|
||||
AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event)]
|
||||
public sealed class NotNullAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// Can be appplied to symbols of types derived from IEnumerable as well as to symbols of Task
|
||||
/// and Lazy classes to indicate that the value of a collection item, of the Task.Result property
|
||||
/// or of the Lazy.Value property can never be null.
|
||||
/// </summary>
|
||||
[AttributeUsage(
|
||||
AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property |
|
||||
AttributeTargets.Delegate | AttributeTargets.Field)]
|
||||
public sealed class ItemNotNullAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// Can be appplied to symbols of types derived from IEnumerable as well as to symbols of Task
|
||||
/// and Lazy classes to indicate that the value of a collection item, of the Task.Result property
|
||||
/// or of the Lazy.Value property can be null.
|
||||
/// </summary>
|
||||
[AttributeUsage(
|
||||
AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property |
|
||||
AttributeTargets.Delegate | AttributeTargets.Field)]
|
||||
public sealed class ItemCanBeNullAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the marked method builds string by format pattern and (optional) arguments.
|
||||
/// Parameter, which contains format string, should be given in constructor. The format string
|
||||
/// should be in <see cref="string.Format(IFormatProvider,string,object[])"/>-like form.
|
||||
/// </summary>
|
||||
/// <example><code>
|
||||
/// [StringFormatMethod("message")]
|
||||
/// public void ShowError(string message, params object[] args) { /* do something */ }
|
||||
/// public void Foo() {
|
||||
/// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string
|
||||
/// }
|
||||
/// </code></example>
|
||||
[AttributeUsage(
|
||||
AttributeTargets.Constructor | AttributeTargets.Method |
|
||||
AttributeTargets.Property | AttributeTargets.Delegate)]
|
||||
public sealed class StringFormatMethodAttribute : Attribute
|
||||
{
|
||||
/// <param name="formatParameterName">
|
||||
/// Specifies which parameter of an annotated method should be treated as format-string
|
||||
/// </param>
|
||||
public StringFormatMethodAttribute(string formatParameterName)
|
||||
{
|
||||
FormatParameterName = formatParameterName;
|
||||
}
|
||||
|
||||
public string FormatParameterName { get; private set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For a parameter that is expected to be one of the limited set of values.
|
||||
/// Specify fields of which type should be used as values for this parameter.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)]
|
||||
public sealed class ValueProviderAttribute : Attribute
|
||||
{
|
||||
public ValueProviderAttribute(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
[NotNull] public string Name { get; private set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the function argument should be string literal and match one
|
||||
/// of the parameters of the caller function. For example, ReSharper annotates
|
||||
/// the parameter of <see cref="System.ArgumentNullException"/>.
|
||||
/// </summary>
|
||||
/// <example><code>
|
||||
/// public void Foo(string param) {
|
||||
/// if (param == null)
|
||||
/// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol
|
||||
/// }
|
||||
/// </code></example>
|
||||
[AttributeUsage(AttributeTargets.Parameter)]
|
||||
public sealed class InvokerParameterNameAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the method is contained in a type that implements
|
||||
/// <c>System.ComponentModel.INotifyPropertyChanged</c> interface and this method
|
||||
/// is used to notify that some property value changed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The method should be non-static and conform to one of the supported signatures:
|
||||
/// <list>
|
||||
/// <item><c>NotifyChanged(string)</c></item>
|
||||
/// <item><c>NotifyChanged(params string[])</c></item>
|
||||
/// <item><c>NotifyChanged{T}(Expression{Func{T}})</c></item>
|
||||
/// <item><c>NotifyChanged{T,U}(Expression{Func{T,U}})</c></item>
|
||||
/// <item><c>SetProperty{T}(ref T, T, string)</c></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
/// <example><code>
|
||||
/// public class Foo : INotifyPropertyChanged {
|
||||
/// public event PropertyChangedEventHandler PropertyChanged;
|
||||
/// [NotifyPropertyChangedInvocator]
|
||||
/// protected virtual void NotifyChanged(string propertyName) { ... }
|
||||
///
|
||||
/// private string _name;
|
||||
/// public string Name {
|
||||
/// get { return _name; }
|
||||
/// set { _name = value; NotifyChanged("LastName"); /* Warning */ }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// Examples of generated notifications:
|
||||
/// <list>
|
||||
/// <item><c>NotifyChanged("Property")</c></item>
|
||||
/// <item><c>NotifyChanged(() => Property)</c></item>
|
||||
/// <item><c>NotifyChanged((VM x) => x.Property)</c></item>
|
||||
/// <item><c>SetProperty(ref myField, value, "Property")</c></item>
|
||||
/// </list>
|
||||
/// </example>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public sealed class NotifyPropertyChangedInvocatorAttribute : Attribute
|
||||
{
|
||||
public NotifyPropertyChangedInvocatorAttribute() { }
|
||||
public NotifyPropertyChangedInvocatorAttribute(string parameterName)
|
||||
{
|
||||
ParameterName = parameterName;
|
||||
}
|
||||
|
||||
public string ParameterName { get; private set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes dependency between method input and output.
|
||||
/// </summary>
|
||||
/// <syntax>
|
||||
/// <p>Function Definition Table syntax:</p>
|
||||
/// <list>
|
||||
/// <item>FDT ::= FDTRow [;FDTRow]*</item>
|
||||
/// <item>FDTRow ::= Input => Output | Output <= Input</item>
|
||||
/// <item>Input ::= ParameterName: Value [, Input]*</item>
|
||||
/// <item>Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value}</item>
|
||||
/// <item>Value ::= true | false | null | notnull | canbenull</item>
|
||||
/// </list>
|
||||
/// If method has single input parameter, it's name could be omitted.<br/>
|
||||
/// Using <c>halt</c> (or <c>void</c>/<c>nothing</c>, which is the same)
|
||||
/// for method output means that the methos doesn't return normally.<br/>
|
||||
/// <c>canbenull</c> annotation is only applicable for output parameters.<br/>
|
||||
/// You can use multiple <c>[ContractAnnotation]</c> for each FDT row,
|
||||
/// or use single attribute with rows separated by semicolon.<br/>
|
||||
/// </syntax>
|
||||
/// <examples><list>
|
||||
/// <item><code>
|
||||
/// [ContractAnnotation("=> halt")]
|
||||
/// public void TerminationMethod()
|
||||
/// </code></item>
|
||||
/// <item><code>
|
||||
/// [ContractAnnotation("halt <= condition: false")]
|
||||
/// public void Assert(bool condition, string text) // regular assertion method
|
||||
/// </code></item>
|
||||
/// <item><code>
|
||||
/// [ContractAnnotation("s:null => true")]
|
||||
/// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty()
|
||||
/// </code></item>
|
||||
/// <item><code>
|
||||
/// // A method that returns null if the parameter is null,
|
||||
/// // and not null if the parameter is not null
|
||||
/// [ContractAnnotation("null => null; notnull => notnull")]
|
||||
/// public object Transform(object data)
|
||||
/// </code></item>
|
||||
/// <item><code>
|
||||
/// [ContractAnnotation("s:null=>false; =>true,result:notnull; =>false, result:null")]
|
||||
/// public bool TryParse(string s, out Person result)
|
||||
/// </code></item>
|
||||
/// </list></examples>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
||||
public sealed class ContractAnnotationAttribute : Attribute
|
||||
{
|
||||
public ContractAnnotationAttribute([NotNull] string contract)
|
||||
: this(contract, false) { }
|
||||
|
||||
public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates)
|
||||
{
|
||||
Contract = contract;
|
||||
ForceFullStates = forceFullStates;
|
||||
}
|
||||
|
||||
public string Contract { get; private set; }
|
||||
public bool ForceFullStates { get; private set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that marked element should be localized or not.
|
||||
/// </summary>
|
||||
/// <example><code>
|
||||
/// [LocalizationRequiredAttribute(true)]
|
||||
/// public class Foo {
|
||||
/// private string str = "my string"; // Warning: Localizable string
|
||||
/// }
|
||||
/// </code></example>
|
||||
[AttributeUsage(AttributeTargets.All)]
|
||||
public sealed class LocalizationRequiredAttribute : Attribute
|
||||
{
|
||||
public LocalizationRequiredAttribute() : this(true) { }
|
||||
public LocalizationRequiredAttribute(bool required)
|
||||
{
|
||||
Required = required;
|
||||
}
|
||||
|
||||
public bool Required { get; private set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the value of the marked type (or its derivatives)
|
||||
/// cannot be compared using '==' or '!=' operators and <c>Equals()</c>
|
||||
/// should be used instead. However, using '==' or '!=' for comparison
|
||||
/// with <c>null</c> is always permitted.
|
||||
/// </summary>
|
||||
/// <example><code>
|
||||
/// [CannotApplyEqualityOperator]
|
||||
/// class NoEquality { }
|
||||
/// class UsesNoEquality {
|
||||
/// public void Test() {
|
||||
/// var ca1 = new NoEquality();
|
||||
/// var ca2 = new NoEquality();
|
||||
/// if (ca1 != null) { // OK
|
||||
/// bool condition = ca1 == ca2; // Warning
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code></example>
|
||||
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)]
|
||||
public sealed class CannotApplyEqualityOperatorAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// When applied to a target attribute, specifies a requirement for any type marked
|
||||
/// with the target attribute to implement or inherit specific type or types.
|
||||
/// </summary>
|
||||
/// <example><code>
|
||||
/// [BaseTypeRequired(typeof(IComponent)] // Specify requirement
|
||||
/// public class ComponentAttribute : Attribute { }
|
||||
/// [Component] // ComponentAttribute requires implementing IComponent interface
|
||||
/// public class MyComponent : IComponent { }
|
||||
/// </code></example>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
[BaseTypeRequired(typeof(Attribute))]
|
||||
public sealed class BaseTypeRequiredAttribute : Attribute
|
||||
{
|
||||
public BaseTypeRequiredAttribute([NotNull] Type baseType)
|
||||
{
|
||||
BaseType = baseType;
|
||||
}
|
||||
|
||||
[NotNull] public Type BaseType { get; private set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library),
|
||||
/// so this symbol will not be marked as unused (as well as by other usage inspections).
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.All)]
|
||||
public sealed class UsedImplicitlyAttribute : Attribute
|
||||
{
|
||||
public UsedImplicitlyAttribute()
|
||||
: this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { }
|
||||
|
||||
public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags)
|
||||
: this(useKindFlags, ImplicitUseTargetFlags.Default) { }
|
||||
|
||||
public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags)
|
||||
: this(ImplicitUseKindFlags.Default, targetFlags) { }
|
||||
|
||||
public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags)
|
||||
{
|
||||
UseKindFlags = useKindFlags;
|
||||
TargetFlags = targetFlags;
|
||||
}
|
||||
|
||||
public ImplicitUseKindFlags UseKindFlags { get; private set; }
|
||||
public ImplicitUseTargetFlags TargetFlags { get; private set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Should be used on attributes and causes ReSharper to not mark symbols marked with such attributes
|
||||
/// as unused (as well as by other usage inspections)
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter)]
|
||||
public sealed class MeansImplicitUseAttribute : Attribute
|
||||
{
|
||||
public MeansImplicitUseAttribute()
|
||||
: this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { }
|
||||
|
||||
public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags)
|
||||
: this(useKindFlags, ImplicitUseTargetFlags.Default) { }
|
||||
|
||||
public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags)
|
||||
: this(ImplicitUseKindFlags.Default, targetFlags) { }
|
||||
|
||||
public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags)
|
||||
{
|
||||
UseKindFlags = useKindFlags;
|
||||
TargetFlags = targetFlags;
|
||||
}
|
||||
|
||||
[UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; private set; }
|
||||
[UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; private set; }
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ImplicitUseKindFlags
|
||||
{
|
||||
Default = Access | Assign | InstantiatedWithFixedConstructorSignature,
|
||||
/// <summary>Only entity marked with attribute considered used.</summary>
|
||||
Access = 1,
|
||||
/// <summary>Indicates implicit assignment to a member.</summary>
|
||||
Assign = 2,
|
||||
/// <summary>
|
||||
/// Indicates implicit instantiation of a type with fixed constructor signature.
|
||||
/// That means any unused constructor parameters won't be reported as such.
|
||||
/// </summary>
|
||||
InstantiatedWithFixedConstructorSignature = 4,
|
||||
/// <summary>Indicates implicit instantiation of a type.</summary>
|
||||
InstantiatedNoFixedConstructorSignature = 8,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specify what is considered used implicitly when marked
|
||||
/// with <see cref="MeansImplicitUseAttribute"/> or <see cref="UsedImplicitlyAttribute"/>.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ImplicitUseTargetFlags
|
||||
{
|
||||
Default = Itself,
|
||||
Itself = 1,
|
||||
/// <summary>Members of entity marked with attribute are considered used.</summary>
|
||||
Members = 2,
|
||||
/// <summary>Entity marked with attribute and all its members considered used.</summary>
|
||||
WithMembers = Itself | Members
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This attribute is intended to mark publicly available API
|
||||
/// which should not be removed and so is treated as used.
|
||||
/// </summary>
|
||||
[MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)]
|
||||
public sealed class PublicAPIAttribute : Attribute
|
||||
{
|
||||
public PublicAPIAttribute() { }
|
||||
public PublicAPIAttribute([NotNull] string comment)
|
||||
{
|
||||
Comment = comment;
|
||||
}
|
||||
|
||||
public string Comment { get; private set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tells code analysis engine if the parameter is completely handled when the invoked method is on stack.
|
||||
/// If the parameter is a delegate, indicates that delegate is executed while the method is executed.
|
||||
/// If the parameter is an enumerable, indicates that it is enumerated while the method is executed.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter)]
|
||||
public sealed class InstantHandleAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that a method does not make any observable state changes.
|
||||
/// The same as <c>System.Diagnostics.Contracts.PureAttribute</c>.
|
||||
/// </summary>
|
||||
/// <example><code>
|
||||
/// [Pure] private int Multiply(int x, int y) { return x * y; }
|
||||
/// public void Foo() {
|
||||
/// const int a = 2, b = 2;
|
||||
/// Multiply(a, b); // Waring: Return value of pure method is not used
|
||||
/// }
|
||||
/// </code></example>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public sealed class PureAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that a parameter is a path to a file or a folder within a web project.
|
||||
/// Path can be relative or absolute, starting from web root (~).
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter)]
|
||||
public sealed class PathReferenceAttribute : Attribute
|
||||
{
|
||||
public PathReferenceAttribute() { }
|
||||
public PathReferenceAttribute([PathReference] string basePath)
|
||||
{
|
||||
BasePath = basePath;
|
||||
}
|
||||
|
||||
public string BasePath { get; private set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An extension method marked with this attribute is processed by ReSharper code completion
|
||||
/// as a 'Source Template'. When extension method is completed over some expression, it's source code
|
||||
/// is automatically expanded like a template at call site.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Template method body can contain valid source code and/or special comments starting with '$'.
|
||||
/// Text inside these comments is added as source code when the template is applied. Template parameters
|
||||
/// can be used either as additional method parameters or as identifiers wrapped in two '$' signs.
|
||||
/// Use the <see cref="MacroAttribute"/> attribute to specify macros for parameters.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// In this example, the 'forEach' method is a source template available over all values
|
||||
/// of enumerable types, producing ordinary C# 'foreach' statement and placing caret inside block:
|
||||
/// <code>
|
||||
/// [SourceTemplate]
|
||||
/// public static void forEach<T>(this IEnumerable<T> xs) {
|
||||
/// foreach (var x in xs) {
|
||||
/// //$ $END$
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public sealed class SourceTemplateAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// Allows specifying a macro for a parameter of a <see cref="SourceTemplateAttribute">source template</see>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You can apply the attribute on the whole method or on any of its additional parameters. The macro expression
|
||||
/// is defined in the <see cref="MacroAttribute.Expression"/> property. When applied on a method, the target
|
||||
/// template parameter is defined in the <see cref="MacroAttribute.Target"/> property. To apply the macro silently
|
||||
/// for the parameter, set the <see cref="MacroAttribute.Editable"/> property value = -1.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// Applying the attribute on a source template method:
|
||||
/// <code>
|
||||
/// [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")]
|
||||
/// public static void forEach<T>(this IEnumerable<T> collection) {
|
||||
/// foreach (var item in collection) {
|
||||
/// //$ $END$
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// Applying the attribute on a template method parameter:
|
||||
/// <code>
|
||||
/// [SourceTemplate]
|
||||
/// public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) {
|
||||
/// /*$ var $x$Id = "$newguid$" + x.ToString();
|
||||
/// x.DoSomething($x$Id); */
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = true)]
|
||||
public sealed class MacroAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows specifying a macro that will be executed for a <see cref="SourceTemplateAttribute">source template</see>
|
||||
/// parameter when the template is expanded.
|
||||
/// </summary>
|
||||
public string Expression { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the target parameter is used several times in the template, only one occurrence becomes editable;
|
||||
/// other occurrences are changed synchronously. To specify the zero-based index of the editable occurrence,
|
||||
/// use values >= 0. To make the parameter non-editable when the template is expanded, use -1.
|
||||
/// </remarks>>
|
||||
public int Editable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the target parameter of a <see cref="SourceTemplateAttribute">source template</see> if the
|
||||
/// <see cref="MacroAttribute"/> is applied on a template method.
|
||||
/// </summary>
|
||||
public string Target { get; set; }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
|
||||
public sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute
|
||||
{
|
||||
public AspMvcAreaMasterLocationFormatAttribute(string format)
|
||||
{
|
||||
Format = format;
|
||||
}
|
||||
|
||||
public string Format { get; private set; }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
|
||||
public sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute
|
||||
{
|
||||
public AspMvcAreaPartialViewLocationFormatAttribute(string format)
|
||||
{
|
||||
Format = format;
|
||||
}
|
||||
|
||||
public string Format { get; private set; }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
|
||||
public sealed class AspMvcAreaViewLocationFormatAttribute : Attribute
|
||||
{
|
||||
public AspMvcAreaViewLocationFormatAttribute(string format)
|
||||
{
|
||||
Format = format;
|
||||
}
|
||||
|
||||
public string Format { get; private set; }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
|
||||
public sealed class AspMvcMasterLocationFormatAttribute : Attribute
|
||||
{
|
||||
public AspMvcMasterLocationFormatAttribute(string format)
|
||||
{
|
||||
Format = format;
|
||||
}
|
||||
|
||||
public string Format { get; private set; }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
|
||||
public sealed class AspMvcPartialViewLocationFormatAttribute : Attribute
|
||||
{
|
||||
public AspMvcPartialViewLocationFormatAttribute(string format)
|
||||
{
|
||||
Format = format;
|
||||
}
|
||||
|
||||
public string Format { get; private set; }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
|
||||
public sealed class AspMvcViewLocationFormatAttribute : Attribute
|
||||
{
|
||||
public AspMvcViewLocationFormatAttribute(string format)
|
||||
{
|
||||
Format = format;
|
||||
}
|
||||
|
||||
public string Format { get; private set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter
|
||||
/// is an MVC action. If applied to a method, the MVC action name is calculated
|
||||
/// implicitly from the context. Use this attribute for custom wrappers similar to
|
||||
/// <c>System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String)</c>.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
|
||||
public sealed class AspMvcActionAttribute : Attribute
|
||||
{
|
||||
public AspMvcActionAttribute() { }
|
||||
public AspMvcActionAttribute(string anonymousProperty)
|
||||
{
|
||||
AnonymousProperty = anonymousProperty;
|
||||
}
|
||||
|
||||
public string AnonymousProperty { get; private set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC area.
|
||||
/// Use this attribute for custom wrappers similar to
|
||||
/// <c>System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String)</c>.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter)]
|
||||
public sealed class AspMvcAreaAttribute : Attribute
|
||||
{
|
||||
public AspMvcAreaAttribute() { }
|
||||
public AspMvcAreaAttribute(string anonymousProperty)
|
||||
{
|
||||
AnonymousProperty = anonymousProperty;
|
||||
}
|
||||
|
||||
public string AnonymousProperty { get; private set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is
|
||||
/// an MVC controller. If applied to a method, the MVC controller name is calculated
|
||||
/// implicitly from the context. Use this attribute for custom wrappers similar to
|
||||
/// <c>System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String)</c>.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
|
||||
public sealed class AspMvcControllerAttribute : Attribute
|
||||
{
|
||||
public AspMvcControllerAttribute() { }
|
||||
public AspMvcControllerAttribute(string anonymousProperty)
|
||||
{
|
||||
AnonymousProperty = anonymousProperty;
|
||||
}
|
||||
|
||||
public string AnonymousProperty { get; private set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC Master. Use this attribute
|
||||
/// for custom wrappers similar to <c>System.Web.Mvc.Controller.View(String, String)</c>.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter)]
|
||||
public sealed class AspMvcMasterAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC model type. Use this attribute
|
||||
/// for custom wrappers similar to <c>System.Web.Mvc.Controller.View(String, Object)</c>.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter)]
|
||||
public sealed class AspMvcModelTypeAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC
|
||||
/// partial view. If applied to a method, the MVC partial view name is calculated implicitly
|
||||
/// from the context. Use this attribute for custom wrappers similar to
|
||||
/// <c>System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String)</c>.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
|
||||
public sealed class AspMvcPartialViewAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||
public sealed class AspMvcSupressViewErrorAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template.
|
||||
/// Use this attribute for custom wrappers similar to
|
||||
/// <c>System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String)</c>.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter)]
|
||||
public sealed class AspMvcDisplayTemplateAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC editor template.
|
||||
/// Use this attribute for custom wrappers similar to
|
||||
/// <c>System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String)</c>.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter)]
|
||||
public sealed class AspMvcEditorTemplateAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC template.
|
||||
/// Use this attribute for custom wrappers similar to
|
||||
/// <c>System.ComponentModel.DataAnnotations.UIHintAttribute(System.String)</c>.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter)]
|
||||
public sealed class AspMvcTemplateAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter
|
||||
/// is an MVC view. If applied to a method, the MVC view name is calculated implicitly
|
||||
/// from the context. Use this attribute for custom wrappers similar to
|
||||
/// <c>System.Web.Mvc.Controller.View(Object)</c>.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
|
||||
public sealed class AspMvcViewAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// ASP.NET MVC attribute. When applied to a parameter of an attribute,
|
||||
/// indicates that this parameter is an MVC action name.
|
||||
/// </summary>
|
||||
/// <example><code>
|
||||
/// [ActionName("Foo")]
|
||||
/// public ActionResult Login(string returnUrl) {
|
||||
/// ViewBag.ReturnUrl = Url.Action("Foo"); // OK
|
||||
/// return RedirectToAction("Bar"); // Error: Cannot resolve action
|
||||
/// }
|
||||
/// </code></example>
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)]
|
||||
public sealed class AspMvcActionSelectorAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)]
|
||||
public sealed class HtmlElementAttributesAttribute : Attribute
|
||||
{
|
||||
public HtmlElementAttributesAttribute() { }
|
||||
public HtmlElementAttributesAttribute(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public string Name { get; private set; }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public sealed class HtmlAttributeValueAttribute : Attribute
|
||||
{
|
||||
public HtmlAttributeValueAttribute([NotNull] string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
[NotNull] public string Name { get; private set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Razor attribute. Indicates that a parameter or a method is a Razor section.
|
||||
/// Use this attribute for custom wrappers similar to
|
||||
/// <c>System.Web.WebPages.WebPageBase.RenderSection(String)</c>.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
|
||||
public sealed class RazorSectionAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates how method, constructor invocation or property access
|
||||
/// over collection type affects content of the collection.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property)]
|
||||
public sealed class CollectionAccessAttribute : Attribute
|
||||
{
|
||||
public CollectionAccessAttribute(CollectionAccessType collectionAccessType)
|
||||
{
|
||||
CollectionAccessType = collectionAccessType;
|
||||
}
|
||||
|
||||
public CollectionAccessType CollectionAccessType { get; private set; }
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum CollectionAccessType
|
||||
{
|
||||
/// <summary>Method does not use or modify content of the collection.</summary>
|
||||
None = 0,
|
||||
/// <summary>Method only reads content of the collection but does not modify it.</summary>
|
||||
Read = 1,
|
||||
/// <summary>Method can change content of the collection but does not add new elements.</summary>
|
||||
ModifyExistingContent = 2,
|
||||
/// <summary>Method can add new elements to the collection.</summary>
|
||||
UpdatedContent = ModifyExistingContent | 4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the marked method is assertion method, i.e. it halts control flow if
|
||||
/// one of the conditions is satisfied. To set the condition, mark one of the parameters with
|
||||
/// <see cref="AssertionConditionAttribute"/> attribute.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public sealed class AssertionMethodAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the condition parameter of the assertion method. The method itself should be
|
||||
/// marked by <see cref="AssertionMethodAttribute"/> attribute. The mandatory argument of
|
||||
/// the attribute is the assertion type.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter)]
|
||||
public sealed class AssertionConditionAttribute : Attribute
|
||||
{
|
||||
public AssertionConditionAttribute(AssertionConditionType conditionType)
|
||||
{
|
||||
ConditionType = conditionType;
|
||||
}
|
||||
|
||||
public AssertionConditionType ConditionType { get; private set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies assertion type. If the assertion method argument satisfies the condition,
|
||||
/// then the execution continues. Otherwise, execution is assumed to be halted.
|
||||
/// </summary>
|
||||
public enum AssertionConditionType
|
||||
{
|
||||
/// <summary>Marked parameter should be evaluated to true.</summary>
|
||||
IS_TRUE = 0,
|
||||
/// <summary>Marked parameter should be evaluated to false.</summary>
|
||||
IS_FALSE = 1,
|
||||
/// <summary>Marked parameter should be evaluated to null value.</summary>
|
||||
IS_NULL = 2,
|
||||
/// <summary>Marked parameter should be evaluated to not null value.</summary>
|
||||
IS_NOT_NULL = 3,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the marked method unconditionally terminates control flow execution.
|
||||
/// For example, it could unconditionally throw exception.
|
||||
/// </summary>
|
||||
[Obsolete("Use [ContractAnnotation('=> halt')] instead")]
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public sealed class TerminatesProgramAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that method is pure LINQ method, with postponed enumeration (like Enumerable.Select,
|
||||
/// .Where). This annotation allows inference of [InstantHandle] annotation for parameters
|
||||
/// of delegate type by analyzing LINQ method chains.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public sealed class LinqTunnelAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that IEnumerable, passed as parameter, is not enumerated.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter)]
|
||||
public sealed class NoEnumerationAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that parameter is regular expression pattern.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter)]
|
||||
public sealed class RegexPatternAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// XAML attribute. Indicates the type that has <c>ItemsSource</c> property and should be treated
|
||||
/// as <c>ItemsControl</c>-derived type, to enable inner items <c>DataContext</c> type resolve.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class XamlItemsControlAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// XAML attibute. Indicates the property of some <c>BindingBase</c>-derived type, that
|
||||
/// is used to bind some item of <c>ItemsControl</c>-derived type. This annotation will
|
||||
/// enable the <c>DataContext</c> type resolve for XAML bindings for such properties.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Property should have the tree ancestor of the <c>ItemsControl</c> type or
|
||||
/// marked with the <see cref="XamlItemsControlAttribute"/> attribute.
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public sealed class XamlItemBindingOfItemsControlAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
public sealed class AspChildControlTypeAttribute : Attribute
|
||||
{
|
||||
public AspChildControlTypeAttribute(string tagName, Type controlType)
|
||||
{
|
||||
TagName = tagName;
|
||||
ControlType = controlType;
|
||||
}
|
||||
|
||||
public string TagName { get; private set; }
|
||||
public Type ControlType { get; private set; }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)]
|
||||
public sealed class AspDataFieldAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)]
|
||||
public sealed class AspDataFieldsAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public sealed class AspMethodPropertyAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
public sealed class AspRequiredAttributeAttribute : Attribute
|
||||
{
|
||||
public AspRequiredAttributeAttribute([NotNull] string attribute)
|
||||
{
|
||||
Attribute = attribute;
|
||||
}
|
||||
|
||||
public string Attribute { get; private set; }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public sealed class AspTypePropertyAttribute : Attribute
|
||||
{
|
||||
public bool CreateConstructorReferences { get; private set; }
|
||||
|
||||
public AspTypePropertyAttribute(bool createConstructorReferences)
|
||||
{
|
||||
CreateConstructorReferences = createConstructorReferences;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
|
||||
public sealed class RazorImportNamespaceAttribute : Attribute
|
||||
{
|
||||
public RazorImportNamespaceAttribute(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public string Name { get; private set; }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
|
||||
public sealed class RazorInjectionAttribute : Attribute
|
||||
{
|
||||
public RazorInjectionAttribute(string type, string fieldName)
|
||||
{
|
||||
Type = type;
|
||||
FieldName = fieldName;
|
||||
}
|
||||
|
||||
public string Type { get; private set; }
|
||||
public string FieldName { get; private set; }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public sealed class RazorHelperCommonAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public sealed class RazorLayoutAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public sealed class RazorWriteLiteralMethodAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public sealed class RazorWriteMethodAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Parameter)]
|
||||
public sealed class RazorWriteMethodParameterAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// Prevents the Member Reordering feature from tossing members of the marked class.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The attribute must be mentioned in your member reordering patterns
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.All)]
|
||||
public sealed class NoReorder : Attribute { }
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("JobSearch")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("JobSearch")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: ComVisible(false)]
|
|
@ -0,0 +1,31 @@
|
|||
<!--
|
||||
This file contains Runtime Directives used by .NET Native. The defaults here are suitable for most
|
||||
developers. However, you can modify these parameters to modify the behavior of the .NET Native
|
||||
optimizer.
|
||||
|
||||
Runtime Directives are documented at http://go.microsoft.com/fwlink/?LinkID=391919
|
||||
|
||||
To fully enable reflection for App1.MyClass and all of its public/private members
|
||||
<Type Name="App1.MyClass" Dynamic="Required All"/>
|
||||
|
||||
To enable dynamic creation of the specific instantiation of AppClass<T> over System.Int32
|
||||
<TypeInstantiation Name="App1.AppClass" Arguments="System.Int32" Activate="Required Public" />
|
||||
|
||||
Using the Namespace directive to apply reflection policy to all the types in a particular namespace
|
||||
<Namespace Name="DataClasses.ViewModels" Seralize="All" />
|
||||
-->
|
||||
|
||||
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
|
||||
<Application>
|
||||
<!--
|
||||
An Assembly element with Name="*Application*" applies to all assemblies in
|
||||
the application package. The asterisks are not wildcards.
|
||||
-->
|
||||
<Assembly Name="*Application*" Dynamic="Required All" />
|
||||
|
||||
|
||||
<!-- Add your application specific runtime directives here. -->
|
||||
|
||||
|
||||
</Application>
|
||||
</Directives>
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Search.Models;
|
||||
|
||||
namespace JobSearch.SearchModels
|
||||
{
|
||||
public class FacetGroup
|
||||
{
|
||||
public string FacetName { get; set; }
|
||||
public string FacetDisplayName { get; set; }
|
||||
public IList<FacetSelection> FacetValues { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using JobSearch.Annotations;
|
||||
|
||||
namespace JobSearch.SearchModels
|
||||
{
|
||||
public class FacetSelection : INotifyPropertyChanged
|
||||
{
|
||||
public string FacetValue { get; set; }
|
||||
|
||||
public long? FacetCount { get; set; }
|
||||
|
||||
public string FacetDisplay => ToString();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return FacetValue + (FacetCount.HasValue ? $" ({FacetCount.Value})" : String.Empty);
|
||||
}
|
||||
|
||||
|
||||
private bool? _isSelected;
|
||||
|
||||
public bool? IsSelected
|
||||
{
|
||||
get { return _isSelected; }
|
||||
set
|
||||
{
|
||||
_isSelected = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
[NotifyPropertyChangedInvocator]
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Search.Models;
|
||||
using Microsoft.Spatial;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace JobSearch.SearchModels
|
||||
{
|
||||
[SerializePropertyNamesAsCamelCase]
|
||||
public class JobResult
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
[JsonProperty("job_id")]
|
||||
public double? JobId { get; set; }
|
||||
public string Agency { get; set; }
|
||||
[JsonProperty("posting_type")]
|
||||
public string PostingType { get; set; }
|
||||
[JsonProperty("num_of_positions")]
|
||||
public string NumOfPositions { get; set; }
|
||||
[JsonProperty("business_title")]
|
||||
public string BusinessTitle { get; set; }
|
||||
[JsonProperty("civil_service_title")]
|
||||
public string CivilServiceTitle { get; set; }
|
||||
[JsonProperty("title_code_no")]
|
||||
public string TitleCodeNo { get; set; }
|
||||
public string Level { get; set; }
|
||||
[JsonProperty("salary_range_from")]
|
||||
public int SalaryRangeFrom { get; set; }
|
||||
[JsonProperty("salary_range_to")]
|
||||
public int SalaryRangeTo { get; set; }
|
||||
[JsonProperty("salary_frequency")]
|
||||
public string SalaryFrequency { get; set; }
|
||||
[JsonProperty("work_location")]
|
||||
public string WorkLocation { get; set; }
|
||||
[JsonProperty("division_work_unit")]
|
||||
public string DivisionWorkUnit { get; set; }
|
||||
[JsonProperty("job_description")]
|
||||
public string JobDescription { get; set; }
|
||||
[JsonProperty("minimum_qual_requirements")]
|
||||
public string MinimumQualRequirements { get; set; }
|
||||
[JsonProperty("preferred_skills")]
|
||||
public string PreferredSkills { get; set; }
|
||||
[JsonProperty("additional_information")]
|
||||
public string AdditionalInformation { get; set; }
|
||||
[JsonProperty("to_apply")]
|
||||
public string ToApply { get; set; }
|
||||
[JsonProperty("hours_per_shift")]
|
||||
public string HoursPerShift { get; set; }
|
||||
[JsonProperty("recruitment_contact")]
|
||||
public string RecruitmentContact { get; set; }
|
||||
[JsonProperty("residency_requirement")]
|
||||
public string ResidencyRequirement { get; set; }
|
||||
[JsonProperty("posting_date")]
|
||||
public DateTime? PostingDate { get; set; }
|
||||
[JsonProperty("post_until")]
|
||||
public DateTime? PostUntil { get; set; }
|
||||
[JsonProperty("posting_updated")]
|
||||
public DateTime? PostingUpdated { get; set; }
|
||||
[JsonProperty("process_date")]
|
||||
public DateTime? ProcessDate { get; set; }
|
||||
[JsonProperty("geo_location")]
|
||||
public GeographyPoint GeoLocation { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Devices.Geolocation;
|
||||
|
||||
namespace JobSearch.SearchModels
|
||||
{
|
||||
public class PositionDistanceSearch
|
||||
{
|
||||
public Geopoint GeoPoint { get; set; }
|
||||
public int Radius { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using JobSearch.SearchModels;
|
||||
using Microsoft.Azure.Search;
|
||||
using Microsoft.Azure.Search.Models;
|
||||
|
||||
namespace JobSearch.Services
|
||||
{
|
||||
public class JobSearchService
|
||||
{
|
||||
private readonly string _apiKey;
|
||||
private readonly string _searchUrl;
|
||||
private readonly string _indexName;
|
||||
|
||||
|
||||
public static Dictionary<string, string> FacetDefinitions = new Dictionary<string, string>()
|
||||
{
|
||||
{"agency", "Agency"},
|
||||
{"posting_type", "Internal/External"},
|
||||
{"civil_service_title", "Common Job Title"}
|
||||
};
|
||||
|
||||
public JobSearchService()
|
||||
{
|
||||
_searchUrl = "azsearch-build";
|
||||
_apiKey = "32D0CF6E6A03CDBD783DD3F6B390D735";
|
||||
_indexName = "jobs";
|
||||
}
|
||||
|
||||
private SearchIndexClient GetClient()
|
||||
{
|
||||
return new SearchIndexClient(_searchUrl, _indexName, new SearchCredentials(_apiKey));
|
||||
}
|
||||
|
||||
public async Task<List<string>> ExecuteSuggest(string query)
|
||||
{
|
||||
//TO DO - Place holder for suggest
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
public async Task<DocumentSearchResult<JobResult>> ExecuteSearch(string query, List<FacetGroup> facets = null, PositionDistanceSearch geoSearch = null)
|
||||
{
|
||||
var searchParameters = new SearchParameters()
|
||||
{
|
||||
QueryType = QueryType.Full,
|
||||
SearchMode = SearchMode.All,
|
||||
};
|
||||
using (var indexClient = GetClient())
|
||||
{
|
||||
return await indexClient.Documents.SearchAsync<JobResult>(query, searchParameters);
|
||||
}
|
||||
}
|
||||
|
||||
private string CreateFilter(List<FacetGroup> facets, PositionDistanceSearch geoSearch = null)
|
||||
{
|
||||
if (facets != null)
|
||||
{
|
||||
var query = new StringBuilder();
|
||||
var groupCount = facets.Count(e => e.FacetValues.Any(f => f.IsSelected ?? false));
|
||||
var groupCounter = 0;
|
||||
|
||||
foreach (var facet in facets)
|
||||
{
|
||||
var selectedValues = facet.FacetValues.Where(e => e.IsSelected ?? false).ToArray();
|
||||
;
|
||||
if (selectedValues.Length > 0)
|
||||
{
|
||||
int counter = 0;
|
||||
query.Append("(");
|
||||
foreach (var facetSelection in selectedValues)
|
||||
{
|
||||
query.Append($"{facet.FacetName} eq '{facetSelection.FacetValue}'");
|
||||
if (counter < selectedValues.Length - 1)
|
||||
{
|
||||
query.Append(" or ");
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
query.Append(")");
|
||||
if (groupCounter < groupCount - 1)
|
||||
{
|
||||
query.Append(" and ");
|
||||
}
|
||||
groupCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
return query.ToString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,275 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Windows.Devices.Geolocation;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Maps;
|
||||
using JobSearch.Annotations;
|
||||
using JobSearch.Controls;
|
||||
using JobSearch.Extensions;
|
||||
using JobSearch.SearchModels;
|
||||
using JobSearch.Services;
|
||||
using Microsoft.Azure.Search.Models;
|
||||
|
||||
namespace JobSearch.ViewModels
|
||||
{
|
||||
public class SearchViewModel : INotifyPropertyChanged
|
||||
{
|
||||
private readonly JobSearchService _searchService;
|
||||
private readonly MapControl _map;
|
||||
private readonly LocationSearchIcon _searchLocationIcon;
|
||||
|
||||
public ObservableCollection<JobResult> SearchResults { get; }
|
||||
public ObservableCollection<FacetGroup> SearchFacets { get; }
|
||||
|
||||
public int[] KilometersSearch => new[] { 1, 2, 5, 10, 15, 20, 25, 50, 100, 150 };
|
||||
|
||||
public Visibility ShowBusyRing => IsBusy ? Visibility.Visible : Visibility.Collapsed;
|
||||
public Visibility ShowContent => IsBusy ? Visibility.Collapsed : Visibility.Visible;
|
||||
|
||||
private bool _isBusy = false;
|
||||
public bool IsBusy
|
||||
{
|
||||
get { return _isBusy; }
|
||||
set
|
||||
{
|
||||
_isBusy = value;
|
||||
OnPropertyChanged(nameof(IsBusy));
|
||||
OnPropertyChanged(nameof(ShowBusyRing));
|
||||
OnPropertyChanged(nameof(ShowContent));
|
||||
}
|
||||
}
|
||||
|
||||
private int _kilometersSelected = 1;
|
||||
public int KilometersSelected
|
||||
{
|
||||
get { return _kilometersSelected; }
|
||||
set
|
||||
{
|
||||
_kilometersSelected = value;
|
||||
OnPropertyChanged(nameof(KilometersSelected));
|
||||
}
|
||||
}
|
||||
|
||||
private string _searchQuery = "";
|
||||
public string SearchQuery
|
||||
{
|
||||
get { return _searchQuery; }
|
||||
set
|
||||
{
|
||||
_searchQuery = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public SearchViewModel(MapControl mapControl)
|
||||
{
|
||||
_searchService = new JobSearchService();
|
||||
SearchResults = new ObservableCollection<JobResult>();
|
||||
SearchFacets = new ObservableCollection<FacetGroup>();
|
||||
_map = mapControl;
|
||||
_searchLocationIcon = new LocationSearchIcon();
|
||||
}
|
||||
|
||||
public async Task ExecuteFilter()
|
||||
{
|
||||
IsBusy = true;
|
||||
try
|
||||
{
|
||||
var results = await _searchService.ExecuteSearch(SearchQuery, SearchFacets.ToList());
|
||||
await UpdateSearchResults(results);
|
||||
UpdateFacets(results, true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ExecuteGeoFilter()
|
||||
{
|
||||
IsBusy = true;
|
||||
try
|
||||
{
|
||||
var results = await _searchService.ExecuteSearch(SearchQuery, SearchFacets.ToList(), new PositionDistanceSearch() { GeoPoint = _searchLocationIcon.GeoPoint, Radius = KilometersSelected });
|
||||
await UpdateSearchResults(results);
|
||||
UpdateFacets(results, true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
public Visibility ShowFacets => (SearchResults.Count > 0 && SearchFacets.Count > 0) ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
public async Task<List<string>> ExecuteSuggest(string currentText)
|
||||
{
|
||||
return await _searchService.ExecuteSuggest(currentText);
|
||||
}
|
||||
|
||||
public async void ExecuteSearch()
|
||||
{
|
||||
IsBusy = true;
|
||||
try
|
||||
{
|
||||
var results = await _searchService.ExecuteSearch(SearchQuery, null);
|
||||
await UpdateSearchResults(results);
|
||||
UpdateFacets(results);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateSearchResults(DocumentSearchResult<JobResult> results)
|
||||
{
|
||||
if (results.Results != null)
|
||||
{
|
||||
SearchResults.Clear();
|
||||
foreach (var result in results.Results)
|
||||
{
|
||||
SearchResults.Add(result.Document);
|
||||
}
|
||||
_map.Children.Clear();
|
||||
var locations = new List<Geopoint>();
|
||||
|
||||
foreach (var result in SearchResults)
|
||||
{
|
||||
locations.Add(await AddPin(result.GeoLocation.Latitude, result.GeoLocation.Longitude));
|
||||
}
|
||||
if (_searchLocationIcon?.GeoPoint != null)
|
||||
{
|
||||
await UpdateUserLocation(_searchLocationIcon.GeoPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateFacets(DocumentSearchResult<JobResult> results, bool bindExistingFacets = false)
|
||||
{
|
||||
if (results.Facets != null)
|
||||
{
|
||||
var existingSelectedFacets =
|
||||
SearchFacets.Select(e => new FacetGroup()
|
||||
{
|
||||
FacetName = e.FacetName,
|
||||
FacetValues = e.FacetValues.Where(f => f.IsSelected.HasValue && f.IsSelected.Value).ToList()
|
||||
}).ToList();
|
||||
|
||||
SearchFacets.Clear();
|
||||
foreach (var result in results.Facets)
|
||||
{
|
||||
SearchFacets.Add(new FacetGroup()
|
||||
{
|
||||
FacetName = result.Key,
|
||||
FacetDisplayName = JobSearchService.FacetDefinitions.ContainsKey(result.Key) ? JobSearchService.FacetDefinitions[result.Key] : result.Key,
|
||||
FacetValues =
|
||||
result.Value.Select(e => new FacetSelection() { FacetValue = e.Value.ToString(), IsSelected = bindExistingFacets && existingSelectedFacets.Any(ef => ef.FacetName == result.Key && ef.FacetValues.Any(fv => fv.FacetValue == e.Value.ToString())), FacetCount = e.Count })
|
||||
.ToList()
|
||||
});
|
||||
}
|
||||
}
|
||||
OnPropertyChanged(nameof(ShowFacets));
|
||||
}
|
||||
|
||||
private async Task UpdateElementLocation(DependencyObject dependentObject, Geopoint point, Point anchorPoint)
|
||||
{
|
||||
if (dependentObject != null)
|
||||
{
|
||||
await _map.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
MapControl.SetLocation(dependentObject, point);
|
||||
MapControl.SetNormalizedAnchorPoint(dependentObject, anchorPoint);
|
||||
});
|
||||
}
|
||||
}
|
||||
private async Task<Geopoint> AddPin(double latitude, double longitude)
|
||||
{
|
||||
var location = new Geopoint(new BasicGeoposition { Longitude = longitude, Latitude = latitude });
|
||||
var icon = new CustomMapIcon();
|
||||
_map.Children.Add(icon);
|
||||
await UpdateElementLocation(icon, location, new Point(0.5, 1.0));
|
||||
return location;
|
||||
}
|
||||
|
||||
private async Task ScopeToBoundingBox(List<Geopoint> coordinateSet)
|
||||
{
|
||||
var boundingBox = CreateBoundingBoxFromCoordiateSet(coordinateSet);
|
||||
await _map.TrySetViewBoundsAsync(boundingBox, new Thickness(25), MapAnimationKind.None);
|
||||
}
|
||||
|
||||
private GeoboundingBox CreateBoundingBoxFromCoordiateSet(List<Geopoint> coordinateSet)
|
||||
{
|
||||
var first = coordinateSet.First();
|
||||
var northWestCorner = new BasicGeoposition();
|
||||
var southEastCorner = new BasicGeoposition();
|
||||
for (int i = 1; i < coordinateSet.Count; i++)
|
||||
{
|
||||
var coordinate = coordinateSet[i];
|
||||
if (coordinate.Position.Latitude > northWestCorner.Latitude)
|
||||
northWestCorner.Latitude = coordinate.Position.Latitude;
|
||||
if (coordinate.Position.Latitude < southEastCorner.Latitude)
|
||||
southEastCorner.Latitude = coordinate.Position.Latitude;
|
||||
|
||||
if (coordinate.Position.Longitude < northWestCorner.Longitude)
|
||||
northWestCorner.Longitude = coordinate.Position.Longitude;
|
||||
if (coordinate.Position.Longitude > southEastCorner.Longitude)
|
||||
southEastCorner.Longitude = coordinate.Position.Longitude;
|
||||
}
|
||||
var box = new GeoboundingBox(northWestCorner, southEastCorner);
|
||||
return box;
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
[NotifyPropertyChangedInvocator]
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
public async Task UpdateUserLocation(Geopoint position)
|
||||
{
|
||||
if (!_map.Children.Contains(_searchLocationIcon))
|
||||
{
|
||||
_map.Children.Add(_searchLocationIcon);
|
||||
}
|
||||
_searchLocationIcon.GeoPoint = position;
|
||||
await UpdateElementLocation(_searchLocationIcon, position, new Point(0.5, 0.5));
|
||||
UpdateSearchRadius();
|
||||
}
|
||||
|
||||
public void UpdateSearchRadius()
|
||||
{
|
||||
if (_searchLocationIcon?.GeoPoint != null)
|
||||
{
|
||||
var mapPolygon = new MapPolygon()
|
||||
{
|
||||
Path = new Geopath(_searchLocationIcon.GeoPoint.GetCirclePoints(KilometersSelected * 1000)),
|
||||
ZIndex = -1,
|
||||
FillColor = Color.FromArgb(128, 128, 128, 128),
|
||||
StrokeThickness = 0
|
||||
};
|
||||
foreach (var element in _map.MapElements.Where(e => e.ZIndex == -1).ToList())
|
||||
{
|
||||
_map.MapElements.Remove(element);
|
||||
}
|
||||
_map.MapElements.Add(mapPolygon);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<Page
|
||||
x:Class="JobSearch.Views.MainPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:JobSearch.Views"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Frame x:Name="ContentFrame"/>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.Services.Maps;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
|
||||
|
||||
namespace JobSearch.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class MainPage : Page
|
||||
{
|
||||
public MainPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
MapService.ServiceToken =
|
||||
"FuOaXv11dk804VQw1Wl9~T8VVb75ai9-kw-3kZMH8Gw~An-DCRJDKGE5zRC5WieBG_kFMNJzs7NNZTn9aNdmPkKKEj2vq-ILMJ4QvmNZaR2o";
|
||||
ContentFrame.Navigate(typeof(Search));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
<Page
|
||||
x:Class="JobSearch.Views.Search"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:JobSearch.Views"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="using:JobSearch.ViewModels"
|
||||
xmlns:models="using:JobSearch.SearchModels"
|
||||
xmlns:maps="using:Windows.UI.Xaml.Controls.Maps"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid Margin="10" Grid.Row="0" Grid.Column="0" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0" Grid.Column="0" Margin="10">
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<AutoSuggestBox KeyUp="SearchBox_OnKeyUp" x:Name="SearchBox" Grid.Column="0" Text="{x:Bind Searcher.SearchQuery, Mode=TwoWay}" TextChanged="SearchBox_OnTextChanged" SuggestionChosen="SearchBox_OnSuggestionChosen" Margin="2"/>
|
||||
|
||||
<Button Grid.Column="1" Name="searchButton" Content="Search" Click="{x:Bind Searcher.ExecuteSearch}" VerticalAlignment="Bottom" Margin="2"></Button>
|
||||
|
||||
</Grid>
|
||||
|
||||
<Pivot Grid.Row="1">
|
||||
<PivotItem Header="Search Results">
|
||||
<Grid Margin="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid Grid.Column="0" Visibility="{x:Bind Searcher.ShowFacets, Mode=OneWay}" Margin="10">
|
||||
<ScrollViewer VerticalScrollMode="Auto">
|
||||
<ItemsControl Margin="10" ItemsSource="{x:Bind Searcher.SearchFacets}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:FacetGroup">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0" Text="{x:Bind FacetDisplayName}"></TextBlock>
|
||||
<ItemsControl Margin="10" Grid.Row="1" Grid.Column="0" ItemsSource="{x:Bind FacetValues}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:FacetSelection">
|
||||
<CheckBox Content="{x:Bind FacetDisplay}" Loaded="FrameworkElement_OnLoaded" Unchecked="ToggleButton_OnUnchecked" Checked="ToggleButton_OnChecked" FontSize="12"></CheckBox>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Vertical"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
</ItemsControl>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
<ListView Margin="10" Grid.Column="1" ItemsSource="{x:Bind Searcher.SearchResults}">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:JobResult">
|
||||
<Grid Margin="10" >
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Margin="2" Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Margin="2" Text="Job Title : " FontSize="12"></TextBlock>
|
||||
<TextBlock Grid.Column="1" Margin="2" Text="{x:Bind BusinessTitle}" FontSize="12"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Margin="2" Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Margin="2" Text="Agency : " FontSize="12"></TextBlock>
|
||||
<TextBlock Grid.Column="1" Margin="2" Text="{x:Bind Agency}" FontSize="12" />
|
||||
</Grid>
|
||||
<Grid Margin="2" Grid.Row="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Margin="2" Text="Salary From : " FontSize="12"></TextBlock>
|
||||
<TextBlock Grid.Column="1" Margin="2" Text="{x:Bind SalaryRangeFrom}" FontSize="12"></TextBlock>
|
||||
</Grid>
|
||||
<Grid Margin="2" Grid.Row="3">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Margin="2" Text="Division / Work Unit : " FontSize="12"></TextBlock>
|
||||
<TextBlock Grid.Column="1" Margin="2" Text="{x:Bind DivisionWorkUnit}" FontSize="12"></TextBlock>
|
||||
</Grid>
|
||||
<Grid Margin="2" Grid.Row="4">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Margin="2" Text="Job Description : " FontSize="10"></TextBlock>
|
||||
<TextBlock Grid.Column="1" Margin="2" Text="{x:Bind JobDescription}" TextWrapping="WrapWholeWords" FontSize="10"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</PivotItem>
|
||||
<PivotItem Header="Map Results">
|
||||
<Grid Margin="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid Grid.Column="0">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock Margin="2" Text="Search Radius (in KM)" FontSize="12"></TextBlock>
|
||||
<ComboBox Margin="2" ItemsSource="{x:Bind Searcher.KilometersSearch}" SelectionChanged="Selector_OnSelectionChanged" SelectedValuePath="{x:Bind Searcher.KilometersSelected, Mode=OneTime}" FontSize="12"></ComboBox>
|
||||
<Button Margin="2" Content="Filter Results" Click="{x:Bind Searcher.ExecuteGeoFilter}" FontSize="12" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<maps:MapControl Grid.Column="1" x:Name="MapControl" IsTapEnabled="True"
|
||||
MapTapped="MapControl_OnMapTapped"
|
||||
MapDoubleTapped="MapControl_OnMapDoubleTapped" />
|
||||
</Grid>
|
||||
</PivotItem>
|
||||
</Pivot>
|
||||
</Grid>
|
||||
<Grid Grid.Row="0" Grid.Column="0" Visibility="{x:Bind Searcher.ShowBusyRing, Mode=OneWay}" Opacity="20" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<ProgressRing Height="200" Width="200" VerticalAlignment="Center" HorizontalAlignment="Center" IsActive="True" ></ProgressRing>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,132 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.Media.Audio;
|
||||
using Windows.System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Maps;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using JobSearch.SearchModels;
|
||||
using JobSearch.ViewModels;
|
||||
|
||||
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
|
||||
|
||||
namespace JobSearch.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class Search : Page
|
||||
{
|
||||
public Search()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
this.Searcher = new SearchViewModel(MapControl);
|
||||
}
|
||||
|
||||
public SearchViewModel Searcher
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
private async void SearchBox_OnTextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
|
||||
{
|
||||
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
|
||||
{
|
||||
if (sender.Text.Length > 2)
|
||||
{
|
||||
sender.ItemsSource = await Searcher.ExecuteSuggest(sender.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SearchBox_OnSuggestionChosen(AutoSuggestBox sender, AutoSuggestBoxSuggestionChosenEventArgs args)
|
||||
{
|
||||
sender.Text = $"\"{args.SelectedItem}\"";
|
||||
Searcher.ExecuteSearch();
|
||||
}
|
||||
|
||||
private void SearchBox_OnKeyUp(object sender, KeyRoutedEventArgs e)
|
||||
{
|
||||
if (e.Key == VirtualKey.Enter)
|
||||
{
|
||||
Searcher.ExecuteSearch();
|
||||
}
|
||||
}
|
||||
|
||||
private async void MapControl_OnMapTapped(object sender, MapInputEventArgs args)
|
||||
{
|
||||
await Searcher.UpdateUserLocation(args.Location);
|
||||
}
|
||||
|
||||
private async void MapControl_OnMapDoubleTapped(object sender, MapInputEventArgs args)
|
||||
{
|
||||
await Searcher.UpdateUserLocation(args.Location);
|
||||
}
|
||||
|
||||
private void Selector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
Searcher.KilometersSelected = (int) e.AddedItems[0];
|
||||
Searcher.UpdateSearchRadius();
|
||||
}
|
||||
|
||||
private void ToggleButton_OnChecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!_stopBinding)
|
||||
{
|
||||
var checkBox = sender as CheckBox;
|
||||
var facet = checkBox?.DataContext as FacetSelection;
|
||||
if (facet != null)
|
||||
{
|
||||
facet.IsSelected = true;
|
||||
Searcher.ExecuteFilter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ToggleButton_OnUnchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!_stopBinding)
|
||||
{
|
||||
var checkBox = sender as CheckBox;
|
||||
var facet = checkBox?.DataContext as FacetSelection;
|
||||
if (facet != null)
|
||||
{
|
||||
facet.IsSelected = false;
|
||||
Searcher.ExecuteFilter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _stopBinding = false;
|
||||
|
||||
private void FrameworkElement_OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_stopBinding = true;
|
||||
try
|
||||
{
|
||||
var checkBox = sender as CheckBox;
|
||||
var facet = checkBox?.DataContext as FacetSelection;
|
||||
if (facet != null)
|
||||
{
|
||||
checkBox.IsChecked = facet.IsSelected;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_stopBinding = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"Microsoft.Azure.Search": "1.1.0",
|
||||
"Microsoft.NETCore.UniversalWindowsPlatform": "5.0.0"
|
||||
},
|
||||
"frameworks": {
|
||||
"uap10.0": {}
|
||||
},
|
||||
"runtimes": {
|
||||
"win10-arm": {},
|
||||
"win10-arm-aot": {},
|
||||
"win10-x86": {},
|
||||
"win10-x86-aot": {},
|
||||
"win10-x64": {},
|
||||
"win10-x64-aot": {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
Document DB
|
||||
======
|
||||
|
||||
See the hands-on lab [here](hands-on-lab.md)
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.24627.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocumentDBSeeder", "DocumentDBSeeder\DocumentDBSeeder.csproj", "{F13AD473-3F20-4ADA-9294-C12D029432A8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{F13AD473-3F20-4ADA-9294-C12D029432A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F13AD473-3F20-4ADA-9294-C12D029432A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F13AD473-3F20-4ADA-9294-C12D029432A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F13AD473-3F20-4ADA-9294-C12D029432A8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
|
||||
</startup>
|
||||
<appSettings>
|
||||
<add key="endpoint" value="https://tj-build2016.documents.azure.com:443/" />
|
||||
<add key="key" value="zjWiFXg++5TwW0zqK+Leo6zKdHSfszI+u9MaDnpiGUujQI6eQbrY4YJrtV7ExUXxPew+BWdMwJuiCo8m8/BP4w==" />
|
||||
<add key="database" value="metrics" />
|
||||
<add key="collection" value="livelogs" />
|
||||
</appSettings>
|
||||
</configuration>
|
|
@ -0,0 +1,66 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{F13AD473-3F20-4ADA-9294-C12D029432A8}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>DocumentDBSeeder</RootNamespace>
|
||||
<AssemblyName>DocumentDBSeeder</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Azure.Documents.Client">
|
||||
<HintPath>..\packages\Microsoft.Azure.DocumentDB.1.5.2\lib\net40\Microsoft.Azure.Documents.Client.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>..\packages\Newtonsoft.Json.5.0.8\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,147 @@
|
|||
using System;
|
||||
using System.Configuration;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Documents;
|
||||
using Microsoft.Azure.Documents.Client;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace DocumentDBSeeder
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
private static DocumentClient _client;
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
var endpoint = ConfigurationManager.AppSettings["endpoint"];
|
||||
var masterKey = ConfigurationManager.AppSettings["key"];
|
||||
var db = ConfigurationManager.AppSettings["database"];
|
||||
var coll = ConfigurationManager.AppSettings["collection"];
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
using (_client = new DocumentClient(new Uri(endpoint), masterKey, new ConnectionPolicy()))
|
||||
{
|
||||
RunUpdateAsync(coll, db).Wait();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// If the Exception is a DocumentClientException, the "StatusCode" value might help identity
|
||||
// the source of the problem.
|
||||
Console.WriteLine("Perf Counter runner failed with exception:{0}", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Console.WriteLine("Perf Counter runner emission ends.");
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
}
|
||||
private static async Task RunUpdateAsync(string collection, string database)
|
||||
{
|
||||
Uri collUri = UriFactory.CreateDocumentCollectionUri(database, collection);
|
||||
|
||||
try
|
||||
{
|
||||
await _client.ReadDocumentCollectionAsync(collUri);
|
||||
}
|
||||
catch (DocumentClientException ex)
|
||||
{
|
||||
//Simply return false if this is the error. Else throw it.
|
||||
if (ex.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
await
|
||||
_client.CreateDocumentCollectionAsync(UriFactory.CreateDatabaseUri(database),
|
||||
new DocumentCollection() { Id = collection });
|
||||
}
|
||||
}
|
||||
|
||||
var categoryCounters = new[]
|
||||
{
|
||||
new PerformanceCounterCategory("LogicalDisk"),
|
||||
new PerformanceCounterCategory("PhysicalDisk"),
|
||||
new PerformanceCounterCategory("HTTP Service"),
|
||||
new PerformanceCounterCategory("ASP.NET v4.0.30319"),
|
||||
new PerformanceCounterCategory("Power Meter"),
|
||||
new PerformanceCounterCategory("Processor"),
|
||||
new PerformanceCounterCategory("Energy Meter"),
|
||||
new PerformanceCounterCategory("Node.js"),
|
||||
};
|
||||
|
||||
while (true)
|
||||
{
|
||||
DateTime dt = DateTime.UtcNow;
|
||||
TimeSpan sleepTime = TimeSpan.FromSeconds(5);
|
||||
try
|
||||
{
|
||||
var counterMetric = new JObject
|
||||
{
|
||||
{"id", Guid.NewGuid()},
|
||||
{"timestamp", dt},
|
||||
{"machineName", Environment.MachineName},
|
||||
};
|
||||
|
||||
var counterArray = new JArray();
|
||||
|
||||
foreach (var category in categoryCounters)
|
||||
{
|
||||
foreach (var instance in category.GetInstanceNames())
|
||||
{
|
||||
var instanceObject = new JObject()
|
||||
{
|
||||
{"counterType", category.CategoryName},
|
||||
{"counterFor", instance}
|
||||
};
|
||||
|
||||
foreach (var counter in category.GetCounters(instance))
|
||||
{
|
||||
instanceObject.Add(counter.CounterName, counter.RawValue);
|
||||
}
|
||||
|
||||
counterArray.Add(instanceObject);
|
||||
}
|
||||
}
|
||||
|
||||
counterMetric.Add("logs", counterArray);
|
||||
|
||||
await _client.CreateDocumentAsync(collUri, counterMetric);
|
||||
Console.WriteLine(counterMetric);
|
||||
}
|
||||
catch (DocumentClientException de)
|
||||
{
|
||||
if (de.StatusCode != null && (int)de.StatusCode != 429)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
sleepTime = de.RetryAfter;
|
||||
}
|
||||
catch (AggregateException ae)
|
||||
{
|
||||
if (!(ae.InnerException is DocumentClientException))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
DocumentClientException de = (DocumentClientException)ae.InnerException;
|
||||
if (de.StatusCode != null && (int)de.StatusCode != 429)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
sleepTime = de.RetryAfter;
|
||||
}
|
||||
|
||||
await Task.Delay(sleepTime);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("PerfEventCounter")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("PerfEventCounter")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("4e171c3f-ea00-46dd-a6d2-d360f594e450")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Azure.DocumentDB" version="1.5.2" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="5.0.8" targetFramework="net45" />
|
||||
</packages>
|
|
@ -0,0 +1,241 @@
|
|||
<h1 id="azure-documentdb-lab">Azure DocumentDB Lab</h1>
|
||||
<h2 id="overview">Overview</h2>
|
||||
<p>Azure DocumentDB is a NoSQL, JSON document database built for big data solutions that require scaling and high availability.</p>
|
||||
<p>This hands-on lab will step you through the following features:</p>
|
||||
<ol>
|
||||
<li><strong>Querying</strong> - Connect to a DocumentDB database and execute a simple query</li>
|
||||
<li><strong>Filtering</strong> - Execute ad-hoc queries on schemaless JSON data.</li>
|
||||
</ol>
|
||||
<h3 id="about-the-code">About the code</h3>
|
||||
<p>This lab uses a simple ASP.NET MVC website as a test application. This application allows you to
|
||||
write arbitrary query commands and execute them against our test databases. Any result set will be
|
||||
rendered automatically into the JSON response panel. There are arrows to navigate left and right through the results. </p>
|
||||
<p>To begin, open the <code>Azure DocumentDB Lab.sln</code> solution in Visual Studio 2015 and press <code>F5</code> to
|
||||
compile and launch the web app on the local machine.</p>
|
||||
<blockquote>
|
||||
<p><strong>Note:</strong> The DocumentDB that we will be querying was created via the Azure Portal.
|
||||
For more information on the Azure Portal refer to the <strong>Appendix</strong> at the end of this lab.</p>
|
||||
</blockquote>
|
||||
<hr>
|
||||
<h2 id="scenario-1">Scenario 1</h2>
|
||||
<p>In this scenario. We will change the MVC application to send a query to the DocumentDB server.</p>
|
||||
<h3 id="part-one">Part One</h3>
|
||||
<p>To begin, open the <code>Azure DocumentDB Lab.sln</code> solution in Visual Studio 2015 and press <code>F5</code> to
|
||||
compile and launch the web app on the local machine.</p>
|
||||
<p>You should be presented with an application that looks like this:</p>
|
||||
<p><img src="images/home_page.png" alt=""></p>
|
||||
<p>This page is designed to take the query that the user writes and pass it to a DocumentDB server that
|
||||
we have set up for the purposes of this demo. </p>
|
||||
<p>Type this query into the query editor:</p>
|
||||
<pre><code class="lang-SQL">SELECT *
|
||||
FROM c
|
||||
</code></pre>
|
||||
<p>...and click on <strong>Run It!</strong></p>
|
||||
<p><img src="images/no_results.png" alt=""></p>
|
||||
<p>Currently there are no results - we need to finish implementing the DocumentDB call first.</p>
|
||||
<h3 id="part-two">Part Two</h3>
|
||||
<p>In the visual studio solution navigate to the <code>HomeController</code> class in the <code>LabWeb</code> project.</p>
|
||||
<p><img src="images/home_controller.png" alt=""></p>
|
||||
<p>Find the <code>Query</code> action method. There is a line of code that looks like this:</p>
|
||||
<pre><code class="lang-csharp">IDocumentQuery<dynamic> docQuery = null;
|
||||
</code></pre>
|
||||
<p>We will modify it to create and send a DocumentDB query. </p>
|
||||
<p>The query text from the page is passed into the action via the <code>query</code> variable. Change it to the following:</p>
|
||||
<pre><code class="lang-csharp">var collectionUri = UriFactory.CreateDocumentCollectionUri(ConfigurationManager.AppSettings["DocumentDBName"], ConfigurationManager.AppSettings["DocumentDBCollectionName"]);
|
||||
IDocumentQuery<dynamic> docQuery = ReadOnlyClient.CreateDocumentQuery(
|
||||
collectionUri,
|
||||
query,
|
||||
new FeedOptions
|
||||
{
|
||||
MaxItemCount = 10,
|
||||
EnableScanInQuery = true
|
||||
}
|
||||
).AsDocumentQuery();
|
||||
</code></pre>
|
||||
<p>Notice in the FeedOptions, we are setting <code>MaxItemCount = 10</code>. This means we will get up to 10 results per execution of the query. The DocumentDB API has
|
||||
support for paging built in (We will see an example of this shortly).</p>
|
||||
<p>Let's quickly inspect the rest of the Query Action:</p>
|
||||
<pre><code class="lang-csharp">var results = await docQuery.ExecuteNextAsync();
|
||||
</code></pre>
|
||||
<p>This part is what actually uses the Azure DocumentDB SDK to call DocumentDB and retrieve the results for
|
||||
our query. Notice this will only return up to <code>MaxItemCount</code> results as above (In our case 10 items).
|
||||
This can also be set to -1 for dynamic sizing of the resulting set to the maximum response size.</p>
|
||||
<p>If we wanted to get the next set of results we would have to call <code>docQuery.ExecuteNextAsync()</code> again.</p>
|
||||
<p>In the interests of this demo, we are only retrieving the first ten results. However if this was a real-world application
|
||||
where we need ALL of the results for a query. We would set the MaxItemCount to -1 and do something like the following:</p>
|
||||
<pre><code class="lang-csharp">while (docQuery.HasMoreResults)
|
||||
{
|
||||
//Can use strongly typed objects by using <T> on docQuery.ExecuteNextAsync<T>()
|
||||
var results = await docQuery.ExecuteNextAsync();
|
||||
|
||||
//dynamic can also be T
|
||||
foreach (dynamic result in results)
|
||||
{
|
||||
//Do something with results
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Note:</strong> We are deserializing the JSON string and serializing it back again so that we can format the JSON into human readable string.</p>
|
||||
<p>Press <code>F5</code> to compile and launch the web app on the local machine.</p>
|
||||
<p>Type this query into the query box:</p>
|
||||
<pre><code class="lang-SQL">SELECT *
|
||||
FROM c
|
||||
</code></pre>
|
||||
<p>...and click on <strong>Run It!</strong></p>
|
||||
<p><img src="images/50_results.png" alt=""></p>
|
||||
<p>Progress! We have successfully returned results from DocumentDB.</p>
|
||||
<h2 id="scenario-2">Scenario 2</h2>
|
||||
<p>From now on, we will be working directly in the web browser.</p>
|
||||
<p>In this scenario we will introduce the SQL-like syntax of DocumentDB and show how we can use it to manipulate our results.</p>
|
||||
<p>The dataset we are querying was extracted from the performance counters on various computers running windows 10.</p>
|
||||
<p>The performance counters are:</p>
|
||||
<ul>
|
||||
<li>LogicalDisk</li>
|
||||
<li>PhysicalDisk</li>
|
||||
<li>HTTP Service</li>
|
||||
<li>ASP.NET v4.0.30319</li>
|
||||
<li>Power Meter</li>
|
||||
<li>Processor</li>
|
||||
<li>Energy Meter</li>
|
||||
</ul>
|
||||
<h3 id="part-one">Part One</h3>
|
||||
<p>In the query, the <code>FROM</code> name is simply an alias to the entire collection for the user to refer to in the query. It is not actually a table like in traditional SQL.</p>
|
||||
<p>For example:</p>
|
||||
<pre><code class="lang-SQL">SELECT *
|
||||
FROM collection
|
||||
</code></pre>
|
||||
<p>...is exactly the same as:</p>
|
||||
<pre><code class="lang-SQL">SELECT *
|
||||
FROM logs
|
||||
</code></pre>
|
||||
<p>Give it a try!</p>
|
||||
<p><img src="images/select_from_collection.png" alt=""></p>
|
||||
<p><img src="images/select_from_logs.png" alt=""></p>
|
||||
<p>When refering to fields you must use the alias you define in the <code>FROM</code> clause.</p>
|
||||
<p>Execute this query:</p>
|
||||
<pre><code class="lang-SQL">SELECT id
|
||||
FROM logs
|
||||
</code></pre>
|
||||
<p><img src="images/id_error.png" alt=""></p>
|
||||
<p>As you can see this resulted in an error.</p>
|
||||
<p>To fix this error we have to provide the full "path" to the properties of the objects within the database.</p>
|
||||
<p>Execute this query instead:</p>
|
||||
<pre><code class="lang-SQL">SELECT logs.id
|
||||
FROM logs
|
||||
</code></pre>
|
||||
<p><img src="images/id_results.png" alt=""></p>
|
||||
<h3 id="part-two">Part Two</h3>
|
||||
<p>Now that we know how to select a certain field, we can filter on them.</p>
|
||||
<p>Write a query to select a specific record by its ID:</p>
|
||||
<pre><code class="lang-SQL">SELECT *
|
||||
FROM logs
|
||||
WHERE logs.id = "ef811f1d-d271-4e1f-baf7-4d5ac0b6c840"
|
||||
</code></pre>
|
||||
<p><img src="images/id_filter.png" alt=""></p>
|
||||
<h3 id="part-three">Part Three</h3>
|
||||
<p>Let's use a real-world scenario and filter the results to logs for a certain machine.</p>
|
||||
<p>Execute this query:</p>
|
||||
<pre><code class="lang-SQL">SELECT *
|
||||
FROM logs
|
||||
WHERE logs.machineName = "WKS-1604"
|
||||
</code></pre>
|
||||
<p><img src="images/filter1.png" alt=""></p>
|
||||
<p>The results are limited to logs from the machine <code>WKS-1604</code>.</p>
|
||||
<h2 id="scenario-3">Scenario 3</h2>
|
||||
<p>In this scenario we are going to see how we can use joins to inspect child objects / arrays.</p>
|
||||
<h3 id="part-one">Part One</h3>
|
||||
<p>We have been using DocumentDB to inspect all the logs of a certain machine.
|
||||
What if we only wanted to see logs of a certain type. We can use the <code>JOIN</code> keyword to join to our logs array. We can also give it an alias and inspect its properties.</p>
|
||||
<p>Let's see the <code>JOIN</code> in action. Try this query:</p>
|
||||
<pre><code class="lang-SQL">SELECT perfLog
|
||||
FROM machinelogs
|
||||
JOIN perfLog IN machinelogs.logs
|
||||
</code></pre>
|
||||
<p>Inspect the results and you will see for each log object in the array of each document has been returned as a seperate result set:</p>
|
||||
<p><img src="images/join.png" alt=""></p>
|
||||
<p><img src="images/join2.png" alt=""></p>
|
||||
<p>Now that we know how to join to our child array we can use it for filtering. Lets find all "Power Meter" logs:</p>
|
||||
<pre><code class="lang-SQL">SELECT perfLog
|
||||
FROM machinelogs
|
||||
JOIN perfLog IN machinelogs.logs
|
||||
WHERE perfLog.counterType = "Power Meter"
|
||||
</code></pre>
|
||||
<p><img src="images/filter2.png" alt=""></p>
|
||||
<p>Because we are refering to objects / documents, we can filter our result set by seeing if a property exists on the object.</p>
|
||||
<p>For example:</p>
|
||||
<pre><code class="lang-SQL">SELECT perfLog
|
||||
FROM machinelogs
|
||||
JOIN perfLog IN machinelogs.logs
|
||||
WHERE perfLog.Power != null
|
||||
</code></pre>
|
||||
<p><img src="images/filter3.png" alt=""></p>
|
||||
<p>Imagine that we want to see both LogicalDisk and PhysicalDisk results. There two ways we can achieve this.</p>
|
||||
<p>Using an <code>OR</code> predicate:</p>
|
||||
<pre><code class="lang-SQL">SELECT perfLog
|
||||
FROM machinelogs
|
||||
JOIN perfLog IN machinelogs.logs
|
||||
WHERE (perfLog.counterType = "LogicalDisk" OR perfLog.counterType = "PhysicalDisk")
|
||||
</code></pre>
|
||||
<p>...or using an <code>IN</code> predicate: </p>
|
||||
<pre><code class="lang-SQL">SELECT perfLog
|
||||
FROM machinelogs
|
||||
JOIN perfLog IN machinelogs.logs
|
||||
WHERE perfLog.counterType IN ("LogicalDisk", "PhysicalDisk")
|
||||
</code></pre>
|
||||
<p><img src="images/filter4.png" alt=""></p>
|
||||
<p><img src="images/filter5.png" alt=""></p>
|
||||
<p>We can filter these results by the "% Disk Time" property.Because this property name contains white space, we must use special index to address it:</p>
|
||||
<pre><code class="lang-SQL">WHERE perfLog["% Disk Time"] ...
|
||||
</code></pre>
|
||||
<p>This syntax will be familiar to users of JavaScript, or C# dictionary accessor syntax.</p>
|
||||
<p>We can use the <code>BETWEEN</code> keyword to filter by a range of values.</p>
|
||||
<p>Try this query:</p>
|
||||
<pre><code class="lang-SQL">SELECT perfLog
|
||||
FROM machinelogs
|
||||
JOIN perfLog IN machinelogs.logs
|
||||
WHERE perfLog["% Disk Time"] BETWEEN 42247790840000 AND 42247790860000
|
||||
</code></pre>
|
||||
<p><img src="images/between_filter.png" alt=""></p>
|
||||
<p>We have used the <code>MaxItemCount</code> in the code to limit our results to 10 items. We can also restrict the amount
|
||||
of results returned by using a <code>TOP</code> clause in our query. </p>
|
||||
<p>Lets adjust our query to find the top result. Give this a try:</p>
|
||||
<pre><code class="lang-SQL">SELECT TOP 1 perfLog
|
||||
FROM machinelogs
|
||||
JOIN perfLog IN machinelogs.logs
|
||||
WHERE perfLog["% Disk Time"] BETWEEN 42247790840000 AND 42247790860000
|
||||
</code></pre>
|
||||
<p><img src="images/top_1.png" alt=""></p>
|
||||
<h2 id="part-two">Part Two</h2>
|
||||
<p>We can use a feature called <strong>Projection</strong> to create an entirely new result set. We could use this to create a common structure or to make it match a structure we already have.</p>
|
||||
<p>Try this query:</p>
|
||||
<pre><code class="lang-SQL">SELECT {
|
||||
"FreeSpacePercent": perfLog["% Free Space"],
|
||||
"FreeMegabytes": perfLog["Free Megabytes"],
|
||||
"CurrentDiskQueueLength": perfLog["Current Disk Queue Length"],
|
||||
"Power" : perfLog["Power"],
|
||||
"PowerBudget" : perfLog["Power Budget"]
|
||||
} AS SummaryLogs
|
||||
FROM machinelogs
|
||||
JOIN perfLog IN machinelogs.logs
|
||||
</code></pre>
|
||||
<p><img src="images/projection.png" alt=""></p>
|
||||
<p>This query allowed us to combine all logs into one well-known structure which could be useful, for example, when binding to a strongly typed dataset.</p>
|
||||
<h3 id="further-reading">Further Reading</h3>
|
||||
<p><a href="http://aka.ms/docdbstart">Get Started with DocumentDB - http://aka.ms/docdbstart</a></p>
|
||||
<p><a href="http://aka.ms/docdbdocs">Documentation and Videos - http://aka.ms/docdbdocs</a></p>
|
||||
<p><a href="http://aka.ms/docdbpricing">How does pricing work? - http://aka.ms/docdbpricing</a></p>
|
||||
<p><a href="http://aka.ms/docdbforum">Get help on the forums - http://aka.ms/docdbforum</a></p>
|
||||
<p><a href="https://azure.microsoft.com/en-us/documentation/articles/documentdb-sql-query">DocumentDB SQL Query Syntax</a></p>
|
||||
<p><a href="https://www.documentdb.com/sql/demo">DocumentDB Query Playground</a></p>
|
||||
<h2 id="appendix">Appendix</h2>
|
||||
<p>The Azure Portal was used to create the DocumentDB server. The Azure Portal can be found at <a href="https://portal.azure.com/">https://portal.azure.com/</a>.</p>
|
||||
<p>Some features that you can use in Azure Portal with DocumentDB include:</p>
|
||||
<h4 id="document-explorer">Document Explorer</h4>
|
||||
<p>View the JSON documents inside your collections.</p>
|
||||
<p><img src="images/DocumentExplorer.png" alt=""></p>
|
||||
<h4 id="query-explorer">Query Explorer</h4>
|
||||
<p>Test your queries and view the results.</p>
|
||||
<p><img src="images/QueryExplorer.png" alt=""></p>
|
||||
<h4 id="script-explorer">Script Explorer</h4>
|
||||
<p>View, add and modify stored procedures, user functions and triggers.</p>
|
||||
<p><img src="images/ScriptExplorer.png" alt=""></p>
|
|
@ -0,0 +1,413 @@
|
|||
Azure DocumentDB Lab
|
||||
=================================================
|
||||
|
||||
## Overview
|
||||
|
||||
Azure DocumentDB is a NoSQL, JSON document database built for big data solutions that require scaling and high availability.
|
||||
|
||||
This hands-on lab will step you through the following features:
|
||||
|
||||
1. **Querying** - Connect to a DocumentDB database and execute a simple query
|
||||
2. **Filtering** - Execute ad-hoc queries on schemaless JSON data.
|
||||
|
||||
### About the code
|
||||
|
||||
This lab uses a simple ASP.NET MVC website as a test application. This application allows you to
|
||||
write arbitrary query commands and execute them against our test databases. Any result set will be
|
||||
rendered automatically into the JSON response panel. There are arrows to navigate left and right through the results.
|
||||
|
||||
To begin, open the `Azure DocumentDB Lab.sln` solution in Visual Studio 2015 and press `F5` to
|
||||
compile and launch the web app on the local machine.
|
||||
|
||||
> **Note:** The DocumentDB that we will be querying was created via the Azure Portal.
|
||||
> For more information on the Azure Portal refer to the **Appendix** at the end of this lab.
|
||||
|
||||
-----
|
||||
|
||||
## Scenario 1
|
||||
|
||||
In this scenario. We will change the MVC application to send a query to the DocumentDB server.
|
||||
|
||||
### Part One
|
||||
|
||||
To begin, open the `Azure DocumentDB Lab.sln` solution in Visual Studio 2015 and press `F5` to
|
||||
compile and launch the web app on the local machine.
|
||||
|
||||
You should be presented with an application that looks like this:
|
||||
|
||||
![](images/home_page.png)
|
||||
|
||||
This page is designed to take the query that the user writes and pass it to a DocumentDB server that
|
||||
we have set up for the purposes of this demo.
|
||||
|
||||
Type this query into the query editor:
|
||||
|
||||
```SQL
|
||||
SELECT *
|
||||
FROM c
|
||||
```
|
||||
|
||||
...and click on **Run It!**
|
||||
|
||||
![](images/no_results.png)
|
||||
|
||||
Currently there are no results - we need to finish implementing the DocumentDB call first.
|
||||
|
||||
### Part Two
|
||||
|
||||
In the visual studio solution navigate to the `HomeController` class in the `LabWeb` project.
|
||||
|
||||
![](images/home_controller.png)
|
||||
|
||||
Find the `Query` action method. There is a line of code that looks like this:
|
||||
|
||||
```csharp
|
||||
IDocumentQuery<dynamic> docQuery = null;
|
||||
```
|
||||
|
||||
We will modify it to create and send a DocumentDB query.
|
||||
|
||||
The query text from the page is passed into the action via the `query` variable. Change it to the following:
|
||||
|
||||
```csharp
|
||||
var collectionUri = UriFactory.CreateDocumentCollectionUri(ConfigurationManager.AppSettings["DocumentDBName"], ConfigurationManager.AppSettings["DocumentDBCollectionName"]);
|
||||
IDocumentQuery<dynamic> docQuery = ReadOnlyClient.CreateDocumentQuery(
|
||||
collectionUri,
|
||||
query,
|
||||
new FeedOptions
|
||||
{
|
||||
MaxItemCount = 10,
|
||||
EnableScanInQuery = true
|
||||
}
|
||||
).AsDocumentQuery();
|
||||
```
|
||||
|
||||
Notice in the FeedOptions, we are setting `MaxItemCount = 10`. This means we will get up to 10 results per execution of the query. The DocumentDB API has
|
||||
support for paging built in (We will see an example of this shortly).
|
||||
|
||||
Let's quickly inspect the rest of the Query Action:
|
||||
|
||||
```csharp
|
||||
var results = await docQuery.ExecuteNextAsync();
|
||||
```
|
||||
|
||||
This part is what actually uses the Azure DocumentDB SDK to call DocumentDB and retrieve the results for
|
||||
our query. Notice this will only return up to `MaxItemCount` results as above (In our case 10 items).
|
||||
This can also be set to -1 for dynamic sizing of the resulting set to the maximum response size.
|
||||
|
||||
If we wanted to get the next set of results we would have to call `docQuery.ExecuteNextAsync()` again.
|
||||
|
||||
In the interests of this demo, we are only retrieving the first ten results. However if this was a real-world application
|
||||
where we need ALL of the results for a query. We would set the MaxItemCount to -1 and do something like the following:
|
||||
|
||||
```csharp
|
||||
while (docQuery.HasMoreResults)
|
||||
{
|
||||
//Can use strongly typed objects by using <T> on docQuery.ExecuteNextAsync<T>()
|
||||
var results = await docQuery.ExecuteNextAsync();
|
||||
|
||||
//dynamic can also be T
|
||||
foreach (dynamic result in results)
|
||||
{
|
||||
//Do something with results
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** We are deserializing the JSON string and serializing it back again so that we can format the JSON into human readable string.
|
||||
|
||||
Press `F5` to compile and launch the web app on the local machine.
|
||||
|
||||
Type this query into the query box:
|
||||
|
||||
```SQL
|
||||
SELECT *
|
||||
FROM c
|
||||
```
|
||||
|
||||
...and click on **Run It!**
|
||||
|
||||
![](images/50_results.png)
|
||||
|
||||
Progress! We have successfully returned results from DocumentDB.
|
||||
|
||||
|
||||
## Scenario 2
|
||||
|
||||
From now on, we will be working directly in the web browser.
|
||||
|
||||
In this scenario we will introduce the SQL-like syntax of DocumentDB and show how we can use it to manipulate our results.
|
||||
|
||||
The dataset we are querying was extracted from the performance counters on various computers running windows 10.
|
||||
|
||||
The performance counters are:
|
||||
|
||||
- LogicalDisk
|
||||
- PhysicalDisk
|
||||
- HTTP Service
|
||||
- ASP.NET v4.0.30319
|
||||
- Power Meter
|
||||
- Processor
|
||||
- Energy Meter
|
||||
|
||||
|
||||
### Part One
|
||||
|
||||
In the query, the `FROM` name is simply an alias to the entire collection for the user to refer to in the query. It is not actually a table like in traditional SQL.
|
||||
|
||||
For example:
|
||||
|
||||
```SQL
|
||||
SELECT *
|
||||
FROM collection
|
||||
```
|
||||
|
||||
...is exactly the same as:
|
||||
|
||||
```SQL
|
||||
SELECT *
|
||||
FROM logs
|
||||
```
|
||||
|
||||
Give it a try!
|
||||
|
||||
![](images/select_from_collection.png)
|
||||
|
||||
![](images/select_from_logs.png)
|
||||
|
||||
When refering to fields you must use the alias you define in the `FROM` clause.
|
||||
|
||||
Execute this query:
|
||||
|
||||
```SQL
|
||||
SELECT id
|
||||
FROM logs
|
||||
```
|
||||
|
||||
![](images/id_error.png)
|
||||
|
||||
As you can see this resulted in an error.
|
||||
|
||||
To fix this error we have to provide the full "path" to the properties of the objects within the database.
|
||||
|
||||
Execute this query instead:
|
||||
|
||||
```SQL
|
||||
SELECT logs.id
|
||||
FROM logs
|
||||
```
|
||||
|
||||
![](images/id_results.png)
|
||||
|
||||
|
||||
### Part Two
|
||||
|
||||
Now that we know how to select a certain field, we can filter on them.
|
||||
|
||||
Write a query to select a specific record by its ID:
|
||||
|
||||
```SQL
|
||||
SELECT *
|
||||
FROM logs
|
||||
WHERE logs.id = "ef811f1d-d271-4e1f-baf7-4d5ac0b6c840"
|
||||
```
|
||||
|
||||
![](images/id_filter.png)
|
||||
|
||||
|
||||
### Part Three
|
||||
|
||||
Let's use a real-world scenario and filter the results to logs for a certain machine.
|
||||
|
||||
Execute this query:
|
||||
|
||||
```SQL
|
||||
SELECT *
|
||||
FROM logs
|
||||
WHERE logs.machineName = "WKS-1604"
|
||||
```
|
||||
|
||||
![](images/filter1.png)
|
||||
|
||||
The results are limited to logs from the machine `WKS-1604`.
|
||||
|
||||
|
||||
## Scenario 3
|
||||
|
||||
In this scenario we are going to see how we can use joins to inspect child objects / arrays.
|
||||
|
||||
### Part One
|
||||
|
||||
We have been using DocumentDB to inspect all the logs of a certain machine.
|
||||
What if we only wanted to see logs of a certain type. We can use the `JOIN` keyword to join to our logs array. We can also give it an alias and inspect its properties.
|
||||
|
||||
Let's see the `JOIN` in action. Try this query:
|
||||
|
||||
```SQL
|
||||
SELECT perfLog
|
||||
FROM machinelogs
|
||||
JOIN perfLog IN machinelogs.logs
|
||||
```
|
||||
|
||||
Inspect the results and you will see for each log object in the array of each document has been returned as a seperate result set:
|
||||
|
||||
![](images/join.png)
|
||||
|
||||
![](images/join2.png)
|
||||
|
||||
|
||||
Now that we know how to join to our child array we can use it for filtering. Lets find all "Power Meter" logs:
|
||||
|
||||
```SQL
|
||||
SELECT perfLog
|
||||
FROM machinelogs
|
||||
JOIN perfLog IN machinelogs.logs
|
||||
WHERE perfLog.counterType = "Power Meter"
|
||||
```
|
||||
|
||||
![](images/filter2.png)
|
||||
|
||||
Because we are refering to objects / documents, we can filter our result set by seeing if a property exists on the object.
|
||||
|
||||
For example:
|
||||
|
||||
```SQL
|
||||
SELECT perfLog
|
||||
FROM machinelogs
|
||||
JOIN perfLog IN machinelogs.logs
|
||||
WHERE perfLog.Power != null
|
||||
```
|
||||
|
||||
![](images/filter3.png)
|
||||
|
||||
|
||||
Imagine that we want to see both LogicalDisk and PhysicalDisk results. There two ways we can achieve this.
|
||||
|
||||
Using an `OR` predicate:
|
||||
|
||||
```SQL
|
||||
SELECT perfLog
|
||||
FROM machinelogs
|
||||
JOIN perfLog IN machinelogs.logs
|
||||
WHERE (perfLog.counterType = "LogicalDisk" OR perfLog.counterType = "PhysicalDisk")
|
||||
```
|
||||
|
||||
...or using an `IN` predicate:
|
||||
|
||||
```SQL
|
||||
SELECT perfLog
|
||||
FROM machinelogs
|
||||
JOIN perfLog IN machinelogs.logs
|
||||
WHERE perfLog.counterType IN ("LogicalDisk", "PhysicalDisk")
|
||||
```
|
||||
|
||||
![](images/filter4.png)
|
||||
|
||||
![](images/filter5.png)
|
||||
|
||||
|
||||
We can filter these results by the "% Disk Time" property.Because this property name contains white space, we must use special index to address it:
|
||||
|
||||
```SQL
|
||||
WHERE perfLog["% Disk Time"] ...
|
||||
```
|
||||
|
||||
This syntax will be familiar to users of JavaScript, or C# dictionary accessor syntax.
|
||||
|
||||
We can use the `BETWEEN` keyword to filter by a range of values.
|
||||
|
||||
Try this query:
|
||||
|
||||
```SQL
|
||||
SELECT perfLog
|
||||
FROM machinelogs
|
||||
JOIN perfLog IN machinelogs.logs
|
||||
WHERE perfLog["% Disk Time"] BETWEEN 42247790840000 AND 42247790860000
|
||||
```
|
||||
|
||||
![](images/between_filter.png)
|
||||
|
||||
We have used the `MaxItemCount` in the code to limit our results to 10 items. We can also restrict the amount
|
||||
of results returned by using a `TOP` clause in our query.
|
||||
|
||||
Lets adjust our query to find the top result. Give this a try:
|
||||
|
||||
```SQL
|
||||
SELECT TOP 1 perfLog
|
||||
FROM machinelogs
|
||||
JOIN perfLog IN machinelogs.logs
|
||||
WHERE perfLog["% Disk Time"] BETWEEN 42247790840000 AND 42247790860000
|
||||
```
|
||||
|
||||
![](images/top_1.png)
|
||||
|
||||
## Part Two
|
||||
|
||||
We can use a feature called **Projection** to create an entirely new result set. We could use this to create a common structure or to make it match a structure we already have.
|
||||
|
||||
Try this query:
|
||||
|
||||
```SQL
|
||||
SELECT {
|
||||
"FreeSpacePercent": perfLog["% Free Space"],
|
||||
"FreeMegabytes": perfLog["Free Megabytes"],
|
||||
"CurrentDiskQueueLength": perfLog["Current Disk Queue Length"],
|
||||
"Power" : perfLog["Power"],
|
||||
"PowerBudget" : perfLog["Power Budget"]
|
||||
} AS SummaryLogs
|
||||
FROM machinelogs
|
||||
JOIN perfLog IN machinelogs.logs
|
||||
```
|
||||
|
||||
![](images/projection.png)
|
||||
|
||||
This query allowed us to combine all logs into one well-known structure which could be useful, for example, when binding to a strongly typed dataset.
|
||||
|
||||
### Further Reading
|
||||
|
||||
[Get Started with DocumentDB - http://aka.ms/docdbstart](http://aka.ms/docdbstart)
|
||||
|
||||
[Documentation and Videos - http://aka.ms/docdbdocs](http://aka.ms/docdbdocs)
|
||||
|
||||
[How does pricing work? - http://aka.ms/docdbpricing](http://aka.ms/docdbpricing)
|
||||
|
||||
[Get help on the forums - http://aka.ms/docdbforum](http://aka.ms/docdbforum)
|
||||
|
||||
[DocumentDB SQL Query Syntax](https://azure.microsoft.com/en-us/documentation/articles/documentdb-sql-query)
|
||||
|
||||
[DocumentDB Query Playground](https://www.documentdb.com/sql/demo)
|
||||
|
||||
## Appendix
|
||||
|
||||
The Azure Portal was used to create the DocumentDB server. The Azure Portal can be found at [https://portal.azure.com/](https://portal.azure.com/).
|
||||
|
||||
Some features that you can use in Azure Portal with DocumentDB include:
|
||||
|
||||
|
||||
#### Document Explorer
|
||||
|
||||
View the JSON documents inside your collections.
|
||||
|
||||
![](images/DocumentExplorer.png)
|
||||
|
||||
|
||||
#### Query Explorer
|
||||
|
||||
Test your queries and view the results.
|
||||
|
||||
![](images/QueryExplorer.png)
|
||||
|
||||
|
||||
#### Script Explorer
|
||||
|
||||
View, add and modify stored procedures, user functions and triggers.
|
||||
|
||||
![](images/ScriptExplorer.png)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
После Ширина: | Высота: | Размер: 43 KiB |
После Ширина: | Высота: | Размер: 131 KiB |
После Ширина: | Высота: | Размер: 80 KiB |
После Ширина: | Высота: | Размер: 68 KiB |
После Ширина: | Высота: | Размер: 36 KiB |
После Ширина: | Высота: | Размер: 32 KiB |
После Ширина: | Высота: | Размер: 10 KiB |
После Ширина: | Высота: | Размер: 10 KiB |
После Ширина: | Высота: | Размер: 36 KiB |
После Ширина: | Высота: | Размер: 36 KiB |
После Ширина: | Высота: | Размер: 43 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 12 KiB |
После Ширина: | Высота: | Размер: 32 KiB |
После Ширина: | Высота: | Размер: 6.0 KiB |
После Ширина: | Высота: | Размер: 36 KiB |
После Ширина: | Высота: | Размер: 10 KiB |
После Ширина: | Высота: | Размер: 8.5 KiB |
После Ширина: | Высота: | Размер: 35 KiB |
После Ширина: | Высота: | Размер: 11 KiB |
После Ширина: | Высота: | Размер: 32 KiB |
После Ширина: | Высота: | Размер: 32 KiB |
После Ширина: | Высота: | Размер: 36 KiB |
|
@ -0,0 +1,40 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.23107.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LabWeb", "LabWeb\LabWeb.csproj", "{E18A7397-1BBF-4060-856E-1A72307CDDEA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|ARM = Debug|ARM
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|ARM = Release|ARM
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{E18A7397-1BBF-4060-856E-1A72307CDDEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E18A7397-1BBF-4060-856E-1A72307CDDEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E18A7397-1BBF-4060-856E-1A72307CDDEA}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{E18A7397-1BBF-4060-856E-1A72307CDDEA}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{E18A7397-1BBF-4060-856E-1A72307CDDEA}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{E18A7397-1BBF-4060-856E-1A72307CDDEA}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{E18A7397-1BBF-4060-856E-1A72307CDDEA}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{E18A7397-1BBF-4060-856E-1A72307CDDEA}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{E18A7397-1BBF-4060-856E-1A72307CDDEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E18A7397-1BBF-4060-856E-1A72307CDDEA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E18A7397-1BBF-4060-856E-1A72307CDDEA}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{E18A7397-1BBF-4060-856E-1A72307CDDEA}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{E18A7397-1BBF-4060-856E-1A72307CDDEA}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{E18A7397-1BBF-4060-856E-1A72307CDDEA}.Release|x64.Build.0 = Release|Any CPU
|
||||
{E18A7397-1BBF-4060-856E-1A72307CDDEA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E18A7397-1BBF-4060-856E-1A72307CDDEA}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,696 @@
|
|||
body {
|
||||
padding-top: 50px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
/* Set padding to keep content from hitting the edges */
|
||||
.body-content {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
/* Override the default bootstrap behavior where horizontal description lists
|
||||
will truncate terms that are too long to fit in the left column
|
||||
*/
|
||||
.dl-horizontal dt {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
/* Set width on the form input elements since they're 100% wide by default */
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
max-width: 280px;
|
||||
}
|
||||
|
||||
|
||||
/* Branding! */
|
||||
.build-anchor, .build-anchor:visited {
|
||||
color: #0080A6;
|
||||
/* Build logo */
|
||||
padding: 5px;
|
||||
/* Text */
|
||||
font-size: 18pt;
|
||||
}
|
||||
|
||||
.build-anchor:hover {
|
||||
text-decoration: none;
|
||||
color: #1A1A1A;
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
min-width: 539px;
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
#main {
|
||||
overflow: auto;
|
||||
padding-bottom: 180px;
|
||||
}
|
||||
/* must be same height as the footer */
|
||||
|
||||
#footer {
|
||||
position: relative;
|
||||
margin-top: -120px; /* negative value of footer height */
|
||||
height: 120px;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/*Opera Fix*/
|
||||
body:before {
|
||||
content: "";
|
||||
height: 100%;
|
||||
float: left;
|
||||
width: 0;
|
||||
margin-top: -32767px;
|
||||
}
|
||||
|
||||
.demo-background {
|
||||
background-color: #48a0b8;
|
||||
}
|
||||
|
||||
.demo-title {
|
||||
padding: 35px 15px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.tabs-container {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.demo-main {
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
.navbar-header {
|
||||
position: relative;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
.feedback-divider, .footer, .footer-logo {
|
||||
margin-right: 1.8%;
|
||||
}
|
||||
|
||||
a.usda-dataset, a.link-community {
|
||||
color: white;
|
||||
}
|
||||
|
||||
/*Tiles*/
|
||||
.title-block, .category-block, .gap-block, .arrow-block {
|
||||
width: 14%;
|
||||
margin-right: 1%;
|
||||
float: left;
|
||||
display: inline;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.title-block {
|
||||
background-color: #30819e;
|
||||
font-family: "Segoe UI Light", "Segoe UI",Tahoma,Arial,sans-serif;
|
||||
font-size: 19px;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.category-block {
|
||||
background-color: #daecf1;
|
||||
color: #4a5152;
|
||||
font-family: "Segoe UI Light", "Segoe UI",Tahoma,Arial,sans-serif;
|
||||
font-size: 22px;
|
||||
cursor: pointer;
|
||||
padding-top: 3px;
|
||||
}
|
||||
|
||||
.arrow-block-left {
|
||||
width: 2%;
|
||||
}
|
||||
|
||||
.arrow-block-right {
|
||||
width: 3.5%;
|
||||
}
|
||||
|
||||
.arrow-block.arrow-title-block, .title-block {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.arrow-block.arrow-category-block, .category-block {
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.arrow-block.arrow-gap-block, .gap-block {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.title-block, .category-block {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.title-block.title-block-active {
|
||||
background-color: #2a7997;
|
||||
}
|
||||
|
||||
.title-block.title-block-preview {
|
||||
background-color: #DB6A94;
|
||||
}
|
||||
|
||||
.title-block.title-block-preview.title-block-active {
|
||||
background-color: #DB5280;
|
||||
}
|
||||
|
||||
.title-block.title-block-disabled {
|
||||
background-color: #ccc;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.category-block.category-block-active {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.category-block.category-block-preview {
|
||||
padding-left: 2%;
|
||||
}
|
||||
|
||||
.category-block.category-block-disabled {
|
||||
/* Safari 5.1 to 6.0 */
|
||||
background: -webkit-repeating-linear-gradient( 135deg, #daecf1, #daecf1 10px, #ccc 0px, #ccc 12px );
|
||||
/* Opera 11.1 to 12.0 */
|
||||
background: -o-repeating-linear-gradient( 135deg, #daecf1, #daecf1 10px, #ccc 0px, #ccc 12px );
|
||||
/* Firefox 3.6 to 15 */
|
||||
background: -moz-repeating-linear-gradient( 135deg, #daecf1, #daecf1 10px, #ccc 0px, #ccc 12px );
|
||||
/* Standard syntax */
|
||||
background: repeating-linear-gradient( 135deg, #daecf1, #daecf1 10px, #ccc 0px, #ccc 12px );
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.gap-block.gap-block-active, .category-block.category-block-active {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.disabled-block {
|
||||
background-color: #48a0b8;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.glyphicon-chevron-left, .glyphicon-chevron-right {
|
||||
font-size: 18px;
|
||||
color: white;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.tab6.title-block {
|
||||
background-color: #69b25d;
|
||||
}
|
||||
|
||||
.tab6.title-block.title-block-active {
|
||||
background-color: #89c402;
|
||||
}
|
||||
|
||||
.preview-text {
|
||||
font-size: 11px;
|
||||
vertical-align: 10px;
|
||||
padding-left: 2px;
|
||||
}
|
||||
|
||||
/*Main content*/
|
||||
.demo-main {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.bold-p {
|
||||
font-family: "Segoe UI",Tahoma,Arial,sans-serif;
|
||||
}
|
||||
|
||||
.italic-p {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.underline-pointer-p {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.switch-feedback-orderby {
|
||||
cursor: pointer;
|
||||
color: #337ab7;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.left-pane {
|
||||
width: 45%;
|
||||
float: left;
|
||||
display: inline;
|
||||
margin: 3px 0px;
|
||||
}
|
||||
|
||||
.right-pane {
|
||||
float: right;
|
||||
width: 50.3%;
|
||||
display: inline;
|
||||
margin: 1px 0px 4px;
|
||||
margin-right: 2%;
|
||||
}
|
||||
|
||||
.query-title {
|
||||
font-family: "Segoe UI", "Segoe UI Light", Tahoma,Arial,sans-serif;
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.pane-title {
|
||||
font-family: "Segoe UI Light", "Segoe UI",Tahoma,Arial,sans-serif;
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
padding-right: 1%;
|
||||
padding-left: 1%;
|
||||
color: #30819e;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pane-title:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.pane-title:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.pane-title-active {
|
||||
font-family: 'Segoe UI', "Segoe UI Light", Tahoma, Geneva, Verdana, sans-serif;
|
||||
color: #333;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.vertical-split {
|
||||
border-left: 1px solid #333;
|
||||
}
|
||||
|
||||
.ace-container {
|
||||
border: 1px solid #E0E0E0;
|
||||
}
|
||||
|
||||
#vsEditor {
|
||||
min-height: 298px;
|
||||
height: 298px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.ace-container, .results-container, #results-pane, #spatial-map {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
#results-pane .monaco-editor {
|
||||
background-color: #F0F0F0;
|
||||
}
|
||||
|
||||
#results-pane .monaco-editor .cursor {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.error-pane {
|
||||
background-color: #f2dede !important;
|
||||
border-color: #ebccd1 !important;
|
||||
}
|
||||
|
||||
#spatial-map {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#results-pane {
|
||||
display: block;
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
line-height: 1.1;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
border: 1px solid #E0E0E0;
|
||||
border-radius: 0px;
|
||||
overflow: hidden;
|
||||
z-index: 1;
|
||||
width:100%;
|
||||
height:100%;
|
||||
}
|
||||
|
||||
.centerer
|
||||
{
|
||||
display: none;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
opacity: 30%;
|
||||
}
|
||||
.centered
|
||||
{
|
||||
display: none;
|
||||
vertical-align: middle;
|
||||
opacity: 30%;
|
||||
}
|
||||
|
||||
.gap {
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
#vsEditor {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.show-answer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#trydocdb-sandbox, #azure-portal-sandbox {
|
||||
font-family: "Segoe UI",Tahoma,Arial,sans-serif;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.ext-docdb-builtinFunction {
|
||||
color: #FF00FF !important;
|
||||
}
|
||||
|
||||
.margin-top-9 {
|
||||
display: block;
|
||||
margin-top: 9px;
|
||||
}
|
||||
|
||||
/* Results */
|
||||
.result-pager {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.title-right, .close-map {
|
||||
color: #30819e;
|
||||
font-size: 16px;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.switch-icon, .title-right, .close-map, .close-map-icon {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.disabled-arrow {
|
||||
opacity: 0.2;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.left-arrow, .right-arrow, .title-right, .switch-icon, .close-map, .close-map-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.close-map-icon {
|
||||
padding-top: 10px;
|
||||
color: #30819e;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.switch-icon {
|
||||
height: 26px;
|
||||
width: 22px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.map-logo {
|
||||
height: 22px;
|
||||
width: 15px;
|
||||
cursor: pointer;
|
||||
vertical-align: -3px;
|
||||
}
|
||||
|
||||
/* Feedback */
|
||||
.feedback-container {
|
||||
padding: 0;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.feedback-bar {
|
||||
background-image: url(../Content/Images/page_icon.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0px center;
|
||||
}
|
||||
|
||||
.feedback-thanks, .feedback-bar {
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.feedback-title {
|
||||
padding-top: 10px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.feedback-subtitle {
|
||||
font-size: 15px;
|
||||
clear: both;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.feedback-subtitle, .feedback-title, .feedback-thanks {
|
||||
padding-left: 55px;
|
||||
}
|
||||
|
||||
.btn.feedback-btn {
|
||||
float: left;
|
||||
margin-top: -8px;
|
||||
height: 28px;
|
||||
line-height: 26px;
|
||||
background-color: #48a0b8;
|
||||
border: 1px solid #48a0b8;
|
||||
}
|
||||
|
||||
.btn.feedback-btn:hover {
|
||||
background-color: #48a0b8;
|
||||
border-color: #0094bf;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn.feedback-btn:active {
|
||||
background-color: white;
|
||||
color: #48a0b8 !important;
|
||||
}
|
||||
|
||||
.btn.feedback-btn:focus {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn.feedback-btn.feedback-btn-yes {
|
||||
margin-left: 200px;
|
||||
}
|
||||
|
||||
.btn.feedback-btn.feedback-btn-no {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.feedback-divider {
|
||||
border-bottom: 1px solid #B8B8B8;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.comments-title {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.comments-input {
|
||||
width: 98.2%;
|
||||
height: 240px;
|
||||
}
|
||||
|
||||
.feedback-thanks, .comments-thanks {
|
||||
font-size: 20px;
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
.comments-thanks {
|
||||
padding-top: 150px;
|
||||
}
|
||||
|
||||
/* Btn */
|
||||
.btn {
|
||||
background-color: #89c402;
|
||||
border: 1px solid #89c402;
|
||||
border-radius: 0px;
|
||||
color: #fff;
|
||||
padding: 0px 15px;
|
||||
margin-top: 0px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: #89c402;
|
||||
border-color: #0094bf;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn:active {
|
||||
background-color: white;
|
||||
color: #89c402 !important;
|
||||
}
|
||||
|
||||
.btn.btn-share {
|
||||
background-color: #A8A8A8;
|
||||
border-color: #A8A8A8;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.btn.btn-share:hover {
|
||||
border-color: #606060;
|
||||
}
|
||||
|
||||
.btn.btn-share:active {
|
||||
background-color: white;
|
||||
color: #606060 !important;
|
||||
}
|
||||
|
||||
.btn-run, .btn-comments, .btn-share {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
input#share-link {
|
||||
clear: both;
|
||||
width: 95.9%;
|
||||
cursor: text;
|
||||
float: left;
|
||||
}
|
||||
|
||||
img.img-select-all {
|
||||
display: inline;
|
||||
vertical-align: -7px;
|
||||
float: left;
|
||||
margin-left: -1px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.footer-background {
|
||||
background-color: #e8e8e8;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.footer-logo {
|
||||
float: right;
|
||||
margin-top: -93px;
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.footer-link {
|
||||
margin-left: 15px;
|
||||
cursor: pointer;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.footer-link:first-child {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 1200px) {
|
||||
#share-link {
|
||||
max-width: 95%;
|
||||
}
|
||||
|
||||
.category-block.category-block-preview {
|
||||
padding-left: 1%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 991px) and (min-width: 539px) {
|
||||
.category-block {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
#share-link {
|
||||
max-width: 93.4%;
|
||||
}
|
||||
|
||||
.category-block.category-block-preview {
|
||||
padding-left: 0%;
|
||||
}
|
||||
|
||||
.long-text {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.preview-text {
|
||||
font-size: 9px;
|
||||
vertical-align: 8px;
|
||||
padding-left: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.navbar-header.pull-right {
|
||||
margin-right: 1.8%;
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
#share-link {
|
||||
max-width: 91%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 683px) {
|
||||
.long-text {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.preview-text {
|
||||
font-size: 8px;
|
||||
vertical-align: 8px;
|
||||
padding-left: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 631px) {
|
||||
.long-text {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 572px) {
|
||||
.preview-text {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 539px) {
|
||||
.category-block {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.title-block {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
#share-link {
|
||||
max-width: 91%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Geospatial */
|
||||
.geospatial-inactive {
|
||||
color: #48a0b8;
|
||||
cursor: pointer;
|
||||
}
|
После Ширина: | Высота: | Размер: 9.9 KiB |
|
@ -0,0 +1,104 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Mvc;
|
||||
using LabWeb.Models;
|
||||
using Microsoft.Azure.Documents;
|
||||
using Microsoft.Azure.Documents.Client;
|
||||
using Microsoft.Azure.Documents.Linq;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace LabWeb.Controllers
|
||||
{
|
||||
public class HomeController : Controller
|
||||
{
|
||||
private object obj = new object();
|
||||
private DocumentClient _readonlyClient;
|
||||
|
||||
public ActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public DocumentClient ReadOnlyClient
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_readonlyClient == null)
|
||||
{
|
||||
lock (obj)
|
||||
{
|
||||
_readonlyClient = new DocumentClient(new Uri(
|
||||
ConfigurationManager.AppSettings["DocumentDBEndpoint"]),
|
||||
ConfigurationManager.AppSettings["DocumentDBPrimaryReadonlyKey"]);
|
||||
}
|
||||
}
|
||||
|
||||
return _readonlyClient;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ActionResult> Query(string query)
|
||||
{
|
||||
var newModel = new QueryModel
|
||||
{
|
||||
Documents = new List<string>(),
|
||||
Query = query
|
||||
};
|
||||
int numRetries = 0;
|
||||
IDocumentQuery<dynamic> docQuery = null;
|
||||
|
||||
if (docQuery != null)
|
||||
{
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
var results = await docQuery.ExecuteNextAsync();
|
||||
|
||||
foreach (dynamic result in results)
|
||||
{
|
||||
string json = result.ToString();
|
||||
string formattedJson = (json.StartsWith("{", StringComparison.InvariantCulture) ||
|
||||
json.StartsWith("[", StringComparison.InvariantCulture))
|
||||
? JsonConvert.SerializeObject(JsonConvert.DeserializeObject(json), Formatting.Indented)
|
||||
: json;
|
||||
newModel.Documents.Add(formattedJson);
|
||||
newModel.Count++;
|
||||
}
|
||||
newModel.Error = null;
|
||||
newModel.StatusCode = 200;
|
||||
break;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
newModel.Documents.Clear();
|
||||
numRetries++;
|
||||
var exception = e.InnerException as DocumentClientException;
|
||||
if (exception != null)
|
||||
{
|
||||
if (exception.StatusCode != null && (int) exception.StatusCode == 429)
|
||||
{
|
||||
numRetries--;
|
||||
}
|
||||
int startIndex = exception.Message.IndexOf("{", StringComparison.InvariantCulture);
|
||||
int endIndex = exception.Message.LastIndexOf("}", StringComparison.InvariantCulture) + 1;
|
||||
newModel.Error = (startIndex < 0 || endIndex < 0)
|
||||
? exception.Message
|
||||
: exception.Message.Substring(startIndex, endIndex - startIndex);
|
||||
if (exception.StatusCode != null) newModel.StatusCode = (int) exception.StatusCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
newModel.Error = e.Message;
|
||||
}
|
||||
}
|
||||
} while (numRetries < 1);
|
||||
}
|
||||
|
||||
Response.Write(JsonConvert.SerializeObject(newModel));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<%@ Application Codebehind="Global.asax.cs" Inherits="LabWeb.MvcApplication" Language="C#" %>
|
|
@ -0,0 +1,55 @@
|
|||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Optimization;
|
||||
using System.Web.Routing;
|
||||
|
||||
namespace LabWeb
|
||||
{
|
||||
public class MvcApplication : HttpApplication
|
||||
{
|
||||
protected void Application_Start()
|
||||
{
|
||||
AreaRegistration.RegisterAllAreas();
|
||||
|
||||
// Global filters
|
||||
GlobalFilters.Filters.Add(new HandleErrorAttribute());
|
||||
|
||||
// Route configuration
|
||||
var routes = RouteTable.Routes;
|
||||
|
||||
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
|
||||
|
||||
routes.MapRoute(
|
||||
name: "Default",
|
||||
url: "{controller}/{action}/{id}",
|
||||
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
|
||||
);
|
||||
|
||||
routes.MapRoute(
|
||||
"SQLDemo",
|
||||
"sql/demo/{controller}/{action}/{id}",
|
||||
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
|
||||
);
|
||||
|
||||
// Bundling configuration
|
||||
var bundles = BundleTable.Bundles;
|
||||
|
||||
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
|
||||
"~/Scripts/jquery-{version}.js"));
|
||||
|
||||
bundles.Add(new ScriptBundle("~/bundles/vsjs").Include(
|
||||
"~/Scripts/vs/loader.js"));
|
||||
|
||||
bundles.Add(new ScriptBundle("~/bundles/bootstrap")
|
||||
.Include("~/Scripts/bootstrap.js")
|
||||
.Include("~/Scripts/respond.js"));
|
||||
|
||||
bundles.Add(new StyleBundle("~/Content/css")
|
||||
.Include("~/Content/bootstrap.css")
|
||||
.Include("~/Content/site.css"));
|
||||
|
||||
bundles.Add(new ScriptBundle("~/bundles/client").Include(
|
||||
"~/Scripts/tryout.js"));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,250 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props" Condition="Exists('..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props')" />
|
||||
<Import Project="..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>
|
||||
</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{E18A7397-1BBF-4060-856E-1A72307CDDEA}</ProjectGuid>
|
||||
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>LabWeb</RootNamespace>
|
||||
<AssemblyName>LabWeb</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<MvcBuildViews>false</MvcBuildViews>
|
||||
<UseIISExpress>true</UseIISExpress>
|
||||
<IISExpressSSLPort />
|
||||
<IISExpressAnonymousAuthentication />
|
||||
<IISExpressWindowsAuthentication />
|
||||
<IISExpressUseClassicPipelineMode />
|
||||
<UseGlobalApplicationHostFile />
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Azure.Documents.Client, Version=1.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Azure.DocumentDB.1.5.3\lib\net40\Microsoft.Azure.Documents.Client.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Azure.SqlDatabase.ElasticScale.Client, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Azure.SqlDatabase.ElasticScale.Client.1.2.0\lib\net45\Microsoft.Azure.SqlDatabase.ElasticScale.Client.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Transactions" />
|
||||
<Reference Include="System.Web.DynamicData" />
|
||||
<Reference Include="System.Web.Entity" />
|
||||
<Reference Include="System.Web.ApplicationServices" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Web.Abstractions" />
|
||||
<Reference Include="System.Web.Extensions" />
|
||||
<Reference Include="System.Web.Routing" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Web.Services" />
|
||||
<Reference Include="System.EnterpriseServices" />
|
||||
<Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http">
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http.WebRequest">
|
||||
</Reference>
|
||||
<Reference Include="System.Web.Helpers, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.Helpers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\Microsoft.AspNet.Mvc.5.2.3\lib\net45\System.Web.Mvc.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.Optimization">
|
||||
<HintPath>..\packages\Microsoft.AspNet.Web.Optimization.1.1.3\lib\net40\System.Web.Optimization.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\Microsoft.AspNet.Razor.3.2.3\lib\net45\System.Web.Razor.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.WebPages, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.WebPages.Deployment, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Deployment.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Razor.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="WebGrease">
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\WebGrease.1.5.2\lib\WebGrease.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Antlr3.Runtime">
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\Antlr.3.4.1.9004\lib\Antlr3.Runtime.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Controllers\HomeController.cs" />
|
||||
<Compile Include="Global.asax.cs">
|
||||
<DependentUpon>Global.asax</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Models\QueryModel.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Content\bootstrap.css" />
|
||||
<Content Include="Content\bootstrap.min.css" />
|
||||
<Content Include="Content\images\animatedCircle.gif" />
|
||||
<Content Include="favicon.ico" />
|
||||
<Content Include="fonts\glyphicons-halflings-regular.svg" />
|
||||
<Content Include="Global.asax" />
|
||||
<Content Include="Content\Site.css" />
|
||||
<Content Include="Scripts\bootstrap.js" />
|
||||
<Content Include="Scripts\jquery-2.2.1.js" />
|
||||
<Content Include="Scripts\jquery-2.2.1.min.js" />
|
||||
<Content Include="Scripts\respond.js" />
|
||||
<Content Include="Scripts\tryout.js" />
|
||||
<Content Include="Scripts\vs\base\ui\widgets\case-sensitive.svg" />
|
||||
<Content Include="Scripts\vs\base\ui\widgets\regex.svg" />
|
||||
<Content Include="Scripts\vs\base\ui\widgets\whole-word.svg" />
|
||||
<Content Include="Scripts\vs\base\worker\workerMain.js" />
|
||||
<Content Include="Scripts\vs\base\worker\workerServer.js" />
|
||||
<Content Include="Scripts\vs\base\worker\workerServer.nls.js" />
|
||||
<Content Include="Scripts\vs\base\worker\workerServer.nls.keys.js" />
|
||||
<Content Include="Scripts\vs\editor\contrib\find\cancelSelectionFind-inverse.svg" />
|
||||
<Content Include="Scripts\vs\editor\contrib\find\cancelSelectionFind.svg" />
|
||||
<Content Include="Scripts\vs\editor\contrib\find\close.svg" />
|
||||
<Content Include="Scripts\vs\editor\contrib\find\expando-collapsed.png" />
|
||||
<Content Include="Scripts\vs\editor\contrib\find\next.svg" />
|
||||
<Content Include="Scripts\vs\editor\contrib\find\previous.svg" />
|
||||
<Content Include="Scripts\vs\editor\editor.main.css" />
|
||||
<Content Include="Scripts\vs\editor\editor.main.js" />
|
||||
<Content Include="Scripts\vs\editor\editor.main.nls.js" />
|
||||
<Content Include="Scripts\vs\editor\editor.main.nls.keys.js" />
|
||||
<Content Include="Scripts\vs\editor\modes\monarch\monarchWorker.js" />
|
||||
<Content Include="Scripts\vs\editor\modes\monarch\monarchWorker.nls.js" />
|
||||
<Content Include="Scripts\vs\editor\modes\monarch\monarchWorker.nls.keys.js" />
|
||||
<Content Include="Scripts\vs\editor\worker\editorWorkerServer.js" />
|
||||
<Content Include="Scripts\vs\editor\worker\editorWorkerServer.nls.js" />
|
||||
<Content Include="Scripts\vs\editor\worker\editorWorkerServer.nls.keys.js" />
|
||||
<Content Include="Scripts\vs\languages\json\json.js" />
|
||||
<Content Include="Scripts\vs\languages\json\json.nls.js" />
|
||||
<Content Include="Scripts\vs\languages\json\json.nls.keys.js" />
|
||||
<Content Include="Scripts\vs\languages\json\jsonWorker.js" />
|
||||
<Content Include="Scripts\vs\languages\json\jsonWorker.nls.js" />
|
||||
<Content Include="Scripts\vs\languages\json\jsonWorker.nls.keys.js" />
|
||||
<Content Include="Scripts\vs\languages\nullWorker.js" />
|
||||
<Content Include="Scripts\vs\languages\nullWorker.nls.js" />
|
||||
<Content Include="Scripts\vs\languages\nullWorker.nls.keys.js" />
|
||||
<Content Include="Scripts\vs\loader.js" />
|
||||
<Content Include="Web.config">
|
||||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
<Content Include="Web.Debug.config">
|
||||
<DependentUpon>Web.config</DependentUpon>
|
||||
</Content>
|
||||
<Content Include="Web.Release.config">
|
||||
<DependentUpon>Web.config</DependentUpon>
|
||||
</Content>
|
||||
<Content Include="Views\Web.config" />
|
||||
<Content Include="Views\_ViewStart.cshtml" />
|
||||
<Content Include="Views\Shared\Error.cshtml" />
|
||||
<Content Include="Views\Shared\_Layout.cshtml" />
|
||||
<Content Include="Views\Home\Index.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="App_Data\" />
|
||||
<Folder Include="App_Start\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="fonts\glyphicons-halflings-regular.woff" />
|
||||
<Content Include="fonts\glyphicons-halflings-regular.ttf" />
|
||||
<Content Include="fonts\glyphicons-halflings-regular.eot" />
|
||||
<Content Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Scripts\jquery-2.2.1.min.map" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
|
||||
<Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
|
||||
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
|
||||
</Target>
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
|
||||
<WebProjectProperties>
|
||||
<UseIIS>True</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>52925</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>http://localhost:52925/</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>False</UseCustomServer>
|
||||
<CustomServerUrl>
|
||||
</CustomServerUrl>
|
||||
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
|
||||
</WebProjectProperties>
|
||||
</FlavorProperties>
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props'))" />
|
||||
</Target>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target> -->
|
||||
</Project>
|
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LabWeb.Models
|
||||
{
|
||||
public class QueryModel
|
||||
{
|
||||
[Required]
|
||||
[Display(Name = "Query")]
|
||||
public string Query { get; set; }
|
||||
|
||||
[Required]
|
||||
[Display(Name = "Documents")]
|
||||
public ICollection<string> Documents { get; set; }
|
||||
|
||||
[Required]
|
||||
[Display(Name = "Count")]
|
||||
public int Count { get; set; }
|
||||
|
||||
[Required]
|
||||
[Display(Name = "Error")]
|
||||
public string Error { get; set; }
|
||||
|
||||
[Display(Name = "StatusCode")]
|
||||
public int StatusCode { get; set; }
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("LabWeb")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("LabWeb")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("d4b182f7-0f13-415b-bcf7-086a23758024")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Revision and Build Numbers
|
||||
// by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,340 @@
|
|||
/* NUGET: BEGIN LICENSE TEXT
|
||||
*
|
||||
* Microsoft grants you the right to use these script files for the sole
|
||||
* purpose of either: (i) interacting through your browser with the Microsoft
|
||||
* website or online service, subject to the applicable licensing or use
|
||||
* terms; or (ii) using the files as included with a Microsoft product subject
|
||||
* to that product's license terms. Microsoft reserves all other rights to the
|
||||
* files not expressly granted by Microsoft, whether by implication, estoppel
|
||||
* or otherwise. Insofar as a script file is dual licensed under GPL,
|
||||
* Microsoft neither took the code under GPL nor distributes it thereunder but
|
||||
* under the terms set out in this paragraph. All notices and licenses
|
||||
* below are for informational purposes only.
|
||||
*
|
||||
* NUGET: END LICENSE TEXT */
|
||||
/*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */
|
||||
/*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */
|
||||
window.matchMedia = window.matchMedia || (function(doc, undefined){
|
||||
|
||||
var bool,
|
||||
docElem = doc.documentElement,
|
||||
refNode = docElem.firstElementChild || docElem.firstChild,
|
||||
// fakeBody required for <FF4 when executed in <head>
|
||||
fakeBody = doc.createElement('body'),
|
||||
div = doc.createElement('div');
|
||||
|
||||
div.id = 'mq-test-1';
|
||||
div.style.cssText = "position:absolute;top:-100em";
|
||||
fakeBody.style.background = "none";
|
||||
fakeBody.appendChild(div);
|
||||
|
||||
return function(q){
|
||||
|
||||
div.innerHTML = '­<style media="'+q+'"> #mq-test-1 { width: 42px; }</style>';
|
||||
|
||||
docElem.insertBefore(fakeBody, refNode);
|
||||
bool = div.offsetWidth == 42;
|
||||
docElem.removeChild(fakeBody);
|
||||
|
||||
return { matches: bool, media: q };
|
||||
};
|
||||
|
||||
})(document);
|
||||
|
||||
|
||||
|
||||
|
||||
/*! Respond.js v1.2.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */
|
||||
(function( win ){
|
||||
//exposed namespace
|
||||
win.respond = {};
|
||||
|
||||
//define update even in native-mq-supporting browsers, to avoid errors
|
||||
respond.update = function(){};
|
||||
|
||||
//expose media query support flag for external use
|
||||
respond.mediaQueriesSupported = win.matchMedia && win.matchMedia( "only all" ).matches;
|
||||
|
||||
//if media queries are supported, exit here
|
||||
if( respond.mediaQueriesSupported ){ return; }
|
||||
|
||||
//define vars
|
||||
var doc = win.document,
|
||||
docElem = doc.documentElement,
|
||||
mediastyles = [],
|
||||
rules = [],
|
||||
appendedEls = [],
|
||||
parsedSheets = {},
|
||||
resizeThrottle = 30,
|
||||
head = doc.getElementsByTagName( "head" )[0] || docElem,
|
||||
base = doc.getElementsByTagName( "base" )[0],
|
||||
links = head.getElementsByTagName( "link" ),
|
||||
requestQueue = [],
|
||||
|
||||
//loop stylesheets, send text content to translate
|
||||
ripCSS = function(){
|
||||
var sheets = links,
|
||||
sl = sheets.length,
|
||||
i = 0,
|
||||
//vars for loop:
|
||||
sheet, href, media, isCSS;
|
||||
|
||||
for( ; i < sl; i++ ){
|
||||
sheet = sheets[ i ],
|
||||
href = sheet.href,
|
||||
media = sheet.media,
|
||||
isCSS = sheet.rel && sheet.rel.toLowerCase() === "stylesheet";
|
||||
|
||||
//only links plz and prevent re-parsing
|
||||
if( !!href && isCSS && !parsedSheets[ href ] ){
|
||||
// selectivizr exposes css through the rawCssText expando
|
||||
if (sheet.styleSheet && sheet.styleSheet.rawCssText) {
|
||||
translate( sheet.styleSheet.rawCssText, href, media );
|
||||
parsedSheets[ href ] = true;
|
||||
} else {
|
||||
if( (!/^([a-zA-Z:]*\/\/)/.test( href ) && !base)
|
||||
|| href.replace( RegExp.$1, "" ).split( "/" )[0] === win.location.host ){
|
||||
requestQueue.push( {
|
||||
href: href,
|
||||
media: media
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
makeRequests();
|
||||
},
|
||||
|
||||
//recurse through request queue, get css text
|
||||
makeRequests = function(){
|
||||
if( requestQueue.length ){
|
||||
var thisRequest = requestQueue.shift();
|
||||
|
||||
ajax( thisRequest.href, function( styles ){
|
||||
translate( styles, thisRequest.href, thisRequest.media );
|
||||
parsedSheets[ thisRequest.href ] = true;
|
||||
makeRequests();
|
||||
} );
|
||||
}
|
||||
},
|
||||
|
||||
//find media blocks in css text, convert to style blocks
|
||||
translate = function( styles, href, media ){
|
||||
var qs = styles.match( /@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi ),
|
||||
ql = qs && qs.length || 0,
|
||||
//try to get CSS path
|
||||
href = href.substring( 0, href.lastIndexOf( "/" )),
|
||||
repUrls = function( css ){
|
||||
return css.replace( /(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g, "$1" + href + "$2$3" );
|
||||
},
|
||||
useMedia = !ql && media,
|
||||
//vars used in loop
|
||||
i = 0,
|
||||
j, fullq, thisq, eachq, eql;
|
||||
|
||||
//if path exists, tack on trailing slash
|
||||
if( href.length ){ href += "/"; }
|
||||
|
||||
//if no internal queries exist, but media attr does, use that
|
||||
//note: this currently lacks support for situations where a media attr is specified on a link AND
|
||||
//its associated stylesheet has internal CSS media queries.
|
||||
//In those cases, the media attribute will currently be ignored.
|
||||
if( useMedia ){
|
||||
ql = 1;
|
||||
}
|
||||
|
||||
|
||||
for( ; i < ql; i++ ){
|
||||
j = 0;
|
||||
|
||||
//media attr
|
||||
if( useMedia ){
|
||||
fullq = media;
|
||||
rules.push( repUrls( styles ) );
|
||||
}
|
||||
//parse for styles
|
||||
else{
|
||||
fullq = qs[ i ].match( /@media *([^\{]+)\{([\S\s]+?)$/ ) && RegExp.$1;
|
||||
rules.push( RegExp.$2 && repUrls( RegExp.$2 ) );
|
||||
}
|
||||
|
||||
eachq = fullq.split( "," );
|
||||
eql = eachq.length;
|
||||
|
||||
for( ; j < eql; j++ ){
|
||||
thisq = eachq[ j ];
|
||||
mediastyles.push( {
|
||||
media : thisq.split( "(" )[ 0 ].match( /(only\s+)?([a-zA-Z]+)\s?/ ) && RegExp.$2 || "all",
|
||||
rules : rules.length - 1,
|
||||
hasquery: thisq.indexOf("(") > -1,
|
||||
minw : thisq.match( /\(min\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/ ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ),
|
||||
maxw : thisq.match( /\(max\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/ ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" )
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
applyMedia();
|
||||
},
|
||||
|
||||
lastCall,
|
||||
|
||||
resizeDefer,
|
||||
|
||||
// returns the value of 1em in pixels
|
||||
getEmValue = function() {
|
||||
var ret,
|
||||
div = doc.createElement('div'),
|
||||
body = doc.body,
|
||||
fakeUsed = false;
|
||||
|
||||
div.style.cssText = "position:absolute;font-size:1em;width:1em";
|
||||
|
||||
if( !body ){
|
||||
body = fakeUsed = doc.createElement( "body" );
|
||||
body.style.background = "none";
|
||||
}
|
||||
|
||||
body.appendChild( div );
|
||||
|
||||
docElem.insertBefore( body, docElem.firstChild );
|
||||
|
||||
ret = div.offsetWidth;
|
||||
|
||||
if( fakeUsed ){
|
||||
docElem.removeChild( body );
|
||||
}
|
||||
else {
|
||||
body.removeChild( div );
|
||||
}
|
||||
|
||||
//also update eminpx before returning
|
||||
ret = eminpx = parseFloat(ret);
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
//cached container for 1em value, populated the first time it's needed
|
||||
eminpx,
|
||||
|
||||
//enable/disable styles
|
||||
applyMedia = function( fromResize ){
|
||||
var name = "clientWidth",
|
||||
docElemProp = docElem[ name ],
|
||||
currWidth = doc.compatMode === "CSS1Compat" && docElemProp || doc.body[ name ] || docElemProp,
|
||||
styleBlocks = {},
|
||||
lastLink = links[ links.length-1 ],
|
||||
now = (new Date()).getTime();
|
||||
|
||||
//throttle resize calls
|
||||
if( fromResize && lastCall && now - lastCall < resizeThrottle ){
|
||||
clearTimeout( resizeDefer );
|
||||
resizeDefer = setTimeout( applyMedia, resizeThrottle );
|
||||
return;
|
||||
}
|
||||
else {
|
||||
lastCall = now;
|
||||
}
|
||||
|
||||
for( var i in mediastyles ){
|
||||
var thisstyle = mediastyles[ i ],
|
||||
min = thisstyle.minw,
|
||||
max = thisstyle.maxw,
|
||||
minnull = min === null,
|
||||
maxnull = max === null,
|
||||
em = "em";
|
||||
|
||||
if( !!min ){
|
||||
min = parseFloat( min ) * ( min.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 );
|
||||
}
|
||||
if( !!max ){
|
||||
max = parseFloat( max ) * ( max.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 );
|
||||
}
|
||||
|
||||
// if there's no media query at all (the () part), or min or max is not null, and if either is present, they're true
|
||||
if( !thisstyle.hasquery || ( !minnull || !maxnull ) && ( minnull || currWidth >= min ) && ( maxnull || currWidth <= max ) ){
|
||||
if( !styleBlocks[ thisstyle.media ] ){
|
||||
styleBlocks[ thisstyle.media ] = [];
|
||||
}
|
||||
styleBlocks[ thisstyle.media ].push( rules[ thisstyle.rules ] );
|
||||
}
|
||||
}
|
||||
|
||||
//remove any existing respond style element(s)
|
||||
for( var i in appendedEls ){
|
||||
if( appendedEls[ i ] && appendedEls[ i ].parentNode === head ){
|
||||
head.removeChild( appendedEls[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
//inject active styles, grouped by media type
|
||||
for( var i in styleBlocks ){
|
||||
var ss = doc.createElement( "style" ),
|
||||
css = styleBlocks[ i ].join( "\n" );
|
||||
|
||||
ss.type = "text/css";
|
||||
ss.media = i;
|
||||
|
||||
//originally, ss was appended to a documentFragment and sheets were appended in bulk.
|
||||
//this caused crashes in IE in a number of circumstances, such as when the HTML element had a bg image set, so appending beforehand seems best. Thanks to @dvelyk for the initial research on this one!
|
||||
head.insertBefore( ss, lastLink.nextSibling );
|
||||
|
||||
if ( ss.styleSheet ){
|
||||
ss.styleSheet.cssText = css;
|
||||
}
|
||||
else {
|
||||
ss.appendChild( doc.createTextNode( css ) );
|
||||
}
|
||||
|
||||
//push to appendedEls to track for later removal
|
||||
appendedEls.push( ss );
|
||||
}
|
||||
},
|
||||
//tweaked Ajax functions from Quirksmode
|
||||
ajax = function( url, callback ) {
|
||||
var req = xmlHttp();
|
||||
if (!req){
|
||||
return;
|
||||
}
|
||||
req.open( "GET", url, true );
|
||||
req.onreadystatechange = function () {
|
||||
if ( req.readyState != 4 || req.status != 200 && req.status != 304 ){
|
||||
return;
|
||||
}
|
||||
callback( req.responseText );
|
||||
}
|
||||
if ( req.readyState == 4 ){
|
||||
return;
|
||||
}
|
||||
req.send( null );
|
||||
},
|
||||
//define ajax obj
|
||||
xmlHttp = (function() {
|
||||
var xmlhttpmethod = false;
|
||||
try {
|
||||
xmlhttpmethod = new XMLHttpRequest();
|
||||
}
|
||||
catch( e ){
|
||||
xmlhttpmethod = new ActiveXObject( "Microsoft.XMLHTTP" );
|
||||
}
|
||||
return function(){
|
||||
return xmlhttpmethod;
|
||||
};
|
||||
})();
|
||||
|
||||
//translate CSS
|
||||
ripCSS();
|
||||
|
||||
//expose update for re-running respond later on
|
||||
respond.update = ripCSS;
|
||||
|
||||
//adjust on resize
|
||||
function callMedia(){
|
||||
applyMedia( true );
|
||||
}
|
||||
if( win.addEventListener ){
|
||||
win.addEventListener( "resize", callMedia, false );
|
||||
}
|
||||
else if( win.attachEvent ){
|
||||
win.attachEvent( "onresize", callMedia );
|
||||
}
|
||||
})(this);
|
|
@ -0,0 +1,334 @@
|
|||
// DocDB query language mode
|
||||
var docdbMode = {
|
||||
displayName: 'DocumentDB Query Language',
|
||||
name: 'DocDB',
|
||||
mimeTypes: '',
|
||||
fileExtensions: '',
|
||||
|
||||
ignoreCase: true,
|
||||
lineComment: '--',
|
||||
|
||||
autoClosingPairs: [
|
||||
['"', '"'], ["'", "'"], ['@brackets']
|
||||
],
|
||||
|
||||
keywords: [
|
||||
"as", "asc", "between", "by", "case", "cast", "convert", "cross", "desc", "else", "end", "exists",
|
||||
"for", "from", "group", "having", "in", "inner", "insert", "into", "is", "join", "like", "limit",
|
||||
"offset", "order", "outer", "over", "select", "set", "then", "top", "update", "value", "when", "where", "with"
|
||||
],
|
||||
|
||||
keywordops: [
|
||||
"and", "or", "not"
|
||||
],
|
||||
|
||||
builtinFunctions: [
|
||||
// Mathematical Functions
|
||||
'abs', 'ceiling', 'exp', 'floor', 'log', 'log10', 'power', 'round', 'sign', 'sqrt', 'square',
|
||||
'trunc', 'acos', 'asin', 'atan', 'atn2', 'cos', 'cot', 'degrees', 'pi', 'radians', 'sin', 'tan',
|
||||
|
||||
// Type Checking Functions
|
||||
'is_array', 'is_bool', 'is_null', 'is_number', 'is_object', 'is_string', 'is_defined', 'is_primitive',
|
||||
|
||||
// Array Functions
|
||||
'array_concat', 'array_contains', 'array_length', 'array_slice',
|
||||
|
||||
// String Functions
|
||||
'concat', 'contains', 'endswith', 'index_of', 'left', 'length', 'lower', 'ltrim', 'replace', 'replicate', 'reverse', 'right', 'rtrim', 'startswith', 'substring', 'upper'
|
||||
],
|
||||
|
||||
builtins: [
|
||||
"root", "number", "string", "array", "object"
|
||||
],
|
||||
|
||||
operators: [
|
||||
"=", "<", ">", "<=", ">=", "!=", "<>", "+", "-", "*", "/", "%", "||", "|", "&", "^"
|
||||
],
|
||||
|
||||
brackets: [
|
||||
{ open: '(', close: ')', token: 'delimiter.parenthesis' },
|
||||
{ open: '{', close: '}', token: 'delimiter.curly' },
|
||||
{ open: '[', close: ']', token: 'delimiter.square' }
|
||||
],
|
||||
|
||||
decpart: /\d(_?\d)*/,
|
||||
decimal: /0|@decpart/,
|
||||
|
||||
// Common regular expressions
|
||||
symbols: /[=><!~?:&|+\-*\/\^%\.]+/,
|
||||
|
||||
// The main tokenizer for our languages
|
||||
tokenizer: {
|
||||
root: [
|
||||
// Identifiers and keywords
|
||||
[/[a-zA-Z_][\w_]*/, {
|
||||
cases: {
|
||||
"true|false|null|undefined": "predefined",
|
||||
"@keywords": "keyword",
|
||||
"@keywordops": "keyword",
|
||||
"@builtinFunctions": "ext-docdb-builtinFunction",
|
||||
"@builtins": "predefined",
|
||||
"@default": ""
|
||||
}
|
||||
}],
|
||||
|
||||
// Whitespace
|
||||
{ include: "@whitespace" },
|
||||
|
||||
// Strings
|
||||
[/"/, 'string', '@dstring.\"'],
|
||||
[/'/, 'string', '@sstring.\''],
|
||||
|
||||
// Operators
|
||||
[/[{}()\[\]]/, "@brackets"],
|
||||
[/@symbols/, {
|
||||
cases: {
|
||||
"@operators": "operator",
|
||||
"@default": ""
|
||||
}
|
||||
}],
|
||||
|
||||
// Numbers
|
||||
[/0[xX][0-9a-fA-F](_?[0-9a-fA-F])*/, 'number.hex'],
|
||||
[/0[dD]@decpart/, 'number'],
|
||||
[/@decimal((\.@decpart)?([eE][\-+]?@decpart)?)/, {
|
||||
cases: {
|
||||
'$1': 'number.float',
|
||||
'@default': 'number'
|
||||
}
|
||||
}],
|
||||
|
||||
|
||||
// Delimiter: after number because of .\d floats
|
||||
[/[,.]/, "delimiter"],
|
||||
|
||||
],
|
||||
|
||||
// Single quote strings (also used for symbols)
|
||||
// sstring.<kind> where kind is 'sq' (single quote) or 's' (symbol)
|
||||
sstring: [
|
||||
[/[^\\']+/, 'string'],
|
||||
[/\\./, 'string.invalid'],
|
||||
[/'/, {
|
||||
cases: {
|
||||
'$#==$S2': { token: 'string', bracket: '@close', next: '@pop' },
|
||||
'@default': 'string'
|
||||
}
|
||||
}]
|
||||
],
|
||||
|
||||
// Double quoted "string".
|
||||
// dstring.<kind>.<delim> where kind is 'd' (double quoted), 'x' (command), or 's' (symbol)
|
||||
// and delim is the ending delimiter (" or `)
|
||||
dstring: [
|
||||
[/[^\\"]+/, 'string'],
|
||||
[/\\./, 'string.invalid'],
|
||||
[/"/, {
|
||||
cases: {
|
||||
'$#==$S2': { token: 'string', bracket: '@close', next: '@pop' },
|
||||
'@default': 'string'
|
||||
}
|
||||
}]
|
||||
],
|
||||
|
||||
whitespace: [
|
||||
[/[\s]+/, 'white'],
|
||||
[/--.*$/, 'comment'],
|
||||
],
|
||||
}
|
||||
};
|
||||
require.config({
|
||||
baseUrl: "../Scripts"
|
||||
});
|
||||
|
||||
var editor,
|
||||
resultHighlight;
|
||||
|
||||
$(function () {
|
||||
async(initializeQueryEditor, null);
|
||||
async(initializeResultEditor, null);
|
||||
});
|
||||
|
||||
// Reset button
|
||||
$(".btn-reset").on("click", function () {
|
||||
editor.setValue('');
|
||||
});
|
||||
|
||||
function showLoading() {
|
||||
$("#results-pane").css("text-align", "center");
|
||||
$(".centerer").css("display", "inline-block");
|
||||
$(".centered").css("display", "inline-block");
|
||||
}
|
||||
|
||||
function hideLoading() {
|
||||
$("#results-pane").css("text-align", "left");
|
||||
$(".centerer").css("display", "none");
|
||||
$(".centered").css("display", "none");
|
||||
}
|
||||
|
||||
// Run button
|
||||
var docs = null;
|
||||
var error = null;
|
||||
$(".btn-run").on("click", function () {
|
||||
var query = editor.getValue();
|
||||
if (query === "") {
|
||||
$(".result-pager").addClass("hidden");
|
||||
setErrorPaneWithAnimation(JSON.stringify({ "errors": [{ "severity": "Error", "location": { "start": 0, "end": 0 }, "code": "SC1002", "message": "Syntax error, unexpected end-of-file." }] }, null, 2));
|
||||
} else {
|
||||
showLoading();
|
||||
$.ajax({
|
||||
url: "/sql/demo/Home/Query",
|
||||
data: {
|
||||
query: query
|
||||
},
|
||||
dataType: "json"
|
||||
}).done(function (msg, textStatus, jqXhr) {
|
||||
hideLoading();
|
||||
if (msg.Error === null) {
|
||||
error = null;
|
||||
$("#results-pane .monaco-editor").removeClass("error-pane");
|
||||
if (msg.Documents.length > 0) {
|
||||
$(".left-arrow").removeClass("disabled-arrow");
|
||||
$(".right-arrow").removeClass("disabled-arrow");
|
||||
docs = msg.Documents;
|
||||
$(".current-page").text(1);
|
||||
$(".left-arrow").addClass("disabled-arrow");
|
||||
$(".total-page").text(docs.length);
|
||||
if (docs.length === 1) {
|
||||
$(".right-arrow").addClass("disabled-arrow");
|
||||
}
|
||||
$(".result-pager").removeClass("hidden");
|
||||
setResultPaneWithAnimation(docs[0]);
|
||||
} else {
|
||||
docs = msg.Documents;
|
||||
$(".result-pager").addClass("hidden");
|
||||
$(".current-page").text(0);
|
||||
$(".total-page").text(0);
|
||||
setResultPaneWithAnimation("{ }");
|
||||
}
|
||||
} else {
|
||||
// Hide display, show error
|
||||
docs = null;
|
||||
$(".result-pager").addClass("hidden");
|
||||
error = JSON.stringify(JSON.parse(('{' + msg.Error.substring(0, msg.Error.lastIndexOf('}') + 1) + '}').replace('Message', '"Message"')), null, 2);
|
||||
setErrorPaneWithAnimation(error);
|
||||
}
|
||||
}).fail(function (msg) {
|
||||
hideLoading();
|
||||
docs = null;
|
||||
$(".result-pager").addClass("hidden");
|
||||
error = JSON.stringify(JSON.parse(msg.Error || { "errors": [{ "severity": "Error", "location": { "start": 0, "end": 0 }, "code": "SC1002", "message": "Syntax error, unexpected end-of-file." }] }), null, 2);
|
||||
setErrorPaneWithAnimation(JSON.stringify(JSON.parse(error), null, 2));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Left arrow
|
||||
$(".left-arrow").on("click", function () {
|
||||
if (parseInt($(".current-page").text()) > 1 && docs !== null) {
|
||||
var currentPage = parseInt($(".current-page").text());
|
||||
var prevPage = currentPage - 1;
|
||||
// Animate
|
||||
setResultPaneWithAnimation(docs[prevPage - 1]);
|
||||
|
||||
$(".current-page").text(prevPage);
|
||||
if (prevPage === 1) {
|
||||
$(".left-arrow").addClass("disabled-arrow");
|
||||
}
|
||||
if (currentPage === docs.length) {
|
||||
$(".right-arrow").removeClass("disabled-arrow");
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Right arrow
|
||||
$(".right-arrow").on("click", function () {
|
||||
if (parseInt($(".current-page").text()) < parseInt($(".total-page").text()) && docs !== null) {
|
||||
var currentPage = parseInt($(".current-page").text());
|
||||
// Animate
|
||||
setResultPaneWithAnimation(docs[currentPage]);
|
||||
|
||||
$(".current-page").text(currentPage + 1);
|
||||
if (currentPage === 1) {
|
||||
$(".left-arrow").removeClass("disabled-arrow");
|
||||
}
|
||||
if ((currentPage + 1) >= docs.length) {
|
||||
$(".right-arrow").addClass("disabled-arrow");
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function setErrorPaneWithAnimation(result) {
|
||||
$(".results-container").fadeOut(300, function () {
|
||||
resultHighlight.setValue(result);
|
||||
$("#results-pane .monaco-editor").addClass("error-pane");
|
||||
$(".results-container").fadeIn(300, function () {
|
||||
$("#results-pane .monaco-editor").addClass("error-pane");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setResultPaneWithAnimation(result) {
|
||||
$(".results-container").fadeOut(300, function () {
|
||||
resultHighlight.setValue(result);
|
||||
$(".results-container").fadeIn(300);
|
||||
});
|
||||
}
|
||||
|
||||
function setResultPaneWithoutAnimation(result) {
|
||||
resultHighlight.setValue(result);
|
||||
}
|
||||
|
||||
function initializeQueryEditor() {
|
||||
var container = document.getElementById("vsEditor");
|
||||
|
||||
require(["vs/editor/editor.main"], function () {
|
||||
editor = Monaco.Editor.create(container, {
|
||||
mode: docdbMode,
|
||||
wrappingColumn: 0,
|
||||
scrollBeyondLastLine: false,
|
||||
automaticLayout: true
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function initializeResultEditor() {
|
||||
var resultContainer = document.getElementById("results-pane");
|
||||
|
||||
require(["vs/editor/editor.main"], function () {
|
||||
resultHighlight = Monaco.Editor.create(resultContainer, {
|
||||
mode: "application/json",
|
||||
lineNumbers: false,
|
||||
wrappingColumn: 0,
|
||||
scrollBeyondLastLine: false,
|
||||
readOnly: true,
|
||||
roundedSelection: false,
|
||||
automaticLayout: true,
|
||||
scrollbar: {
|
||||
// other values are "visible" or "hidden"
|
||||
vertical: "auto",
|
||||
horizontal: "hidden"
|
||||
},
|
||||
value: ""
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function async(func, callback) {
|
||||
setTimeout(function () {
|
||||
func();
|
||||
if (callback) { callback(); }
|
||||
}, 0);
|
||||
}
|
||||
|
||||
String.prototype.capitalizeFirstLetter = function () {
|
||||
return this.charAt(0).toUpperCase() + this.slice(1);
|
||||
};
|
||||
String.format = function () {
|
||||
var s = arguments[0];
|
||||
for (var i = 0; i < arguments.length - 1; i++) {
|
||||
var reg = new RegExp("\\{" + i + "\\}", "gm");
|
||||
s = s.replace(reg, arguments[i + 1]);
|
||||
}
|
||||
return s;
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><style type="text/css">.st0{opacity:0;fill:#F6F6F6;} .st1{fill:#F6F6F6;} .st2{fill:#424242;}</style><g id="outline"><rect class="st0" width="16" height="16"/><path class="st1" d="M14.176 5.592c-.555-.6-1.336-.904-2.322-.904-.258 0-.521.024-.784.072-.246.044-.479.101-.7.169-.228.07-.432.147-.613.229-.22.099-.389.196-.512.284l-.419.299v2.701c-.086.108-.162.223-.229.344l-2.45-6.354h-2.394l-3.753 9.804v.598h3.025l.838-2.35h2.167l.891 2.35h3.237l-.001-.003c.305.092.633.15.993.15.344 0 .671-.049.978-.146h2.853v-4.903c-.001-.975-.271-1.763-.805-2.34z"/></g><g id="icon_x5F_bg"><path class="st2" d="M7.611 11.834l-.891-2.35h-3.562l-.838 2.35h-1.095l3.217-8.402h1.02l3.24 8.402h-1.091zm-2.531-6.814l-.044-.135-.038-.156-.029-.152-.024-.126h-.023l-.021.126-.032.152-.038.156-.044.135-1.307 3.574h2.918l-1.318-3.574z"/><path class="st2" d="M13.02 11.834v-.938h-.023c-.199.352-.456.62-.771.806s-.673.278-1.075.278c-.313 0-.588-.045-.826-.135s-.438-.212-.598-.366-.281-.338-.363-.551-.124-.442-.124-.688c0-.262.039-.502.117-.721s.198-.412.36-.58.367-.308.615-.419.544-.19.888-.237l1.811-.252c0-.273-.029-.507-.088-.7s-.143-.351-.252-.472-.241-.21-.396-.267-.325-.085-.513-.085c-.363 0-.714.064-1.052.193s-.638.31-.904.54v-.984c.082-.059.196-.121.343-.188s.312-.128.495-.185.378-.104.583-.141.407-.056.606-.056c.699 0 1.229.194 1.588.583s.539.942.539 1.661v3.902h-.96zm-1.454-2.83c-.273.035-.498.085-.674.149s-.313.144-.41.237-.165.205-.202.334-.055.276-.055.44c0 .141.025.271.076.393s.124.227.22.316.215.16.357.211.308.076.495.076c.242 0 .465-.045.668-.135s.378-.214.524-.372.261-.344.343-.557.123-.442.123-.688v-.609l-1.465.205z"/></g></svg>
|
После Ширина: | Высота: | Размер: 1.7 KiB |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><polygon fill="#F6F6F6" points="13.64,7.396 12.169,2.898 10.706,3.761 11.087,2 6.557,2 6.936,3.762 5.473,2.898 4,7.396 5.682,7.554 4.513,8.561 5.013,9 2,9 2,14 7,14 7,10.747 7.978,11.606 8.82,9.725 9.661,11.602 13.144,8.562 11.968,7.554"/><g fill="#424242"><path d="M12.301 6.518l-2.772.262 2.086 1.788-1.594 1.392-1.201-2.682-1.201 2.682-1.583-1.392 2.075-1.788-2.771-.262.696-2.126 2.358 1.392-.599-2.784h2.053l-.602 2.783 2.359-1.392.696 2.127z"/><rect x="3" y="10" width="3" height="3"/></g></svg>
|
После Ширина: | Высота: | Размер: 564 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><style type="text/css">.st0{opacity:0;fill:#F6F6F6;} .st1{fill:#F6F6F6;} .st2{fill:#424242;}</style><g id="outline"><rect class="st0" width="16" height="16"/><path class="st1" d="M16 4.022v-3.022h-16.014v3.022h3.046l-3.043 7.945h-.004v.01l.015 1.023h-.014v1.991h16.014v-3.023h-1v-7.946h1zm-5.914 5.301c0 .233-.023.441-.066.595-.047.164-.099.247-.127.284l-.078.069-.151.026-.115-.017-.139-.137c-.031-.078-.112-.332-.112-.566 0-.254.091-.561.126-.656l.069-.141.109-.082.178-.027c.077 0 .117.014.177.056l.087.179.051.237-.009.18zm-3.695-5.301v2.893l-1.116-2.893h1.116zm-3.026 7.02h1.573l.351.926h-2.254l.33-.926zm8.635-4.354c-.206-.2-.431-.38-.695-.512-.396-.198-.853-.298-1.355-.298-.215 0-.423.02-.621.058v-1.914h2.671v2.666z"/></g><g id="icon_x5F_bg"><rect x="13" y="4" class="st2" width="1" height="8"/><path class="st2" d="M11.225 8.387c-.078-.299-.199-.562-.36-.786s-.365-.401-.609-.53-.534-.193-.866-.193c-.198 0-.38.024-.547.073-.165.049-.316.117-.453.205-.136.088-.257.194-.365.318l-.179.258v-3.154h-.893v7.422h.893v-.575l.126.175c.087.102.189.19.304.269.117.078.249.14.398.186.149.046.314.068.498.068.353 0 .666-.071.937-.212.272-.143.499-.338.682-.586.183-.25.321-.543.414-.879.093-.338.14-.703.14-1.097-.001-.342-.04-.663-.12-.962zm-1.479-.607c.151.071.282.176.39.314.109.14.194.313.255.517.051.174.082.371.089.587l-.007.125c0 .327-.033.62-.1.869-.067.246-.161.453-.278.614-.117.162-.26.285-.421.366-.322.162-.76.166-1.069.015-.153-.075-.286-.175-.393-.296-.085-.096-.156-.216-.218-.367 0 0-.179-.447-.179-.947 0-.5.179-1.002.179-1.002.062-.177.136-.318.224-.43.114-.143.256-.259.424-.345.168-.086.365-.129.587-.129.19 0 .364.037.517.109z"/><rect x=".987" y="2" class="st2" width="14.013" height="1.023"/><rect x=".987" y="12.968" class="st2" width="14.013" height="1.023"/><path class="st2" d="M1.991 12.031l.728-2.031h2.219l.778 2.031h1.082l-2.485-7.158h-.941l-2.441 7.086-.025.072h1.085zm1.827-5.609h.022l.914 2.753h-1.841l.905-2.753z"/></g></svg>
|
После Ширина: | Высота: | Размер: 2.0 KiB |