WHERE clause Azure CosmosDB Mongo subdocument - c#

Using Azure CosmosDB Mongo.
I have text fields in documents and subdocuments. Which I want be able to search.
Using Contains works fine on the parent document properties. But doesn't seem to look at children at all. And doesn't even return any error.
Document:
{
"TextField1": "this will be found in search",
"Comments": [{
"Comment": "amazing post, let's see if this can be foundtoo",
}, {
"Comment": "thanks",
}]
}
Search:
var postFilter = Builders<MyObject>.Filter.Where(p => p.TextField1.ToLowerInvariant().Contains(searchText.ToLowerInvariant())) |
Builders<MyObject>.Filter.Where(p => p.Comments.Any(pc => pc.Comment.ToLowerInvariant().Contains(searchText.ToLowerInvariant())));
var posts = await Posts.Find(postFilter).ToListAsync();
If I use the above code with search "found". It will return the document.
If I use it with search "foundtoo". It will not return anything.
PS: I have tried using Text and it is not supported and comes back as an error.

Cosmos Mongo Db may have not implemented whole commands as native MongoDB. I tried the code you mentioned also get the same result as you mentioned.
Using Contains works fine on the parent document properties. But doesn't seem to look at children at all. And doesn't even return any error.
Please have a try to use the follow code to do that. I test it on my side, it works correctly.
var filterResult = collection.Find(Builders<MyObject>.Filter.ElemMatch("Comments", Builders<Mydata>.Filter.Where(p=>p.Comment.ToLowerInvariant().Contains(searchText.ToLowerInvariant())))).ToList();
The following is my detail steps:
1.Create a .net project and reference MongoDB.Driver more detail please refer to packages.config.
2.Add the Mydata and MyObject class
public class MyObject
{
public string TextField1;
public Mydata[] Comments;
[JsonProperty(PropertyName = "id")]
public string Id;
}
public class Mydata
{
public string Comment;
}
3.Test with following code.
string connectionString = "connection string";
MongoClientSettings settings = MongoClientSettings.FromUrl(new MongoUrl(connectionString));
settings.SslSettings = new SslSettings { EnabledSslProtocols = SslProtocols.Tls12 };
var mongoClient = new MongoClient(settings);
var searchText = "foundtoo";
var db = mongoClient.GetDatabase("dbname");
var collection = db.GetCollection<MyObject>("collectionName");
var mydata1 = new Mydata {Comment = "Thank you"};
var mydata2 = new Mydata {Comment = "amazing post, let's see if this can be foundtoo"};
var list = new List<Mydata> {mydata1, mydata2};
Mydata[] mydatas = {mydata1,mydata2};
collection.InsertOneAsync(new MyObject
{
Id = Guid.NewGuid().ToString(),
TextField1 = "this will be found in search",
Comments = mydatas
}).Wait();
var filterResult = collection.Find(Builders<MyObject>.Filter.ElemMatch("Comments", Builders<Mydata>.Filter.Where(p=>p.Comment.ToLowerInvariant().Contains(searchText.ToLowerInvariant())))).ToList();
packages.config
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="MongoDB.Bson" version="2.4.4" targetFramework="net461" />
<package id="MongoDB.Driver" version="2.4.4" targetFramework="net461" />
<package id="MongoDB.Driver.Core" version="2.4.4" targetFramework="net461" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net461" />
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net461" />
</packages>

Related

Using C# to find a single node with its path and id attribute value

