Added Last years labs as starting point

This commit is contained in:
matthew.batten@intergen.co.nz 2017-04-18 09:09:43 +12:00
Родитель a0c8cbb335
Коммит 229b6e06f2
147 изменённых файлов: 28562 добавлений и 13 удалений

15
.gitignore поставляемый
Просмотреть файл

@ -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
@ -145,11 +143,6 @@ publish/
*.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/
@ -242,7 +238,6 @@ _Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/

29
.vscode/tasks.json поставляемый Normal file
Просмотреть файл

@ -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 &quot;sounds like&quot; 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 &quot;engineer&quot;
into this text box and press <code>enter</code>. </p>
<p>You will notice that you did not get any results - let&#39;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&lt;DocumentSearchResult&lt;JobResult&gt;&gt; ExecuteSearch(string query, List&lt;FacetGroup&gt; facets = null, PositionDistanceSearch geoSearch = null)
{
//TO DO - Place holder for search
return new DocumentSearchResult&lt;JobResult&gt;();
}
</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&lt;DocumentSearchResult&lt;JobResult&gt;&gt; ExecuteSearch(string query, List&lt;FacetGroup&gt; facets = null, PositionDistanceSearch geoSearch = null)
{
var searchParameters = new SearchParameters()
{
QueryType = QueryType.Full,
SearchMode = SearchMode.All,
};
using (var indexClient = GetClient())
{
return await indexClient.Documents.SearchAsync&lt;JobResult&gt;(query, searchParameters);
}
}
</code></pre>
<p>Press <code>F5</code> to run the application, and type &quot;engineer&quot; 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 &quot;enginer&quot; (<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 &quot;kownsil&quot;. Press <code>enter</code> to search. You will see results related to &quot;counsel&quot;.</p>
<p>Azure Search supports phonetic (&quot;sounds like&quot;) 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 &quot;auto-complete&quot; textbox. Azure Search supports auto-complete suggestions out of the box - let&#39;s see how it&#39;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&lt;List&lt;string&gt;&gt; ExecuteSuggest(string query)
{
//TO DO - Place holder for suggest
return new List&lt;string&gt;();
}
</code></pre>
<p>Modify the method to look like this:</p>
<pre><code class="lang-csharp">public async Task&lt;List&lt;string&gt;&gt; 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&lt;JobResult&gt;(query, &quot;sg&quot;, sp);
// Extract the text suggestions from the result set
return results.Results.Select(e =&gt; 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 &quot;suggestor&quot; that has been pre-created on the Azure Search
index that defines which fields should be used to return a set of &quot;suggestions&quot; 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 &quot;eng&quot; 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&#39;t it be nice to be able to filter the results further? Luckily this is also very easy with Azure Search using the
&quot;facets&quot; 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&lt;DocumentSearchResult&lt;JobResult&gt;&gt; ExecuteSearch(string query, List&lt;FacetGroup&gt; facets = null, PositionDistanceSearch geoSearch = null)
{
var searchParameters = new SearchParameters()
{
QueryType = QueryType.Full,
SearchMode = SearchMode.All,
};
using (var indexClient = GetClient())
{
return await indexClient.Documents.SearchAsync&lt;JobResult&gt;(query, searchParameters);
}
}
</code></pre>
<p>Add some facets to the query...</p>
<pre><code class="lang-csharp">Facets = FacetDefinitions.Select(e =&gt; e.Key).ToList()
</code></pre>
<p>...so the method looks like this:</p>
<pre><code class="lang-csharp">public async Task&lt;DocumentSearchResult&lt;JobResult&gt;&gt; ExecuteSearch(string query, List&lt;FacetGroup&gt; facets = null, PositionDistanceSearch geoSearch = null)
{
var searchParameters = new SearchParameters()
{
QueryType = QueryType.Full,
SearchMode = SearchMode.All,
Facets = FacetDefinitions.Select(e =&gt; e.Key).ToList()
};
using (var indexClient = GetClient())
{
return await indexClient.Documents.SearchAsync&lt;JobResult&gt;(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&lt;string, string&gt; FacetDefinitions = new Dictionary&lt;string, string&gt;()
{
{&quot;agency&quot;, &quot;Agency&quot;},
{&quot;posting_type&quot;, &quot;Internal/External&quot;},
{&quot;civil_service_title&quot;, &quot;Common Job Title&quot;}
};
</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&#39;s run the application and see what this looks like. </p>
<p>Press <code>F5</code> to start the application. Type &quot;engineer&quot; 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&#39;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&lt;DocumentSearchResult&lt;JobResult&gt;&gt; ExecuteSearch(string query, List&lt;FacetGroup&gt; facets = null, PositionDistanceSearch geoSearch = null)
{
var searchParameters = new SearchParameters()
{
QueryType = QueryType.Full,
SearchMode = SearchMode.All,
Facets = FacetDefinitions.Select(e =&gt; e.Key).ToList(),
Filter = CreateFilter(facets, geoSearch)
};
using (var indexClient = GetClient())
{
return await indexClient.Documents.SearchAsync&lt;JobResult&gt;(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 &#39;DEPT OF HEALTH/MENTAL HYGIENE&#39;) and (posting_type eq &#39;Internal&#39;)
</code></pre><pre><code>(agency eq &#39;DEPT OF HEALTH/MENTAL HYGIENE&#39; or agency eq &#39;DEPT OF ENVIRONMENT PROTECTION&#39; or agency eq &#39;DEPT OF DESIGN &amp; CONSTRUCTION&#39; or agency eq &#39;HOUSING PRESERVATION &amp; DVLPMNT&#39; or agency eq &#39;DEPT OF INFO TECH &amp; TELECOMM&#39;)
</code></pre><pre><code>(agency eq &#39;DEPT OF ENVIRONMENT PROTECTION&#39;) and (posting_type eq &#39;Internal&#39;) and (civil_service_title eq &#39;ASSOCIATE PROJECT MANAGER&#39;)
</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&#39;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 &gt; 0)
{
query.Append(&quot; and &quot;);
}
var lat = geoSearch.GeoPoint.Position.Latitude;
var lon = geoSearch.GeoPoint.Position.Longitude;
query.Append($&quot;geo.distance(geo_location, geography&#39;POINT({lon} {lat})&#39;) le {geoSearch.Radius}&quot;);
}
</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&#39;POINT(-73.9593882020563 40.7079201657325)&#39;) 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 &quot;pin&quot; 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)

Двоичные данные
Labs/Azure Search/images/auto_suggest.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 5.9 KiB

Двоичные данные
Labs/Azure Search/images/azure_search_service.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 16 KiB

Двоичные данные
Labs/Azure Search/images/engineer_with_facets.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 56 KiB

Двоичные данные
Labs/Azure Search/images/first_engineer_search.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 29 KiB

Двоичные данные
Labs/Azure Search/images/jobs_lab_azure_search1.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 84 KiB

Двоичные данные
Labs/Azure Search/images/jobs_lab_azure_search2.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 37 KiB

Двоичные данные
Labs/Azure Search/images/jobs_lab_azure_search3.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 175 KiB

Двоичные данные
Labs/Azure Search/images/kownsil_search.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 27 KiB

Двоичные данные
Labs/Azure Search/images/map_filter.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 47 KiB

Двоичные данные
Labs/Azure Search/images/map_results.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 264 KiB

Двоичные данные
Labs/Azure Search/images/map_results_raduis.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 74 KiB

Двоичные данные
Labs/Azure Search/images/second_enginer_search.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 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

Двоичные данные
Labs/Azure Search/src/JobSearch/Assets/SplashScreen.scale-200.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 32 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.5 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.5 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 359 B

Двоичные данные
Labs/Azure Search/src/JobSearch/Assets/StoreLogo.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 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)' &lt; '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(() =&gt; Property)</c></item>
/// <item><c>NotifyChanged((VM x) =&gt; 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 =&gt; Output | Output &lt;= 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 &lt;= 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&lt;T&gt;(this IEnumerable&lt;T&gt; 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&lt;T&gt;(this IEnumerable&lt;T&gt; 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&lt;dynamic&gt; 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[&quot;DocumentDBName&quot;], ConfigurationManager.AppSettings[&quot;DocumentDBCollectionName&quot;]);
IDocumentQuery&lt;dynamic&gt; 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&#39;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 &lt;T&gt; on docQuery.ExecuteNextAsync&lt;T&gt;()
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 &quot;path&quot; 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 = &quot;ef811f1d-d271-4e1f-baf7-4d5ac0b6c840&quot;
</code></pre>
<p><img src="images/id_filter.png" alt=""></p>
<h3 id="part-three">Part Three</h3>
<p>Let&#39;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 = &quot;WKS-1604&quot;
</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&#39;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 &quot;Power Meter&quot; logs:</p>
<pre><code class="lang-SQL">SELECT perfLog
FROM machinelogs
JOIN perfLog IN machinelogs.logs
WHERE perfLog.counterType = &quot;Power Meter&quot;
</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 = &quot;LogicalDisk&quot; OR perfLog.counterType = &quot;PhysicalDisk&quot;)
</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 (&quot;LogicalDisk&quot;, &quot;PhysicalDisk&quot;)
</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 &quot;% Disk Time&quot; property.Because this property name contains white space, we must use special index to address it:</p>
<pre><code class="lang-SQL">WHERE perfLog[&quot;% Disk Time&quot;] ...
</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[&quot;% Disk Time&quot;] 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[&quot;% Disk Time&quot;] 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 {
&quot;FreeSpacePercent&quot;: perfLog[&quot;% Free Space&quot;],
&quot;FreeMegabytes&quot;: perfLog[&quot;Free Megabytes&quot;],
&quot;CurrentDiskQueueLength&quot;: perfLog[&quot;Current Disk Queue Length&quot;],
&quot;Power&quot; : perfLog[&quot;Power&quot;],
&quot;PowerBudget&quot; : perfLog[&quot;Power Budget&quot;]
} 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)

Двоичные данные
Labs/DocumentDB/images/50_results.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 43 KiB

Двоичные данные
Labs/DocumentDB/images/DocumentExplorer.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 131 KiB

Двоичные данные
Labs/DocumentDB/images/QueryExplorer.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 80 KiB

Двоичные данные
Labs/DocumentDB/images/ScriptExplorer.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 68 KiB

Двоичные данные
Labs/DocumentDB/images/between_filter.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 36 KiB

Двоичные данные
Labs/DocumentDB/images/filter1.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 32 KiB

Двоичные данные
Labs/DocumentDB/images/filter2.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 10 KiB

Двоичные данные
Labs/DocumentDB/images/filter3.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 10 KiB

Двоичные данные
Labs/DocumentDB/images/filter4.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 36 KiB

Двоичные данные
Labs/DocumentDB/images/filter5.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 36 KiB

Двоичные данные
Labs/DocumentDB/images/home_controller.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 43 KiB

Двоичные данные
Labs/DocumentDB/images/home_page.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 19 KiB

Двоичные данные
Labs/DocumentDB/images/id_error.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 12 KiB

Двоичные данные
Labs/DocumentDB/images/id_filter.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 32 KiB

Двоичные данные
Labs/DocumentDB/images/id_results.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 6.0 KiB

Двоичные данные
Labs/DocumentDB/images/join.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 36 KiB

Двоичные данные
Labs/DocumentDB/images/join2.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 10 KiB

Двоичные данные
Labs/DocumentDB/images/no_results.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 8.5 KiB

Двоичные данные
Labs/DocumentDB/images/order_by.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 35 KiB

Двоичные данные
Labs/DocumentDB/images/projection.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 11 KiB

Двоичные данные
Labs/DocumentDB/images/select_from_collection.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 32 KiB

Двоичные данные
Labs/DocumentDB/images/select_from_logs.PNG Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 32 KiB

Двоичные данные
Labs/DocumentDB/images/top_1.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 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;
}

6816
Labs/DocumentDB/src/LabWeb/Content/bootstrap.css поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

20
Labs/DocumentDB/src/LabWeb/Content/bootstrap.min.css поставляемый Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Двоичные данные
Labs/DocumentDB/src/LabWeb/Content/images/animatedCircle.gif Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 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")]

2014
Labs/DocumentDB/src/LabWeb/Scripts/bootstrap.js поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

9831
Labs/DocumentDB/src/LabWeb/Scripts/jquery-2.2.1.js поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

4
Labs/DocumentDB/src/LabWeb/Scripts/jquery-2.2.1.min.js поставляемый Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -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 = '&shy;<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

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше