..ToBsonDocument() is extremly slow (60ms) - c#

Im trying to convert a BsonElement to BsonDocument, so i can use TryGetValue();. But that takes for ever (60ms)! I can't wait that for 1 Million entries.
Is there an alternative to get Values from inside a BsonValue? Or can i make it somehow significantly faster?
all i need is a unique list of inside2 (Structure is at the end)
Now i do something like that, to get Values from Array.
//Maybe this code right here is not working, but you get the idea
// convert to doc -> get value -> convert to doc -> get value
BsonDocument doc = ...;
BsonValue val1;
doc.TryGetValue("files", out val1);
BsonDocument fileAsDoc = val1.ToBsonDocument();
BsonValue catetgoriesValue;
fileAsDoc.TryGetValue("Categories", out catetgoriesValue);
BsonDocument catAsDoc = catetgoriesValue.ToBsonDocument();
BsonValue data3Value;
catAsDoc.TryGetValue("data3", out data3Value);
BsonDocument data3AsDoc = data3.ToBsonDocument();
foreach(BsonElement inside in data3AsDoc){
//.....
}
this how my Bson is put together:
{
"_id": ObjectId("1234567890123"),
"language" : "DE",
"files" : [
{
"something" : 10,
"something2" : "something2..",
"Categories" : [
{
"data1" : "1234",
"data2" : "1234",
"data3" : [
{
"inside1" : false,
"inside2" : "this is what i need at the end"
},
{
"inside1" : false,
"inside2" : "this is what i need at the end"
}
]
},
[....]
{
"data1" : "1234",
"data2" : "1234",
"data3" : [
{
"inside1" : false,
"inside2" : "this is what i need at the end"
},
{
"inside1" : false,
"inside2" : "this is what i need at the end"
}
]
}
],
"something3" : "text",
"something4" : "text",
"something5" : "text",
"something6" : "text",
"something7" : "text",
}
]
}

Related

Project single field in array of subdocument returns more than one

I'm having difficulties putting up a code which returns an element in an array of subdocuments. I am actually trying to flatten a document to a new document which is strongly typed. My document is looking like;
{
"_id" : BinData(3, "7FRf4nbe60ev6XmGKBBW4Q=="),
"status" : NumberInt(1),
"title":"Central station",
"attributes" : [
{
"defId" : BinData(3, "QFDtR03NbkqwuhhG76wS8g=="),
"value" : "388",
"name" : null
},
{
"defId" : BinData(3, "RE3MT3clb0OdLEkkqhpFOg=="),
"value" : "",
"name" : null
},
{
"defId" : BinData(3, "pPgJR50h8kGdDaCcH2o17Q=="),
"value" : "Merkez",
"name" : null
}
]}
What I am trying to achieve is;
{
"title":"Central Station",
"value":"388"
}
What I've done already;
using (_dbContext)
{
var filter = Builders<CustomerModel>.Filter.Eq(q => q.Id, Guid.Parse("30B59585-CBFC-4CD5-A43E-0FDB0AE3167A")) &
Builders<CustomerModel>.Filter.ElemMatch(f => f.Attributes, q => q.DefId == Guid.Parse("47ED5040-CD4D-4A6E-B0BA-1846EFAC12F2"));
var projection = Builders<CustomerModel>.Projection.Include(f => f.Title).Include("attributes.value");
var document = _dbContext.Collection<CustomerModel>().Find(filter).Project(projection).FirstOrDefault();
if (document == null)
return null;
return BsonSerializer.Deserialize<TitleAndValueViewModel>(document);
}
Note: TitleAndCodeViewModel contains title and value properties.
This block of code returns;
{{ "_id" : CSUUID("30b59585-cbfc-4cd5-a43e-0fdb0ae3167a"), "title" : "388 güvenevler", "attributes" : [{ "value" : "388" }, { "value" : "" }, { "value" : "Merkez " }] }}
I am trying to get "value":"388" but instead I am getting another two value properties even tough the ElemMatch filter added for subdocument.
Thank you for your help in advance.
Note: I am looking for answers in C# mongodb driver.
Option 1: ( via aggregation)
db.collection.aggregate([
{
$match: {
_id: 5,
"attributes.defId": 1
}
},
{
"$addFields": {
"attributes": {
"$filter": {
"input": "$attributes",
"as": "a",
"cond": {
$eq: [
"$$a.defId",
1
]
}
}
}
}
},
{
$unwind: "$attributes"
},
{
$project: {
_id: 0,
title: 1,
value: "$attributes.value"
}
}
])
Explained:
Match ( good to add index for the matching fields )
Filter only the attribute you need
Unwind to convert the array to object
Project only the necessary output
Playground
Option 2: ( find/$elemMatch )
db.collection.find({
_id: 5,
attributes: {
"$elemMatch": {
"defId": 1
}
}
},
{
_id: 0,
title: 1,
"attributes": {
"$elemMatch": {
"defId": 1
}
}
})
Explained:
Match the element via _id and elemMatch the attribute
Project the necessary elements. ( Note here elemMatch also need to be used to filter the exact match attribute )
( Note this version will not identify if there is second attribute with same attribute.defId , also projection of attribute will be array with single element if found that need to be considered from the app side )
Playground 2
by specifying defId
db.collection.aggregate(
[{
$project: {
title: '$title',
attributes: {
$filter: {
input: '$attributes',
as: 'element',
cond: { $eq: ['$$element.defId', BinData(3, 'QFDtR03NbkqwuhhG76wS8g==')] }
}
}
}
}, {
$project: {
_id: 0,
title: '$title',
value: { $first: '$attributes.value' }
}
}])
result:
{
"title": "Central station",
"value": "388"
}

UpdateMany - ArrayFilterDefinition c#

I'm trying to update just a few nested documents on my .Net Application, but I don't know exactly how to do that.
Here is one of the topics I've been searching after making this question:
C# - MongoDB - Update an element inside a Nested Document
**DataBase Object: **
{
"_id" : ObjectId("5d03a6f5fba276260c4911bd"),
"costumerId" : ObjectId("5c050cdefba2771eac58c84b"),
"order" : 106376,
"items" : [
{
"itemId" : 10226905,
"date" : ISODate("2018-11-30T14:00:00.000Z"),
"description" : "itemA",
"new" : true,
},
{
"itemId" : 10226906,
"date" : ISODate("2018-11-30T14:00:00.000Z"),
"description" : "itemB",
"new" : true,
},
{
"itemId" : 10226907,
"date" : ISODate("2018-11-23T14:00:00.000Z"),
"description" : "ItemC",
"new" : true,
},
{
"itemId" : 10226908,
"date" : ISODate("2018-11-23T14:00:00.000Z"),
"description" : "ItemD",
"new" : false,
},
]
}
MongoDB Update Query:
db.CostumerProducts.update(
{costumerId: 106376},
{
$set:{'items.$[element].new': true}
},
{
multi:true,
arrayFilters: [
{
'element.itemId':{
$in:[10226905,10226906,10226907]
}
}]
})
C# Update Query using MongoDB driver:
var arrayObjects = List<int>{10226905,10226906,10226907};
var filter = Builders<CostumerProductsObject>.Filter.And(new[]
{
new FilterDefinitionBuilder<CostumerProductsObject>().Eq(p=>p.costumerId, costumerId),
new FilterDefinitionBuilder<CostumerProductsObject>().Eq(p=>p.order, order),
});
var set = Builders<ItemsObject>.Update.Set("items.$[element].new", true);
var arrayFilter = new FilterDefinitionBuilder<ItemsObject>().In(a=>a.itemId, itemsArray);
using (var db = new MongoContextInfra())
{
db.Set<CostumerProductsObject>(CollectionName).UpdateMany(filter,
set,
new UpdateOptions
{
ArrayFilters = null
});
}
I expect to update just the three items into "arrayObjects" variable.
Can you show me some code or help me fixing mine so I can make this update?
thanks.

C# Filter to get specific Mongo Document

{
"_id" : {
"order" : "0000006"
},
"Catgeory" : [
{
"ID" : "62982698",
"Data" : [
{
"NO" : "62982698",
"History" : [
{
"Status" : null,
}
]
}
]
},
{
"ID" : "63002696",
"Data" : []
}
],
"Info_ID" : [
"6000016405"
]
}
How to write c# mongo filter to get No:62982698 with "Info_ID" :"6000016405". No:62982698 can be part of multiple documents.So want to write filter that filters document with No 62982698 and "Info_ID" :"6000016405"

LINQ to JSON - SelectToken Error

This is the JSON string that I am working with.
string jsonText = "{
"?xml" : {
"#version" : "1.0",
"#encoding" : "UTF-8",
"#standalone" : "yes"
},
"Grid" : {
"DataRow" : [{
"DataItem" : [{
"#name" : "SYMBOL",
"#text" : "005930"
}, {
"#name" : "NAME",
"#text" : "Samsung Electronics"
}, {
"#name" : "PRICE",
"#text" : "1004.3"
}, {
"#name" : "VOLUME",
"#text" : "273.182"
}, {
"#name" : "AGG_VOLUME",
"#text" : "302.894"
}
]
}, {
"DataItem" : [{
"#name" : "SYMBOL",
"#text" : "AAPL"
}, {
"#name" : "NAME",
"#text" : "Apple Inc."
}, {
"#name" : "PRICE",
"#text" : "99"
}, {
"#name" : "VOLUME",
"#text" : "32936.4"
}, {
"#name" : "AGG_VOLUME",
"#text" : "33078.769"
}
]
}, {
"DataItem" : [{
"#name" : "SYMBOL",
"#text" : "MSFT"
}, {
"#name" : "NAME",
"#text" : "Microsoft Corporation"
}, {
"#name" : "PRICE",
"#text" : "42"
}, {
"#name" : "VOLUME",
"#text" : "103441.6"
}, {
"#name" : "AGG_VOLUME",
"#text" : "1324432.074"
}
]
}
]
}
}"
JObject feed = JObject.Parse(jsonText);
I'm trying to get a list of values for SYMBOL, NAME, PRICE, & AGG_VOLUME. Here's my code so far:
var covg = feed["DataItem"]["#name"].Select(f => (string)f.SelectToken("#text"));
But I'm getting the following error:
Object reference not set to an instance of an object.
What am I doing wrong?
JToken.SelectTokens() supports JSONPath query syntax. You can make use of this syntax to perform the query you require:
".." is the wildcard recursive descent operator. Thus feed.SelectTokens("..DataItem") finds the values of all JSON properties named DataItem no matter where they are in the JSON hierarchy.
"[?(#.#name == 'Value')]" queries for objects in an array with a property named #name with value Value.
Thus, the following does what you need:
var feed = JObject.Parse(jsonText);
var query = from item in feed.SelectTokens("..DataItem")
select new
{
SYMBOL = (string)item.SelectToken("[?(#.#name == 'SYMBOL')].#text"),
NAME = (string)item.SelectToken("[?(#.#name == 'NAME')].#text"),
PRICE = (string)item.SelectToken("[?(#.#name == 'PRICE')].#text"),
AGG_VOLUME = (string)item.SelectToken("[?(#.#name == 'AGG_VOLUME')].#text")
};
var list = query.ToList();
You are getting this error because feed refers to the root JObject which does not directly contain a DataItem property. Thus feed["DataItem"] returns null. When you then try to dereference this null expression it throws a NullReferenceException.
The data you are trying to get is several layers down in the JSON, so your query has to account for this. To get a list of all SYMBOL values in the JSON, for example, you would need to do something like this:
List<string> symbols =
feed.SelectToken("Grid.DataRow")
.SelectMany(jt => jt["DataItem"])
.Where(jt => (string)jt["#name"] == "SYMBOL")
.Select(jt => (string)jt["#text"])
.ToList();
Fiddle: https://dotnetfiddle.net/jxZGZC

mongoDB insert item in Array

{
"_id" : ObjectId("5431f38c4ba4dd20408b0432"),
"UserID" : "1",
"Status" : {
}
},
"ListFilterType" : 1,
"IssueCategories" : [
{
"_id" : ObjectId("000000000000000000000000"),
"IssueCategoryID" : 2,
"IssueCagetoryName" : "test",
"MatchKeyword" : "test",
"MatchKeywordID" : 2
}
]
}
edit :
my expactation is like this.
{
"_id" : ObjectId("5431f38c4ba4dd20408b0432"),
"UserID" : "1",
"Status" : {
}
},
"ListFilterType" : 1,
"IssueCategories" : [
{
"_id" : ObjectId("000000000000000000000000"),
"IssueCategoryID" : 2,
"IssueCagetoryName" : "test",
"MatchKeyword" : "test",
"MatchKeywordID" : 2
},
{
"_id" : ObjectId("000000000000000000000001"),
"IssueCategoryID" : 3,
"IssueCagetoryName" : "test2",
"MatchKeyword" : "test2",
"MatchKeywordID" : 3
},
{
"_id" : ObjectId("000000000000000000000004"),
"IssueCategoryID" : 4,
"IssueCagetoryName" : "test4",
"MatchKeyword" : "test34",
"MatchKeywordID" : 4
}
]
i have a type list as "IssueCategories". You can see my mongodb structure above. If root _id = my parameter, I want to add to mongodb.
I hope I explained right
thanks
You should use either $push or $addToSet to update document.
$push will add all element in IssueCategories including duplicates also.
While in $addToSet do not insert duplicate elements(object) in array if whole object is present in array.
Using $push:
db.collection.update({
"_id": ObjectId("5431f38c4ba4dd20408b0432") // you can add any other param
}, {
$push: {
SCSIssueCategories: {
"_id": ObjectId("11111111111111111"), //ur id here
"IssueCategoryID": 3,
"IssueCagetoryName": "Sözleşme1",
"MatchKeyword": "abonelik süresi1",
"MatchKeywordID": 3
}
}
})
Here is query using $addToSet:
db.collection.update({
"_id": ObjectId("5431f38c4ba4dd20408b0432")
}, {
$addToSet: {
SCSIssueCategories: {
"_id": ObjectId("11111111111111111"), //ur id here
"IssueCategoryID": 3,
"IssueCagetoryName": "Sözleşme1",
"MatchKeyword": "abonelik süresi1",
"MatchKeywordID": 3
}
}
})
**If single param have different value then it will be inserted in array using $addToSet. **

Categories

Resources