Say I have the following xml, and I would like to get the value of the version attribute for the single node with id of Pkg1, which is expected to be 1.2.3.
<project>
<nugets id='test'> </nugets>
<packages>
<package id='test1' version='1'/>
<package id='Pkg1' version='1.2.3'/>
<package id='Pkg1Test' version='4.5.6'/>
</packages>
</project>
Below is my attempt, but the target node is in the field of OuterXml as a string for the target node:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlStr);
string path = "project/packages/package[#id='Pkg1']"; // target node has id=Pkg1
var targetNode = xmlDoc.SelectSingleNode(path);
Console.WriteLine($"{targetNode.OuterXml}"); // prints out the target node as string.
This seems work: string version = targetN.Attributes["version"].Value, is this the right way of getting the attribute value, and why the node is in the targetNode.OuterXml?
Here is a working code to get what you need:
private const string str = #"
<project>
<nugets id='test'> </nugets>
<packages>
<package id='test1' version='1'/>
<package id='Pkg1' version='1.2.3'/>
<package id='Pkg1Test' version='4.5.6'/>
</packages>
</project>";
private static void Test()
{
var el = XElement.Parse(str);
var packages = el.Element("packages")?
.Elements("package")
.ToList();
var package = packages?
.FirstOrDefault(x => x.Attribute("id")?.Value == "Pkg1");
var id = package?.Attribute("version")?.Value;
Console.Write(id);
}

Azure Data Lake store create folder via C# script

I am trying to create new folder inside my datalake store, there is no error in code however nothing is being reflected in datalake store.
Code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Threading;
using Microsoft.Azure.Management.DataLake.Store;
using Microsoft.Azure.Management.DataLake.Store.Models;
using Microsoft.Rest.Azure.Authentication;
namespace test_dlstore
{
class Program
{
private static DataLakeStoreAccountManagementClient _adlsClient;
private static DataLakeStoreFileSystemManagementClient _adlsFileSystemClient;
private static string _adlsAccountName;
private static string _subId;
private static void Main(string[] args)
{
_adlsAccountName = "mycreds#mysite.com";
_subId = "2342342-97ce-a54b2-ba6e-234234234234234";
string localFolderPath = #"C:\myfolder\"; // TODO: Make sure this exists and can be overwritten.
string localFilePath = Path.Combine(localFolderPath, "try.txt");
string remoteFolderPath = "adl://mystore.azuredatalakestore.net/myfolder";
string remoteFilePath = Path.Combine(remoteFolderPath, "try.txt");
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
var tenant_id = "my-tenant-id";
var nativeClientApp_clientId = "1950a258-227b-4e31-a9cf-717495945fc2";
var activeDirectoryClientSettings = ActiveDirectoryClientSettings.UsePromptOnly(nativeClientApp_clientId, new Uri("urn:ietf:wg:oauth:2.0:oob"));
var creds = UserTokenProvider.LoginWithPromptAsync(tenant_id, activeDirectoryClientSettings).Result;
_adlsClient = new DataLakeStoreAccountManagementClient(creds) { SubscriptionId = _subId };
_adlsFileSystemClient = new DataLakeStoreFileSystemManagementClient(creds);
Console.WriteLine(ListAdlStoreAccounts());
Console.WriteLine(AppendToFile("adl://mystore.azuredatalakestore.net/myfolder/stage/testfile.txt", "abcdefghijklmnopqrstuvwxyz"));
CreateDirectory("adl://mystore.azuredatalakestore.net/myfolder/newdir");
Console.ReadLine();
}
// Append to file
public static string AppendToFile(string path, string content)
{
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(content)))
{
Console.WriteLine(_adlsAccountName, path, content);
Console.WriteLine(path);
Console.WriteLine(content);
_adlsFileSystemClient.FileSystem.AppendAsync(_adlsAccountName, path, stream);
return "Tried";
}
}
public static string CreateDirectory(string path)
{
_adlsFileSystemClient.FileSystem.MkdirsAsync(_adlsAccountName, path);
return "Tried Creating directory.";
}
}
}
After execution of above code, program exits with no error. The connection is being made.
Also it is displaying datalake stores that are present but not able to do anything at all over the data lake stores.
I am new to azure please help me out.
I do a demo test on my side,it works correctly on side. The following is my detail steps:
Preparation:
Registry an AD application and assign role to applcation, more details please
refer to Azure official tutorials. After that we can get tenantId, appId, secretKey from the Azure Portal.
Steps:
1.Create an C# console project
2.Referece the Microsoft.Azure.Management.DataLake.Store SDK, more details please refer to packages.config file section.
3.Add the follow code
var applicationId = "appid";
var secretKey = "secretkey";
var tenantId = "tenantid";
var adlsAccountName = "adlsAccount Name";
var creds = ApplicationTokenProvider.LoginSilentAsync(tenantId, applicationId, secretKey).Result;
var adlsFileSystemClient = new DataLakeStoreFileSystemManagementClient(creds,clientTimeoutInMinutes:60);
adlsFileSystemClient.FileSystem.Mkdirs(adlsAccountName, "/tomtest/newfolder");
4.Run the test code
5.Check from the azure portal.
Packages.config
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Azure.Management.DataLake.Store" version="2.2.0" targetFramework="net452" />
<package id="Microsoft.IdentityModel.Clients.ActiveDirectory" version="3.13.8" targetFramework="net452" />
<package id="Microsoft.Rest.ClientRuntime" version="2.3.8" targetFramework="net452" />
<package id="Microsoft.Rest.ClientRuntime.Azure" version="3.3.7" targetFramework="net452" />
<package id="Microsoft.Rest.ClientRuntime.Azure.Authentication" version="2.2.0-preview" targetFramework="net452" />
<package id="Newtonsoft.Json" version="9.0.2-beta1" targetFramework="net452" />
</packages>

