MongoDB + C#: Query inside a document - c#

It seems I don't understand how I can get a value from a collection inside a document. I am using mongoDB in C#.
Here is my code:
var jimi = new Document();
jimi["Firstname"] = "Jimi";
jimi["Lastname"] = "James";
jimi["Pets"] = new[]
{
new Document().Append("Type", "Cat").Append("Name", "Fluffy"),
new Document().Append("Type", "Dog").Append("Name", "Barky"),
new Document().Append("Type", "Gorilla").Append("Name", "Bananas"),
};
test.Insert(jimi);
var query = new Document().Append("Pets.Type","Cat");
So my query will look for the pet cat. But I am not sure how I can get the name of my cat. I tried a few things but I mostly get the whole document back.
Thanks in advance,
Pickels

This isn't as elegant as I'd like as I'm still learning about MongoDB myself but it does show you one way to get the property you wanted.
[TestFixture]
public class When_working_with_nested_documents
{
[Test]
public void Should_be_able_to_fetch_properties_of_nested_objects()
{
var mongo = new Mongo();
mongo.Connect();
var db = mongo.getDB("tests");
var people = db.GetCollection("people");
var jimi = new Document();
jimi["Firstname"] = "Jimi";
jimi["Lastname"] = "James";
jimi["Pets"] = new[]
{
new Document().Append("Type", "Cat").Append("Name", "Fluffy"),
new Document().Append("Type", "Dog").Append("Name", "Barky"),
new Document().Append("Type", "Gorilla").Append("Name", "Bananas"),
};
people.Insert(jimi);
var query = new Document();
query["Pets.Type"] = "Cat";
var personResult = people.FindOne(query);
Assert.IsNotNull(personResult);
var petsResult = (Document[])personResult["Pets"];
var pet = petsResult.FindOne("Type", "Cat");
Assert.IsNotNull(pet);
Assert.AreEqual("Fluffy", pet["Name"]);
}
}
public static class DocumentExtensions
{
public static Document FindOne(this Document[] documents, string key, string value)
{
foreach(var document in documents)
{
var v = document[key];
if (v != null && v.Equals(value))
{
return document;
}
}
return null;
}
}

Related

IList<T> return as a generic

I'm a beginner for coding ,and I was trying to create a search engine , but there s a part I dont know how to solve it that returns an IList as a generic,
public IList<T> Search<T>(string textSearch)
{
IList<T> list = new List<T>();
var result = new DataTable();
using (Analyzer analyzer = new PanGuAnalyzer())
{
var queryParser = new QueryParser(Version.LUCENE_30, "FullText", analyzer);
queryParser.AllowLeadingWildcard = true;
var query = queryParser.Parse(textSearch);
var collector = TopScoreDocCollector.Create(1000, true);
Searcher.Search(query, collector);
var matches = collector.TopDocs().ScoreDocs;
result.Columns.Add("Title");
result.Columns.Add("Starring");
result.Columns.Add("ID");
foreach (var item in matches)
{
var id = item.Doc;
var doc = Searcher.Doc(id);
var row = result.NewRow();
row["Title"] = doc.GetField("Title").StringValue;
row["Starring"] = doc.GetField("Starring").StringValue;
row["ID"] = doc.GetField("ID").StringValue;
result.Rows.Add(row);
}
}
return result;
}
but in this code , I couldn't return result ,it says Cannot Implicitly convert type 'Data.DataTable' to 'Generic.IList',An explicit conversion exists.so how can I solve this?
I guess you don't want to support generics since it doesn't make sense and is impossible. You have a class, for example Film, then return a List<Film>, you don't need the DataTable:
public IList<Film> SearchFilms(string textSearch)
{
IList<Film> list = new List<Film>();
using (Analyzer analyzer = new PanGuAnalyzer())
{
var queryParser = new QueryParser(Version.LUCENE_30, "FullText", analyzer);
queryParser.AllowLeadingWildcard = true;
var query = queryParser.Parse(textSearch);
var collector = TopScoreDocCollector.Create(1000, true);
Searcher.Search(query, collector);
var matches = collector.TopDocs().ScoreDocs;
foreach (var item in matches)
{
var film = new Film();
var id = item.Doc;
var doc = Searcher.Doc(id);
film.Title = doc.GetField("Title").StringValue;
film.Starring = doc.GetField("Starring").StringValue;
film.ID = doc.GetField("ID").StringValue;
list.Add(film);
}
}
return list;
}
Your return statement should be
result.AsEnumerable().ToList();
Don't forget to add namespace
using System.Linq;

