Find an specific element in a MongoDB document from C# - c#

I am trying to access MongoDB from C# ASP.NET application.
Let's assume, I've a document like below-
{
"_id" : ObjectId("546c776b3e23f5f2ebdd3b03"),
"Name" : "Test",
"Values" : [
{
"Name" : "One",
"Value" : 1
},
{
"Name" : "Two",
"Value" : 2,
"Parameters": [{"Type": "Type A"}, {"Type": "Type B"}]
}
]
}
Please note that, only the _id and Name elements are fixed; other elements are dynamically created by the user where both the key and value are defined by the user.
Now, I would like to search for the element Type with the value Type A. How can I do this from a MongoDB C# driver?

You can use this code:
var query = Query.EQ("Values.Parameters.Type", "Type A");
var items = collection.Find(query).ToList();
If you data has structure use this:
var items = collection.FindAs<Item>(query).ToList();
Edit:
For dynaically search the only way comes to my mind is full-text-search:
Step1: Define a full text-search on all fields via db.test.ensureIndex({"$**" : "text"});
Step2: Search your query db.test.find( { $text: { $search: "Type A" } } )
If its your answer, the C# code should be easy.

Below aggregation query may solve your problem but I don't know how to write this in C#
db.collectioName.aggregate({"$unwind":"$Values"},
{"$unwind":"$Values.Parameters"},
{"$match":{"Values.Parameters.Type":"Type A"}},
{"$group":{"_id":"$Values"}})

Related

Mongodb Update or insert in c#

I want to update or insert into to mongo collection "Member". Under this collection i have an array MagazineSubscription. Here magazine Code is unique. Please refer the sample JSON.
So if need to update or insert into mongo using C# mongo driver.
First I need to check this code exist
2, If it exist update one
If it does not exist insert.
Is there any way I can do in one step. Like if it already exist update otherwise insert. Instead of hit twice. Because my collection is very big.
{
"_id" : ObjectId("5c44f7017en0893524d4e9b1"),
"Code" : "WH01",
"Name" : "Lara",
"LastName" : "John",
"DOB" : "12-10-2017",
"Gender" : "Male",
"Dependents" : [
{
"RelationShip" : "Son",
"Name" : "JOHN",
"DOB" : "01-01-1970",
"Gender" : "Male",
"Address" : "Paris",
"ContactNumber" : "+312233445666"
},
{
"RelationShip" : "Wife",
"Name" : "Marry",
"DOB" : "01-01-1980",
"Gender" : "Female",
"Address" : "Paris",
"ContactNumber" : "+312233445666"
}
]
"Matrimony" : [
{
"Fee" : 1000.0,
"FromDate" : "01-01-2015",
"ToDate" : "01-01-2017",
"Status" : false
}
],
"MagazineSubscription" : [
{
"MagazineCode" : "WSS",
"DateFrom" : "01-05-2018",
"DateTo" : "01-01-2020",
"PaidAmount" : 1000.0,
"ActualCost" : 1500.0,
"Status" : false,
"DeliveryStatus" : [
{
"ReturnedDate" : "10-01-2019",
"Comment" : "Returned because of invalid address"
},
{
"ReturnedDate" : "10-02-2019",
"Comment" : "Returned because of invalid address"
}
]
}
]
}
Use mongodb's update operation with upsert:true.
Please refer here: https://docs.mongodb.com/manual/reference/method/db.collection.update/
Here's a sample from the page:
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>, //you need this option
multi: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ]
}
)
And here's a similar question according to what you need:
Upserting in Mongo DB using official C# driver
EDIT 1
Steps:
First you need to write a filter to scan if the document exists. you can check any number of keys (Essentially a document).
Write the update section with the keys you'd like to update (Essentially a document).
Set upsert to true.
Mongodb will use your filter to search the document. If found, it will use the update section to perform the update mentioned by you.
In case the document does not exist, a new document will be created by using the filter keys + keys in the update part.
Hope that makes things clear as I have never used a C# mongo driver. So I won't be able to provide you the exact syntax.
EDIT 2
I'm providing #jeffsaracco's solution here:
MongoCollection collection = db.GetCollection("matches");
var query = new QueryDocument("recordId", recordId); //this is the filter
var update = Update.Set("FirstName", "John").Set("LastName","Doe"); //these are the keys to be updated
matchCollection.Update(query, update, UpdateFlags.Upsert, SafeMode.False);

How to Append to a Field in MongoDB C# [duplicate]

I have a document with a field containing a very long string. I need to concatenate another string to the end of the string already contained in the field.
The way I do it now is that, from Java, I fetch the document, extract the string in the field, append the string to the end and finally update the document with the new string.
The problem: The string contained in the field is very long, which means that it takes time and resources to retrieve and work with this string in Java. Furthermore, this is an operation that is done several times per second.
My question: Is there a way to concatenate a string to an existing field, without having to fetch (db.<doc>.find()) the contents of the field first? In reality all I want is (field.contents += new_string).
I already made this work using Javascript and eval, but as I found out, MongoDB locks the database when it executes javascript, which makes the overall application even slower.
Starting Mongo 4.2, db.collection.updateMany() can accept an aggregation pipeline, finally allowing the update of a field based on its current value:
// { a: "Hello" }
db.collection.updateMany(
{},
[{ $set: { a: { $concat: [ "$a", "World" ] } } }]
)
// { a: "HelloWorld" }
The first part {} is the match query, filtering which documents to update (in this case all documents).
The second part [{ $set: { a: { $concat: [ "$a", "World" ] } } }] is the update aggregation pipeline (note the squared brackets signifying the use of an aggregation pipeline). $set (alias of $addFields) is a new aggregation operator which in this case replaces the field's value (by concatenating a itself with the suffix "World"). Note how a is modified directly based on its own value ($a).
For example (it's append to the start, the same story ):
before
{ "_id" : ObjectId("56993251e843bb7e0447829d"), "name" : "London
City", "city" : "London" }
db.airports
.find( { $text: { $search: "City" } })
.forEach(
function(e, i){
e.name='Big ' + e.name;
db.airports.save(e);
}
)
after:
{ "_id" : ObjectId("56993251e843bb7e0447829d"), "name" : "Big London
City", "city" : "London" }
Old topic but i had the same problem.
Since mongo 2.4, you can use $concat from aggregation framework.
Example
Consider these documents :
{
"_id" : ObjectId("5941003d5e785b5c0b2ac78d"),
"title" : "cov"
}
{
"_id" : ObjectId("594109b45e785b5c0b2ac97d"),
"title" : "fefe"
}
Append fefe to title field :
db.getCollection('test_append_string').aggregate(
[
{ $project: { title: { $concat: [ "$title", "fefe"] } } }
]
)
The result of aggregation will be :
{
"_id" : ObjectId("5941003d5e785b5c0b2ac78d"),
"title" : "covfefe"
}
{
"_id" : ObjectId("594109b45e785b5c0b2ac97d"),
"title" : "fefefefe"
}
You can then save the results with a bulk, see this answer for that.
this is a sample of one document i have :
{
"_id" : 1,
"s" : 1,
"ser" : 2,
"p" : "9919871172",
"d" : ISODate("2018-05-30T05:00:38.057Z"),
"per" : "10"
}
to append a string to any feild you can run a forEach loop throught all documents and then update desired field:
db.getCollection('jafar').find({}).forEach(function(el){
db.getCollection('jafar').update(
{p:el.p},
{$set:{p:'98'+el.p}})
})
This would not be possible.
One optimization you can do is create batches of updates.
i.e. fetch 10K documents, append relevant strings to each of their keys,
and then save them as single batch.
Most mongodb drivers support batch operations.
db.getCollection('<collection>').update(
// query
{},
// update
{
$set: {<field>:this.<field>+"<new string>"}
},
// options
{
"multi" : true, // update only one document
"upsert" : false // insert a new document, if no existing document match the query
});

select specific field in nested array in mongodb using c#

My application accesses a mongodb using the .net c# driver.
My data structure looks like this:
{
"_id" : ObjectId("53d97351e37f520a342e152a"),
"Name" : "full question test 2",
"keywords" : ["personality", "extraversion", "agreeableness"],
"Questions" : [{
"type" : "likert",
"text" : "question 1",
},
{
"type" : "likert",
"text" : "question 2",
}]
}
What I want to do is to select only the type column from the questions array.
Here is my linq code now:
from e in collection.AsQueryable<scales>()
where e.Name == "full question test 2"
select new { e.Id, e.Name, e.Questions}
This returns all the question properties (both type and text). I want just the type. I can do this by saying something like e.Questions[0].text,e.Questions[1].text.
But the number of questions varies from file to file, so I'd love a solution that doesnt require this manual coding.
Open to ideas!
The standard query methods that are wrapped here have a form of "projection" available for field selection, but this is however not capable of doing things such as selecting specific fields within an array element. At least for multiple fields anyway.
But single fields should be possible just using the dot notation form to access the element:
from e in collection.AsQueryable<scales>()
where e.Name == "full question test 2"
select new { e.Id, e.Name, e.Questions.type }
In order to do anything more you need the form of projection that is available to the aggregation framework where your "query" and "projection" are represented as BSON documents using the $match and $project operators for the pipeline. In the shell form it looks like this:
db.collection.aggregate([
{ "$match": {
"Name": "fullquestion test 2"
}},
{ "$project": {
"Name": 1,
"Questions.type": 1
}}
])
Or with constructing the BSON documents for C#:
var match = new BsonDocument {
{ "$match",new BsonDocument {
{
"Name", "full question test 2"
}
}
}
};
var project = new BsonDocument {
{ "$project", new BsonDocument {
{ "Name", 1 },
{ "Questions.type": 1 }
}
}
};
var pipeline = new [] { match, project };
var result = collection.aggregate(pipeline);
Essentially, the $project stage of the aggregation pipeline can do a lot more things than just select fields. The additional support here allows for things such as "changing" the structure of the document inside the array.
Support for aggregation pipeline mapping to Linq is a work in progress. You can monitor the issues here: CSHARP-601

"Extra" nesting in MongoDB C# driver

I have some document stored in MongoDB (2.4.9) with and array if documents field:
{
"_id" : "some id",
"class" : "somevalue",
...
"externalAlarmDefinition" : [
{
"idx" : 1,
"inputId" : 1
},
{
"idx" : 2,
"inputId" : 2
},
...
{
"idx" : 6,
"inputId" : 7
}
]
}
For some reason, when I query this object I get BsonElement who's value is a BsonArray with one element - that element in turn is another BsonArray which contains the actual BsonDocuments. See image for the structure:
Does this make sense? I expected that the value of the BsonElement would be a BsonArray with the 6 BsonDocuments.
Am I missing something - can someone explain this?
I am using Mongo Driver 1.9.1.221 that I got using nuget

Insert in to a nested array Using C# Official Driver MongoDB

Here is my exact schema:
{
"_id" : ObjectId("4fb4fd04b748611ca8da0d48"),
"Name" : "Categories",
"categories" : [{
"_id" : ObjectId("4fb4fd04b748611ca8da0d46"),
"name" : "Naming_Conventions",
"sub-categories" : [{
"_id" : ObjectId("4fb4fd04b748611ca8da0d47"),
"name" : "Namespace_Naming",
"standards" : []
}]
}]
}
As you can see I have an array named "standards" nested way down in there. How would I programmatically insert in to that using the C# driver? I have tried all of the examples I have found online but none of them are working.
Something like the below. Obviously, if any of these are not present on the way down to it, you're going to get a null reference exception.
var doc = collection.FindOne(Query.EQ("_id", new ObjectId("4fb4fd04b748611ca8da0d48")));
var standards = doc["categories"]
.AsBsonArray[0]
.AsBsonDocument["sub-categories"]
.AsBsonArray;
standards.Add(new BsonDocument());
collection.Save(doc);

Categories

Resources