how to replace the entire array using MongoDB c# driver - c#

Say, the existing document in DB is like this:
{
"_id": "1",
"settings": {
"languages": [ "english", "french" ]
}
}
Now I want to update the document to this:
{
"_id": "1",
"settings": {
"languages": [ "korean", "german" ]
}
}
I tired this following code:
var lan = new List<string> { "finish", "russian", "korean" }
collection.UpdateOne(
Builders<MyObject>.Filter.Eq("_id", "1"),
Builders<MyObject>.Update.Set("settings.languages", lan));
But got following exception:
MongoDB.Bson.Serialization.Serializers.EnumerableInterfaceImplementerSerializer2[System.Collections.Generic.List1[System.String],System.String]' cannot be converted to type 'MongoDB.Bson.Serialization.IBsonSerializer`1[System.String]
I also tried using BsonArray to initialize the new languages array:
var bsonArray = new BsonArray
{
"korean",
"german"
};
collection.UpdateOne(
Builders<MyObject>.Filter.Eq("_id", "1"),
Builders<MyObject>.Update.Set("settings.languages", bsonArray));
The update could be executed without error, but the languages in document is changed to:
{
"_id": "1",
"settings": {
"languages": "[korean, german]"
}
}
It becomes "[ xx, xx ]", instead of [ "xx", "xx" ]. It is not what I expected.

You're getting that error message because you're using strongly typed builders on type MyObject which probably looks more or less like below:
public class MyObject
{
public string _id { get; set; }
public Settings settings { get; set; }
}
public class Settings
{
public string[] languages { get; set; }
}
So since you have a strongly typed Builder you are getting an exception because of the type mismatch between List and Array. Two ways to fix that:
Either use .ToArray() on list to get the same type as the one you have in your MyObject:
var lan = new List<string> { "finish", "russian", "korean" };
collection.UpdateOne(
Builders<MyObject2>.Filter.Eq("_id", "1"),
Builders<MyObject2>.Update.Set("settings.languages", lan.ToArray()));
or skip the type validation using BsonDocument class:
var collection = mydb.GetCollection<BsonDocument>("col");
var lan = new List<string> { "finish", "russian", "korean" };
collection.UpdateOne(
Builders<BsonDocument>.Filter.Eq("_id", "1"),
Builders<BsonDocument>.Update.Set("settings.languages", lan));

Related

serilization daughter classes

I am trying to serialize a class that has daughter classes but it does not allow me when I go to complete the json it gives me an error
This is the json file as it should look:
{
"User": "jhuan.caasillas",
"Passwd": "#########",
"IdAplicativo": 2001,
"Firma": "asdlkhg=saldkja=="
}
"Mensaje": {
"CodigoMensaje": 320,
"DescMensaje": "Exito"
},
"Roles": [
{
"Descripcion": "juan casillas"
},
{
"Descripcion": "al21"
},
{
"Descripcion": "comandos"
},
{
"Descripcion": "identificado"
}
]
}
I have this class with these methods created
enter image description here
when I go to fill these methods with the json it doesn't allow me and I get the error
Cannot implicitly convert type 'serialize.Roles' to 'serialize.Roles[]' serialize
enter image description here
I would like to know how I can fill the json array that I showed previously
If Roles is array it must be initialized as array
DominioRes res1 = new DominioRes
{
Roles = new Roles[]
{
new Roles
{
Description="juan casillas"
},
new Roles
{
Description="al21"
},
new Roles
{
Description="comandos"
},
new Roles
{
Description="identificado"
}
}
};

C# JSON Anonymous Type - Multiple properties with the same name

I am needing to produce this JSON string with C#:
{
"in0": {
"children": [
{
"ValueObjectDescriptor": {
"fields": [
{
"FieldDescriptor": {
"name": "length",
"xpath": "#lenth"
}
},
{
"FieldDescriptor": {
"name": "height",
"xpath": "#height"
}
},
{
"FieldDescriptor": {
"name": "width",
"xpath": "#width"
}
}
],
"objectName": "Job",
"limit": 1,
"xpathFilter": "#openJob = 'true'"
}
}
]
}
}
Here is my code:
static string BuildJsonString()
{
var json = new
{
in0 = new
{
children = new
{
ValueObjectDescriptor = new
{
fields = new
{
FieldDescriptor = new
{
name = "length",
xpath = "#length",
},
FieldDescriptor = new
{
name = "height",
xpath = "#height",
},
FieldDescriptor3 = new
{
name = "width",
xpath = "#width",
},
objectName = "Job",
limit = "1",
xpathFilter = "#openJob='true'"
}
}
}
}
};
var jsonFormatted = JsonConvert.SerializeObject(json, Newtonsoft.Json.Formatting.Indented);
return jsonFormatted.ToString();
The issue I am having is that the compiler doesn't like me using "FieldDescriptor" multiple times, I get the error "An anonymous type cannot have multiple properties with the same name".
I am very new to JSON, so any advice would be greatly appreciated.
Note that not only is having multiple fields with the same name invalid C# code, having duplicate keys in the same object is also invalid JSON. You can be sure that there is no JSON that would need you to write duplicate field names to serialise it.
The "FieldDescriptor" JSON keys are actually part of different JSON objects, and these JSON objects are all in a JSON array under the key "fields":
[
{
"FieldDescriptor": {
"name": "length",
"xpath": "#lenth"
}
},
{
"FieldDescriptor": {
"name": "height",
"xpath": "#height"
}
},
{
"FieldDescriptor": {
"name": "width",
"xpath": "#width"
}
}
]
The [ ... ] denotes the array, and each pair of { ... } denotes a JSON object. So you should create an (implicitly typed) array of anonymous objects, each one with the FieldDescriptor property, rather than one object having all three of the proeperties:
fields = new[] // <--- create an array
{
new {
FieldDescriptor = new
{
name = "length",
xpath = "#length",
}},
new { // notice the new pairs of curly braces
FieldDescriptor = new
{
name = "height",
xpath = "#height",
}}, // here's the closing brace
new {
FieldDescriptor3 = new
{
name = "width",
xpath = "#width",
}},
objectName = "Job",
limit = "1",
xpathFilter = "#openJob='true'"
}
It looks like in the json "fields" is an array of objects where each object contains a "FieldDescriptor" field. In your C# code you are creating "fields" as an object with multiple fields not an array of objects.

RavenDB Index: Need a solution to merge 2 dictionary fields into a single dictionary, flatten it and make it searchable

We are building a nested UI view for a customer and require a solution to merge 2 dictionary fields into a single consolidated dictionary as well as make the keys searchable as though they are field names. I managed to create a Map/Reduce index using the techniques mentioned in http://ravendb.net/docs/2.5/client-api/advanced/dynamic-fields and https://groups.google.com/forum/#!msg/ravendb/c0HdJT-yyvQ/qvkVRrZfvmgJ.
public class ViewFolderResultWithIndividualProperties
{
public string EntryId { get; set; }
public List<KeyValuePair<string, string>> MetadataProperties { get; set; }
public List<KeyValuePair<string, string>> NamedProperties { get; set; }
public List<KeyValuePair<string, string>> Properties { get; set; }
public string FlattenedProperties { get; set; }
public string _ { get; set; }
}
MetadateProperties – it is a dictionary of Key, Value pairs. For e.g.,
"MetadataProperties": [
{
"Key": "JobName",
"Value": "one job"
},
{
"Key": "Organization",
"Value": "foo"
}]
NamesProperties – it is a dictionary of known Key, Value pairs. For e.g.,
"NamedProperties": [
{
"Key": "Tags",
"Value": ""
},
{
"Key": "Name",
"Value": "file-184"
},
{
"Key": "Uploader",
"Value": "rmani#transper.com"
},
{
"Key": "FileType",
"Value": "Jpg"
},
{
"Key": "Language",
"Value": "English"
}]
Properties – It is a merged Dictionary that contains the Key, Value pairs from both MetadataProperties and NamedProperties.
FlattenedProperties and _ are the properties that contains the flattened field values of “NamedProperties” and “MetadataProperties” respectively. I can’t figure out a way to flatten a computed Property like “Properties” (which combines both MetadataProperties and NamedProperties dictionaries). I tried Concat
Here’s the Index creation code:
public class PortalEntryViews_DocumentIdSplitIndex : AbstractIndexCreationTask<PortalEntry, ViewFolderResultWithIndividualProperties>
{
public PortalEntryViews_DocumentIdSplitIndex()
{
Map = portalEntries => from portalEntry in portalEntries
select new
{
EntryId = portalEntry.Id,
MetadataProperties = portalEntry.MetaData.Select(t => new KeyValuePair<string, string>(t.Key, t.Value)).ToList(),
NamedProperties = new List<KeyValuePair<string, string>> {
new KeyValuePair<string, string>("Tags", string.Join(",", portalEntry.Tags.Where(t => !t.IsInternal).Select(t=>t.Name))),
new KeyValuePair<string, string>("Name", portalEntry.Name),
new KeyValuePair<string, string>("Uploader", portalEntry.Uploader),
new KeyValuePair<string, string>("FileType", portalEntry.FileType),
new KeyValuePair<string, string>("Language", portalEntry.Language),
new KeyValuePair<string, string>("Name", portalEntry.Name) },
Properties = new List<KeyValuePair<string, string>>(),
FlattenedProperties = "",
_ = ""
};
Reduce = results => from result in results
group result by new { result.EntryId, result.MetadataProperties, result.NamedProperties, result.FlattenedProperties, result._ } into g
select new
{
EntryId = g.Key.EntryId,
MetadataProperties = g.Key.MetadataProperties,
NamedProperties = g.Key.NamedProperties,
Properties = g.Key.MetadataProperties.Concat(g.Key.NamedProperties).ToList(),
FlattenedProperties = g.Key.NamedProperties.Select(f => CreateField(f.Key, f.Value)),
_ = g.Key.MetadataProperties.Select(t => CreateField(t.Key, t.Value, true, true))
};
}
}
When I run a query like “Language:English” from RavenDb Explorer directly, it works and returns a projection. Whereas when I run the same query using LuceneQuery from within my C# code:
var entries =
session.Advanced.LuceneQuery<ViewFolderResultWithIndividualProperties>(
"PortalEntryViews/DocumentIdSplitIndex")
.WhereEquals("Language", "English").ToList();
I get this error:
Raven.Imports.Newtonsoft.Json.JsonSerializationException : Could not read value for property: FlattenedProperties ----> Raven.Imports.Newtonsoft.Json.JsonReaderException : Error reading string. Unexpected token: StartArray
My ultimate goal is to flatten the combined dictionary i.e. Properties field into a single field using CreateField() that can be searched using the keys as though they are field names. But, if I use a call like this:
Properties = g.Key.MetadataProperties.Concat(g.Key.NamedProperties).ToList().Select(t => CreateField(t.Key, t.Value, true, true)), it seems to run but when you look at the index from Ravendb Explorer, it shows the actual error:
Stage: Indexing Section:Reduce Description: ‘System.Collections.Generic.List’ does not contain definition for ‘Select’
Right now, I’m only able to flatten only one Dictionary (MetadataProperties) into that “_” field in reduce section, which works from both Ravendb Explorer and from C# code using LuceneQuery but that does not meet my requirement.
Can someone help me resolve this issue?
If you want to search key-value only, you can do it very simple
//the index
public class FlattenIndex: AbstractIndexCreationTask<PortalEntry>
{
public class ReduceResult
{
public string Key { get; set; }
public string Value { get; set; }
}
public FlattenIndex()
{
Map = portalEntries => from portalEntry in portalEntries
from p in portalEntry.MetadataProperties.Concat(portalEntry.NamedProperties)
select new
{
Key=p.Key,
Value=p.Value
};
}
}
//the query
using (var session = _docStore.OpenSession())
{
var someEntries = session.Query<FlattenIndex.ReduceResult, FlattenIndex>()
.Where(x => x.Key == "Language" && x.Value == "English")
.As<PortalEntry>()
.ToArray();
if (someEntries!=null)
foreach(var entry in someEntries )
{
Console.WriteLine(entry.Id);
}
}

How to serialize a C# object to json so it looks like "["starts-with", "$key", "user/john/"]"?

I'm trying to create a viewmodel in C# that can be serialized into a json document required by amazon S3. Documentation here. One of the properties looks like this,
["starts-with", "$key", "user/john/"]
What would the C# object look like so when it's serialized it would come out like this?
Rest of document looks like this.
{ "expiration": "2007-12-01T12:00:00.000Z",
"conditions": [
{"acl": "public-read" },
{"bucket": "johnsmith" },
["starts-with", "$key", "user/john/"],
]
}
Just use a string array
string[] data = new string[] { "starts-with", "$key", "user/john/" };
The object structure for the second part would be something like
public class S3Expiration {
DateTime expiration { get; set; }
object[] conditions { get; set; }
}
and to populate it you would write something like
var expiration = new S3Expiration {
expiration = DateTime.Now,
conditions = new object[] {
new { acl = "public-read" },
new { bucket = "johnsmith" },
new string[] { "starts-with", "$key", "user/john/" }
}
};

List in C# not rendered as array using MongoDB official client

I am using MongoDB with ASP.NET and the official driver from 10gen. My problems is with how my class is rendered as documents (serialized).
I have a class called 'box', and another class calls 'items'.
Items appear in the class as follows:
[BsonIgnoreIfNull]
[BsonElementAttribute("items")]
public List<Item> Items { get; set; }
Each items have several properties assigned to it in its own class.
When I view the JSON document that was inserted into Mongo, I see it like this:
Items: {
"0": {
"_id": ObjectId("33423423423434343434"),
"name": "Item1"
},
}
However, I don't want it to appear like that, but appear as an array like this:
Items: [
{
"_id": ObjectId("34234234234234234"),
"name": "some name",
},
{
"_id": ObjectId("34234234234234234"),
"name": "some name",
}
]
Adding items in the code behind:
List<Item> theItems = new List<Item>();
Item singleitem = new Item{
Title = "single item one"
};
theItems.Add(singleitem);
theItems.Add(singleitem);
var box= new Box
{
IsPublic = true,
Items = theItems
};
SafeModeResult sf = collectionBoxes.Insert(box);
So the items will appear as an array. How can I change my code to do this? Thanks.

Categories

Resources