Trouble Fetching Value in Variable

Basically here's my code which I'm having trouble with. Insanely new to mongoDB and would love to understand how to get values out of a JSON string that is returns in the variable 'line'.
public string get_data()
{
var client = new MongoClient();
var db = client.GetDatabase("test");
var collection = db.GetCollection<BsonDocument>("metacorp");
var cursor = collection.Find("{'movie_name' : 'Hemin'}").ToCursor();
var line = "";
foreach (var document in cursor.ToEnumerable())
{
using (var stringWriter = new StringWriter())
using (var jsonWriter = new JsonWriter(stringWriter))
{
var context = BsonSerializationContext.CreateRoot(jsonWriter);
collection.DocumentSerializer.Serialize(context, document);
line = stringWriter.ToString();
}
}
var js = new JavaScriptSerializer();
var d = js.Deserialize<dynamic>(line);
var a = d["movie_name"];
return line;
}
This is the output I get if I return line:
{ "_id" : ObjectId("58746dcafead398e4d7233f5"), "movie_name" : "Hemin"
}
I want to be able to fetch the value 'Hemin' into 'a'.
I know this is not what you're asking for but since you're using the c# driver then I would recommend the following. Assumes you have a c# class corresponding to metacorp collection or at least a serializer that handles it. Hope it helps.
var client = new MongoClient();
var db = client.GetDatabase("test");
var collection = db.GetCollection<MetaCorp>("metacorp");
var m = collection.SingleOrDefault(x => x.Movie_Name == "Hemin"); // Assuming 0 or 1 with that name. Use Where otherwise
var movieName = "Not found";
if(m!= null)
movieName = m.Movie_Name;
You could have your dto class for movie ans just fetch the data from collection:
public class Movie
{
public ObjectId Id { get; set; }
public string movie_name { get; set;}
}
...
var client = new MongoClient();
var db = client.GetDatabase("test");
var collection = db.GetCollection<BsonDocument>("metacorp");
var movies = collection.Find(x=>x.movie_name == "Hemin").ToEnumerable();

How to add/update milestones in a feature using rally rest API and C#?