Encrypting XML custom configuration file

I have this method which populates an object from my XML "custom configuration" config file:
public static BindingList<StationConfiguration> GetStationsFromConfigFile()
{
string xmlDocumentText = File.ReadAllText(GetConfigFilePath());
var doc = new XmlDocument();
doc.LoadXml(xmlDocumentText);
BindingList<StationConfiguration> stations = new BindingList<StationConfiguration>();
foreach (XmlNode node in doc.DocumentElement["StationsSection"].ChildNodes[0].ChildNodes)
{
stations.Add(
new StationConfiguration(
node.Attributes["Comment"].Value
, node.Attributes["FtpUsername"].Value
, node.Attributes["FtpPassword"].Value
, node.Attributes["DestinationFolderPath"].Value
));
}
return stations;
}
As you can see, I'm using File.ReadAllText to pull the contents of the XML config file into a String.
This all works well for a non-encrypted config file. But now I need to encrypt it. The way MSDN suggests doing that begins like this:
System.Configuration.Configuration config =
ConfigurationManager.OpenExeConfiguration(
ConfigurationUserLevel.None);
(Incidentally, when I look at the config.FilePath property, it shows me the correct path of the XML config file, just having an extra ".config" extension for some strange reason. It ends with ".exe.config.config" for some odd reason. But I digress...)
This is my StationConfigurationSection class:
public class StationConfigurationSection : ConfigurationSection
{
[ConfigurationProperty("Stations", IsDefaultCollection = false)]
[ConfigurationCollection(typeof(StationCollection),
AddItemName = "add",
ClearItemsName = "clear",
RemoveItemName = "remove")]
public StationCollection Stations
{
get
{
return (StationCollection)base["Stations"];
}
}
public override bool IsReadOnly()
{
return false;
}
}
My complete XML config file looks like this:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="StationsSection" type="EcFtpClient.StationConfigurationSection, EcFtpClient" />
</configSections>
<StationsSection>
<Stations>
<add Comment="ABC" FtpUsername="eliezer" FtpPassword="secret" DestinationFolderPath="C:\Users\eliezer\Desktop\local dest" FtpTimeoutInSeconds="30" FtpHostname="ftp://192.168.1.100/" FtpFolderPath="" />
</Stations>
</StationsSection>
<startup>
<supportedRuntime version="v2.0.50727" />
</startup>
<appSettings>
<add key="NameOfService" value="ECClient" />
<add key="PollingFrequencyInSeconds" value="60" />
</appSettings>
</configuration>
I would like to use the MSDN System.Configuration approach, since it makes en/de-cryption very easy, but how can I blend their approach with what I have to make it work?
-- UPDATE --
I've got the loading of the file but am still stuck when it comes to saving the file.I've changed the loading method (at the very top of this question) to this:
public static BindingList<StationConfiguration> GetStationsFromConfigFile()
{
Configuration config = ConfigurationManager.OpenExeConfiguration(GetConfigFilePath());
StationConfigurationSection stationsConfig = (StationConfigurationSection)config.GetSection("StationsSection");
var stationCollection = ((StationCollection)stationsConfig.Stations);
BindingList<StationConfiguration> stationsToReturn = new BindingList<StationConfiguration>();
for (int index = 0; index < stationCollection.Count; index++)
{
stationsToReturn.Add(
new StationConfiguration(
stationCollection[index].Comment,
stationCollection[index].FtpUsername,
stationCollection[index].FtpPassword,
stationCollection[index].DestinationFolderPath)
);
return stationsToReturn;
}
What that gets me is the ability to load a file regardless of whether it's encrypted or not - it loads successfully. That's great.
But I'm still not sure how to get saving working. Here's my save method:
public static void SaveStationsToConfigFile(BindingList<StationConfiguration> updatedStations, bool isConfigToBeEncrypted)
{
string configFilePath = GetConfigFilePath();
var xDoc = XDocument.Load(configFilePath);
var xDeclaration = xDoc.Declaration;
var xElement = xDoc.XPathSelectElement("//StationsSection/Stations");
// clear out existing station configurations
xDoc.Descendants("Stations").Nodes().Remove();
foreach (var station in updatedStations)
{
xElement.Add(new XElement("add",
new XAttribute("Comment", station.Station),
new XAttribute("FtpUsername", station.Username),
new XAttribute("FtpPassword", station.Password),
new XAttribute("DestinationFolderPath", station.FolderName),
new XAttribute("FtpTimeoutInSeconds", 30),
new XAttribute("FtpHostname", GetEnvironmentAppropriateFtpHostName()),
new XAttribute("FtpFolderPath", GetEnvironmentAppropriateFtpFolderPath())
));
}
xDoc.Declaration = xDeclaration;
xDoc.Save(configFilePath);
}
And in order to save it with the protection/encrpytion, I need to do something like this - in other words, using the System.Configuration.Configuration objects:
stationsConfig.SectionInformation.ProtectSection("RsaProtectedConfigurationProvider");
stationsConfig.SectionInformation.ForceSave = true;
objConfig.Save(ConfigurationSaveMode.Modified);
But I'm currently still doing the save with XDocument.Save...
Is there a way to convert my XDocument into a System.Configuration.Configuration compatible object?The hack that comes to mind is after the call to XDocument.Save - to load it and save it again using the System.Configuration.Configuration stuff. But that's a hack...
I believe you have to play along with the configuration settings framework. Rather than trying to open the xml file yourself, you need to create a descendant of ConfigurationSection.
That way it will read and write from the encrypted configuration for you.
It looks like you already started that route (EcFtpClient.StationConfigurationSection), but you didn't include any of that code.

Retrieve the Current App version from Package

While I can get the assembly version using the following code
var assembly = typeof(App).GetTypeInfo().Assembly;
var assemblyVersion = assembly.GetCustomAttribute<AssemblyFileVersionAttribute>().Version;
I would like to retrieve the Version from Package.appxmanifest in this case 1.0.0.4
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/2010/manifest" xmlns:m2="http://schemas.microsoft.com/appx/2013/manifest">
<Identity Name="zzz" Publisher="CN=zzz" Version="1.0.0.4" />
I expected to have access to Windows.ApplicationModel, but this is not available to me
Here's what you can do to retrieve the version in code:
using Windows.ApplicationModel;
public static string GetAppVersion()
{
Package package = Package.Current;
PackageId packageId = package.Id;
PackageVersion version = packageId.Version;
return string.Format("{0}.{1}.{2}.{3}", version.Major, version.Minor, version.Build, version.Revision);
}
Reference: http://www.michielpost.nl/PostDetail_67.aspx

Extracting values from XML returned by azure service management API

i have tried several methods of trying to extract values from an XML file but none of them seem to work. I am using C#. The XML Is as follows
<?xml version="1.0" encoding="utf-8"?>
<HostedService xmlns="http://schemas.microsoft.com/windowsazure">
<Url>hosted-service-url</Url>
<ServiceName>hosted-service-name</ServiceName>
<HostedServiceProperties>
<Description>description</Description>
<Location>location</Location>
<AffinityGroup>affinity-group</AffinityGroup>
<Label>label</Label>
</HostedServiceProperties>
</HostedService>
I would like to retrieve
hosted-service-url,
hosted-service-name,
description,
location,
affinity-group and
label
What would be the best of way of retrieving these values?
Edit :
Thanks L.B that method works perfectly. However i have just been told i will have to use the larger XML that is below.
<?xml version="1.0" encoding="utf-8"?>
<HostedService xmlns="http://schemas.microsoft.com/windowsazure">
<Url>hosted-service-url</Url>
<ServiceName>hosted-service-name</ServiceName>
<HostedServiceProperties>
<Description>description</Description>
<Location>location</Location>
<AffinityGroup>affinity-group</AffinityGroup>
<Label>base-64-encoded-name-of-the-service</Label>
</HostedServiceProperties>
<Deployments>
<Deployment>
<Name>deployment-name</Name>
<DeploymentSlot>deployment-slot</DeploymentSlot>
<PrivateID>deployment-id</PrivateID>
<Status>deployment-status</Status>
<Label>base64-encoded-deployment-label</Label>
<Url>deployment-url</Url>
<Configuration>base-64-encoded-configuration-file</Configuration>
<RoleInstanceList>
<RoleInstance>
<RoleName>role-name</RoleName>
<InstanceName>role-instance-name</InstanceName>
<InstanceStatus>instance-status</InstanceStatus>
</RoleInstance>
</RoleInstanceList>
<UpgradeDomainCount>upgrade-domain-count</UpgradeDomainCount>
<RoleList>
<Role>
<RoleName>role-name</RoleName>
<OsVersion>operating-system-version</OsVersion>
</Role>
</RoleList>
<SdkVersion>sdk-version-used-to-create-package</SdkVersion>
<InputEndpointList>
<InputEndpoint>
<RoleName>role-name</RoleName>
<Vip>virtual-ip-address</Vip>
<Port>port-number</Port>
</InputEndpoint>
…
</InputEndpointList>
<Locked>deployment-write-allowed-status</Locked>
<RollbackAllowed>rollback-operation-allowed</RollbackAllowed>
</Deployment>
</Deployments>
</HostedService>
My final question is, there is several repeated tags, such as ,
how can i differentiate between them?
you can use Xml to Linq to parse your xml string. For ex,
var xElem = XElement.Load(new StringReader(xml));
var ns = XNamespace.Get("http://schemas.microsoft.com/windowsazure");
var obj = new
{
ServiceName = xElem.Descendants(ns + "ServiceName").First().Value,
Description = xElem.Descendants(ns + "Description").First().Value,
};
or you can use XmlSerializer
XmlSerializer xs = new XmlSerializer(typeof(HostedService), "http://schemas.microsoft.com/windowsazure");
var obj2 = (HostedService)xs.Deserialize(new StringReader(xml));
public class HostedService
{
public string Url;
public string ServiceName;
public HostedServiceProperties HostedServiceProperties;
}
public class HostedServiceProperties
{
public string Description;
public string Location;
public string AffinityGroup;
public string Label;
}
Maybe you can try samples from XmlDocument ( http://msdn.microsoft.com/en-us/library/d271ytdx.aspx) and and LINQ to XML -( http://msdn.microsoft.com/en-us/library/bb669152.aspx) first and than apply it to your case.

Categories