I am not able to add or update milestones field for the Features in the Rally. If anyone having the code available using C# to update the same, please share with me. I am searching and doing from last one week with no luck.
When I am trying to add/Update milestones in the Features. I am getting the error as "Could not read: Could not read referenced object null". My code is as follows:-
public DynamicJsonObject UpdateFeaturesbyName(string fea, string bFun)
{
//getting list of Feature.
Request feat = new Request("PortfolioItem/Feature");
feat.Query = new Query("Name", Query.Operator.Equals, fea);
QueryResult TCSResults = restApi.Query(feat);
foreach (var res in TCSResults.Results)
{
var steps = res["Milestones"];
Request tsteps = new Request(steps);
QueryResult tstepsResults = restApi.Query(tsteps);
foreach (var item in tstepsResults.Results)
{
}
if (res.Name == fea)
{
var targetFeature = TCSResults.Results.FirstOrDefault();
DynamicJsonObject toUpdate = new DynamicJsonObject();
//toUpdate["Milestones"] = "";
// CreateResult createResult = restApi.Create(steps._ref, toUpdate);
// String contentRef = steps._ref;
//String contentRef = createResult._ref;
string[] value = null;
string AccCri = string.Empty;
if (!string.IsNullOrWhiteSpace(bFun))
{
value = bFun.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);
foreach (string item in value)
{
//if (string.IsNullOrWhiteSpace(AccCri))
// AccCri = item;
//else
// AccCri = AccCri + "<br/>" + item;
if (!string.IsNullOrWhiteSpace(item))
{
//Query for Milestone.
Request ms = new Request("Milestone");
ms.Fetch = new List<string>() { "Name", "ObjectID" };
ms.Query = new Query("Name", Query.Operator.Equals, item);
QueryResult msResults = restApi.Query(ms);
var targetMLResult = msResults.Results.FirstOrDefault();
long MLOID = targetMLResult["ObjectID"];
DynamicJsonObject tarML = restApi.GetByReference("Milestone", MLOID, "Name", "_ref", "DisplayColor");
DynamicJsonObject targetML = new DynamicJsonObject();
targetML["Name"] = tarML["Name"];
//targetML["_ref"] = tarML["_ref"];
targetML["_ref"] = "/milestone/" + Convert.ToString(MLOID);
targetML["DisplayColor"] = tarML["DisplayColor"];
// Grab collection of existing Milestones.
var existingMilestones = targetFeature["Milestones"];
long targetOID = targetFeature["ObjectID"];
// Milestones collection on object is expected to be a System.Collections.ArrayList.
var targetMLArray = existingMilestones;
var tagList2 = targetMLArray["_tagsNameArray"];
tagList2.Add(targetML);//
//targetMLArray.Add(targetML);
targetMLArray["_tagsNameArray"] = tagList2;
toUpdate["Milestones"] = targetMLArray;
OperationResult updateResult = restApi.Update(res._ref, toUpdate);
bool resp = updateResult.Success;
}
}
}
//toUpdate["c_AcceptanceCriteria"] = AccCri;
//OperationResult updateResult = restApi.Update(res._ref, toUpdate);
}
}
var features = TCSResults.Results.Where(p => p.Name == fea).FirstOrDefault();
var featuresref = features._ref;
return features;
}
Now that v3.1.1 of the toolkit has been released you can use the AddToCollection method to do this.
Otherwise, you can still always just update the full collection. The value should be an arraylist of objects with _ref properties.
Check out this example (which adds tasks to defects, but should be very similar to what you're doing): https://github.com/RallyCommunity/rally-dot-net-rest-apps/blob/master/UpdateTaskCollectionOnDefect/addTaskOnDefect.cs

Nested XML from list with needed data on multiple lines

I need to format an XML to a given hierarchy from a list (List<>). The list contains, for the banking information, data spread across multiple rows as shown in the image.
The XML output needs to be formatted like this:
<ROOT>
<DocumentElement>
<Supplier>
<Company>STV</Company>
<Code>000199</Code>
<Name>TrafTrans</Name>
<BankAccounts>
<SupplierBankAccount>
<Bban>220-012510-63</Bban>
<Name>B1</Name>
</SupplierBankAccount>
<SupplierBankAccount>
<Bban>RUIL</Bban>
<Name>RUIL</Name>
</SupplierBankAccount>
</BankAccounts>
<SupplierAddresses>
<SupplierAddress>
<Type>PostalAddress</Type>
<Name>Loc TrafTrans</Name>
<IsDefault>true</IsDefault>
<AddressParts>
<SupplierAddressPart>
<AddressPartKey>STREET_NAME</AddressPartKey>
<AddressPartText>Somewhere</AddressPartText>
</SupplierAddressPart>
<SupplierAddressPart>
<AddressPartKey>COUNTRY</AddressPartKey>
<AddressPartText>SPAIN</AddressPartText>
</SupplierAddressPart>
</AddressParts>
</SupplierAddress>
</SupplierAddresses>
</Supplier>
</DocumentElement>
</ROOT>
I already have a method that converts a list to an XML and returns a string. But the problem is that this only formats one item from the list and there could be additional info in the following items.
public static string SuppliersToXML(List<SupplierItem> supplier)
{
CultureInfo ci = new CultureInfo("en-US");
XmlDocument doc = new XmlDocument();
var root = doc.CreateElement("ROOT");
var rootNode = doc.AppendChild(root);
var docElem = doc.CreateElement("DocumentElement");
var docElemNode = rootNode.AppendChild(docElem);
foreach (var item in supplier)
{
var supplierElem = doc.CreateElement("Supplier");
var companyElem = (XmlNode)doc.CreateElement("Company");
companyElem.InnerText = item.Company.ToString();
//more code...
supplierElem.AppendChild(companyElem);
//more code...
}
return doc.OuterXml;
}
I have found the answer myself, it may not be the prettiest code ever written. But it does the job.
public static string SuppliersToXML(List<SupplierItem> supplier)
{
//A distinct select is needed because bank info can be on multiple lines. So first a list is generated with correct info except for bank information
List<SupplierItem> distinctsupplier = supplier
.GroupBy(p => p.Code)
.Select(g => g.First())
.ToList();
CultureInfo ci = new CultureInfo("en-US");
XmlDocument doc = new XmlDocument();
var root = doc.CreateElement("ROOT");
var rootNode = doc.AppendChild(root);
var docElem = doc.CreateElement("DocumentElement");
var docElemNode = rootNode.AppendChild(docElem);
foreach (var item in distinctsupplier)
{
var supplierElem = doc.CreateElement("Supplier");
var companyElem = (XmlNode)doc.CreateElement("Company");
companyElem.InnerText = item.Company.ToString();
var codeElem = (XmlNode)doc.CreateElement("Code");
codeElem.InnerText = item.Code.ToString();
var codeName = (XmlNode)doc.CreateElement("Name");
codeName.InnerText = item.Name.ToString();
//supplieridentifier part
var supplierIdsElem = doc.CreateElement("SupplierIdentifiers");
var supplierIdElem = doc.CreateElement("SupplierIdentifier");
var supplierIdValueElem = (XmlNode)doc.CreateElement("SupplierIDValue");
supplierIdValueElem.InnerText = item.SupplierIDValue;
//supplieraddress part
var supplierAddressesElem = doc.CreateElement("SupplierAddresses");
var supplierAddressElem = doc.CreateElement("SupplierAddress");
var supplierTypeValueElem = (XmlNode)doc.CreateElement("Type");
supplierTypeValueElem.InnerText = item.Type;
var supplierNameValueElem = (XmlNode)doc.CreateElement("Name");
supplierNameValueElem.InnerText = item.AddressName;
var supplierDefaultValueElem = (XmlNode)doc.CreateElement("IsDefault");
supplierDefaultValueElem.InnerText = item.AddressIsDefault;
//address part
var AddressPartElem = doc.CreateElement("AddressParts");
var supplierAddressPartsElem = doc.CreateElement("SupplierAddressPart");
//Street
var AddressPartElemStreetKeyElem = (XmlNode)doc.CreateElement("AddressPartKey");
AddressPartElemStreetKeyElem.InnerText = item.AddressStreetKey;
var AddressPartElemStreetValueElem = (XmlNode)doc.CreateElement("AddressPartText");
AddressPartElemStreetValueElem.InnerText = item.AddressStreetText;
//Country
var AddressPartElemCountryKeyElem = (XmlNode)doc.CreateElement("AddressPartKey");
AddressPartElemCountryKeyElem.InnerText = item.AddressCountryKey;
var AddressPartElemCountryValueElem = (XmlNode)doc.CreateElement("AddressPartText");
AddressPartElemCountryValueElem.InnerText = item.AddressCountryText;
//add elements to supplierelem
supplierElem.AppendChild(companyElem);
supplierElem.AppendChild(codeElem);
supplierElem.AppendChild(codeName);
//bankaccounts part
var bankAccountElem = doc.CreateElement("BankAccounts");
//select all rows that contain multiple lines, so the bank info can be extracted, for a certain supplier
var bankInformation = supplier.Where(s => s.Code == item.Code).ToList();
foreach (var bankitem in bankInformation)
{
if (item.Code == bankitem.Code)
{
var bankAccountSupplElem = doc.CreateElement("SupplierBankAccount");
var bankAccountBbanElem = doc.CreateElement("Bban");
var bankAccountBbanValueElem = (XmlNode)doc.CreateElement("Bban");
bankAccountBbanValueElem.InnerText = bankitem.Bban;
var bankAccountNameElem = doc.CreateElement("Name");
var bankAccountNameValueElem = (XmlNode)doc.CreateElement("Name");
bankAccountNameValueElem.InnerText = bankitem.BankName;
var bankAccountElemNode = supplierElem.AppendChild(bankAccountElem);
var bankAccountSupplElemNode = bankAccountElemNode.AppendChild(bankAccountSupplElem);
bankAccountSupplElemNode.AppendChild(bankAccountBbanValueElem);
bankAccountSupplElemNode.AppendChild(bankAccountNameValueElem);
}
}
//add address elements to supplierelem
var supplierAddressesElemNode = supplierElem.AppendChild(supplierAddressesElem);
var supplierAddressElemNode = supplierAddressesElemNode.AppendChild(supplierAddressElem);
supplierAddressElemNode.AppendChild(supplierTypeValueElem);
supplierAddressElemNode.AppendChild(supplierNameValueElem);
supplierAddressElemNode.AppendChild(supplierDefaultValueElem);
//add addressparts to supplieraddressesnode
//Street
var AddressPartElemNode = supplierAddressElemNode.AppendChild(AddressPartElem);
var supplierAddressPartsElemNode = AddressPartElemNode.AppendChild(supplierAddressPartsElem);
supplierAddressPartsElemNode.AppendChild(AddressPartElemStreetKeyElem);
supplierAddressPartsElemNode.AppendChild(AddressPartElemStreetValueElem);
//Country (first reinitialize supplieraddresspart)
supplierAddressPartsElem = doc.CreateElement("SupplierAddressPart");
var supplierAddressPartsCountryElemNode = AddressPartElemNode.AppendChild(supplierAddressPartsElem);
supplierAddressPartsCountryElemNode.AppendChild(AddressPartElemCountryKeyElem);
supplierAddressPartsCountryElemNode.AppendChild(AddressPartElemCountryValueElem);
//add all supplierelements to docelemnode
docElemNode.AppendChild(supplierElem);
}
return doc.OuterXml;
}

OpenXmlPowerTools DocumentBuilder merging documents on a seperate page

I am trying to merge 4 word documents and force content of each document starts at new page. But instead of appending text from each document to a separate page, it adds text from all docs to one page.
Like this:
How can i fix it?
This is the code:
public class HomeController : Controller
{
public void DocMerger()
{
var source1 = Server.MapPath(Url.Content("~/App_Data/1.docx")); //source 1
var source2 = Server.MapPath(Url.Content("~/App_Data/2.docx")); //source 2
var source3 = Server.MapPath(Url.Content("~/App_Data/3.docx")); //source 3
var source4 = Server.MapPath(Url.Content("~/App_Data/4.docx")); //source 4
var merged = Server.MapPath(Url.Content("~/App_Data/merged.docx")); //merged
var f1 = new FileInfo(source1);
var f2 = new FileInfo(source2);
var f3 = new FileInfo(source3);
var f4 = new FileInfo(source4);
//Use DocumentBuilder and merge the files
var sources = new List<OpenXmlPowerTools.Source>()
{
new Source(new WmlDocument(f1.FullName),false),
new Source(new WmlDocument(f2.FullName),false),
new Source(new WmlDocument(f3.FullName),false),
new Source(new WmlDocument(f4.FullName),false)
};
var mergedDocument = DocumentBuilder.BuildDocument(sources);
mergedDocument.SaveAs(merged); //save merged data as merged.docx
}
}
This is the constructor that you use for "Source":
public Source(WordprocessingDocument source, bool keepSections)
Then you just have to change false with true on keepSections value:
var sources = new List<OpenXmlPowerTools.Source>()
{
new Source(new WmlDocument(f1.FullName),true),
new Source(new WmlDocument(f2.FullName),true),
new Source(new WmlDocument(f3.FullName),true),
new Source(new WmlDocument(f4.FullName),true)
};
Using OpenXmlPowerTools.NetStandard version 4.6.0, it works so far with this code, which is based on the answers above, but a little simplyfied.
using OpenXmlPowerTools;
public class Example {
public static void MergeWithBreaks() {
string doc_A = "doc_A.docx";
string doc_B = "doc_B.docx";
string destination = "Merged_Result.docx";
var sources = new List<Source> {
new(new WmlDocument(doc_A), true),
new(new WmlDocument(doc_B), true)
};
var outputPath = destination;
DocumentBuilder.BuildDocument(sources, outputPath);
}
}
I am still trying to do a memory based version.

Categories