Filtering query from database - c#

I have a problem because I am new to mongodb and c#. I hope you can help.
I need to add a filter when retrieving data from database. Objects in ProductAttributeCombinations in the database exist more than once. I'm getting data from these objects to the variable named ProductAttributeText, but it comes as a list because there is more than one data in the object. What I need to do is to compare the value under a different object in the database with the values ​​under this object and get the data accordingly.
My codes where I get the data
var querySpec = await _productRepository.Collection
.Aggregate()
.Unwind(x => x.ProductAttributeMappings)
.Project(new BsonDocument {
{ "ProductAttributeId", "$ProductAttributeMappings.ProductAttributeId" },
{ "ProductAttributeValues","$ProductAttributeMappings.ProductAttributeValues.Name" },
{ "ProductAttributeCombination","$ProductAttributeCombinations.Text" },
})
.Unwind("ProductAttributeValues")
.Group(new BsonDocument {
{"_id",
new BsonDocument {
{ "ProductAttributeId", "$ProductAttributeId" },
{ "ProductAttributeName", "$ProductAttributeValues" },
{ "ProductAttributeText", "$ProductAttributeCombination" },
}
},
{"count",
new BsonDocument {
{ "$sum" , 1}
}
}
}).ToListAsync();
If one of the list elements returned to me is like this
{{ "_id" : { "ProductAttributeId" : "60daf1f56b2621a0b7942eac", "ProductAttributeName" : "50", "ProductAttributeText" : ["30", "30", "30", "3", "3", "3", "30", "30", "30"] }, "count" : 3 }}
When I add the filtering like this and add it as Match(filterAttr) to my code, QuerySpec returns null. Am I making a mistake in filtering?
var filterDefAttr = new FilterDefinitionBuilder<BsonDocument>();
var filterAttr = (filterDefAttr.Eq("ProductAttributeCombinations.Attributes.Value", "ProductAttributeMappings.ProductAttributeValues._id"));

Related

MongoDB - How to update a single object in the array of objects inside a document

I have the following document structure:
{
"Agencies": [
{
"name": "tcs",
"id": "1",
"AgencyUser": [
{
"UserName": "ABC",
"Code": "ABC40",
"Link": "http.ios.com",
"TotalDownloads": 0
},
{
"UserName": "xyz",
"Code": "xyz20",
"Link": "http.ios.com",
"TotalDownloads": 0
}
]
}
]
}
Like I have multiple agencies and each agency contains a list of agents.
What I am trying is to pass the Code and update the TotalDownloads field of the agent that matches the code.
For example, if someone uses the code ABC40 so I need to update the field TotalDownloads of the agent called "ABC".
What I have tried is as below:
public virtual async Task UpdateAgentUsersDownloadByCode(string Code)
{
var col = _db.GetCollection<Agencies>(Agencies.DocumentName);
FilterDefinition<Agencies> filter = Builders<Agencies>.Filter.Eq("AgencyUsers.Code", Code);
UpdateDefinition<Agencies> update = Builders<Agencies>.Update.Inc(x => x.AgencyUsers.FirstOrDefault().TotalDownloads, 1);
await col.UpdateOneAsync(filter, update);
}
It is giving me the following error:
Unable to determine the serialization information for x => x.AgencyUsers.FirstOrDefault().TotalDownloads.
Where I'm wrong?
Note: From the attached sample document, the array property name: AgencyUser is not matched with the property name that you specified in the update operation, AgencyUsers.
Use arrayFilters with $[<identifier>] positional filtered operator to update the element(s) in the array.
MongoDB syntax
db.Agencies.update({
"AgencyUsers.Code": "ABC40"
},
{
$inc: {
"AgencyUsers.$[agencyUser].TotalDownloads": 1
}
},
{
arrayFilters: [
{
"agencyUser.Code": "ABC40"
}
]
})
Demo # Mongo Playground
MongoDB .NET Driver syntax
UpdateDefinition<Agencies> update = Builders<Agencies>.Update
.Inc("AgencyUsers.$[agencyUser].TotalDownloads", 1);
UpdateOptions updateOptions = new UpdateOptions
{
ArrayFilters = new[]
{
new BsonDocumentArrayFilterDefinition<Agencies>(
new BsonDocument("agencyUser.Code", Code)
)
}
};
UpdateResult result = await col.UpdateOneAsync(filter, update, updateOptions);
Demo

Merge parent field and its nested array field in mongodb

I have a collection like below
{
"id":"5fd13c33ac0277c435117519",
"content":"test",
"votes":[
{
"user":"22",
"isLike":true
},
{
"user":"25",
"isLike":false
},
{
"user":"43",
"isLike":false
}
]
},
{
"id":"5fd13c33ac0277c435237443",
"content":"test 2",
"votes":[
{
"user":"25",
"isLike":true
},
{
"user":"43",
"isLike":false
}
]
}
How can I get the result below with c# driver by querying over votes.user -ie. 25- and then merge those 2 fields from parent and nested array?
{
"id":"5fd13c33ac0277c435117519",
"isLike":false
},
{
"id":"5fd13c33ac0277c435237443",
"isLike":true
}
EDIT: I've got the result after some try and fails on mongoplayground.net, stil not sure how to convert it over c# driver.
db.collection.aggregate([
{
$unwind: "$votes"
},
{
$match: {
"votes.user": "25"
}
},
{
$replaceWith: {
id: "$id",
isLike: "$votes.isLike"
}
}
])
I've managed it like below and wanted to share if someone needs it in the future.
I personally decided to go one-to-many path with separated vote collection after some readings.
var match = new BsonDocument("votes.user", 25);
var replace = new BsonDocument(new List<BsonElement> {
new BsonElement("id", "$id"),
new BsonElement("isLike", "$votes.isLike"),
});
return await _collection.Aggregate()
.Unwind(c => c.votes)
.Match(match)
.ReplaceWith<UserVote>(replace)
.ToListAsync();

How to return a List of all values in a field

I have a document like:
{
"Id":"1",
"Name":"product1",
"Categories":["Cat1",
"Cat2",
"Cat3"]
},
{
"Id":"2",
"Name":"product2",
"Categories":["Cat3",
"Cat2",
"Cat6"]
}
Now I want return a distinct List of all categories.
I tried CollapseParameter, but it doesn't work.
var foo = _solr.Query(new SolrQuery("*"), new QueryOptions
{
Collapse = new CollapseParameters("Categories")
});
var bar= results.CollapseExpand; //NULL
How can I get a list of all categories without iterating through all the documents? Do I have to create new Documents for the categories? Or should I work with Faceting?
It looks like the JSON is not valid. So I created a solution changing the Json:
{
"items": [{
"Id": "1",
"Name": "product1",
"Categories": ["Cat1", "Cat2", "Cat3"]
}, {
"Id": "2",
"Name": "product2",
"Categories": ["Cat3", "Cat2", "Cat6"]
}]
}
And then, I used foreach and LINQ structure:
var x = categories.SelectMany(item => item.Categories).Distinct();
If you are using solr and you want all distinct value for specific field then must start reading solr faceting doc, its amazing.
Only add below query in your query and you we get full list of all distinct categories.
&facet=true&facet.field=Categories
That's it, It will gives result set as you expected.
QueryOptions options = new QueryOptions
{
StartOrCursor = new StartOrCursor.Start(0),
Rows = 1,
Facet = new FacetParameters { Queries = new[] { new SolrFacetFieldQuery("Categories") } }
};
var result = _solrConnection.Query(new SolrQuery("*"), options);
if (result.FacetFields.ContainsKey("Categories"))
{
return result.FacetFields["Categories"]
.Where(li => li.Value > 0)
.Select(y=> y.Key)
.ToList();
}
That's my current solution. It works, but I hoped there would be a better way. I'm searching for a result just to get all the categories.

C# MongoDB - How to add and remove item to multiple nested array elements?

I have a document in MongoDB that looks like that:
{
"Id":"123",
"Product": "test",
"Tags":[
{
"Name": "name",
"Categories": [
{
//item
},
{
//item
}
]
},
{
"Name": "name",
"Categories": [
{
//item
},
{
//item
}
]
}
]
}
Now, I need to add a new Item and it needs to be added to all of the categories of the Tags element of that Product by its Id.
For example, when I'll insert Item 3 the document should look like this:
{
"Id":"123",
"Product": "test",
"Tags":[
{
"Name": "name",
"Categories": [
{
//item 1
},
{
//item 2
},
{
//item 3
}
]
},
{
"Name": "name",
"Categories": [
{
//item 1
},
{
//item 2
},
{
//item 3
}
]
}
]
}
and same goes for removing an item, it needs to be removed from all of the categories as well.
Is there a way to do that with the C# MongoDB Driver without pulling the object and "manually" updating them?
You can try something like below in 2.5 driver with 3.6 version.
Finds the document with filter criteria and update which includes new positional identifier to update multiple elements in array inside UpdateOne method.
$[] updates all the Tags arrays to include new item in all Categories array. It acts as a placeholder for updating all elements in array.
Push
var filter = Builders<Product>.Filter.Eq("Id", "123");
var update = Builders<Product>.Update.Push("Tags.$[].Categories", "Item 3");
var result = collection.UpdateOne(filter, update);
Pull
var filter = Builders<Product>.Filter.Eq("Id", "123");
var update = Builders<Product>.Update.Pull("Tags.$[].Categories", "Item 3");
var result = collection.UpdateOne(filter, update);
Additional Information:
You can set the ArrayFilters options in UpdateOptions for applying query criteria on nested array to control what elements to update.
For example to update all the Categories in Tags array where each tag has Name name.
var filter = Builders<Product>.Filter.Eq("Id", "123");
var update = Builders<Product>.Update.Push("Tags.$[t].Categories", "Item 3");
var arrayFilters = new List<ArrayFilterDefinition>{ new ArrayFilterDefinition(new BsonDocument("t.Name", "name")) };
var updateOptions = new UpdateOptions({ArrayFilters = arrayFilters});
var result = collection.UpdateOne(filter, update, updateOptions);

Serialize list of dictionaries into accepted DataTable Ajax object

I have a web application in which I'm retrieving some data into bootstrap table, what i want to do now is to use jQuery DataTable instead of the current as it has too much useful features.
Currently I'm retrieving the data from the server side using OOP approach, where a class object represents a data row in a particular table, and this object includes a dictionary which stores column names and values.
What I'm doing now is that I'm retrieving these class objects and append each dictionary of each object in a List<Item> and then serialize this list using JavaScriptSerializer object, and this object returns the following JSON format:
[
{
"slno":"2",
"status_message":"Lights still flashing",
"crm_services_id":"1", "subject_id":"Lights are flashing",
"severity_id":"5",
"user_id":"husain.alhamali",
"status_id":"1"
},
{
"slno":"3",
"status_message":"lights working fine",
"crm_services_id":"2",
"subject_id":"Lights are flashing",
"severity_id":"3",
"user_id":"husain.alhamali",
"status_id":"2"
}
]
When i tried to use this object to fill my DataTable AJAX I've got an error says:
Invalid JSON response
I saw some examples of a valid JSON response that is acceptable to a DataTable which is as follow:
{
"data": [
[
"Tiger Nixon",
"System Architect",
"Edinburgh",
"5421",
"2011/04/25",
"$320,800"
],
[
"Garrett Winters",
"Accountant",
"Tokyo",
"8422",
"2011/07/25",
"$170,750"
]
}
Now my question is is there any tool or plugin that could re-format my JSON string into an acceptable format like the one above?
With this HTML:
<table id="example"></table>
This JS will create a table:
var data = [{
"slno": "2",
"status_message": "Lights still flashing",
"crm_services_id": "1",
"subject_id": "Lights are flashing",
"severity_id": "5",
"user_id": "husain.alhamali",
"status_id": "1"
}, {
"slno": "3",
"status_message": "lights working fine",
"crm_services_id": "2",
"subject_id": "Lights are flashing",
"severity_id": "3",
"user_id": "husain.alhamali",
"status_id": "2"
}];
function getColumns(){
for(var i = 0; i < data.length; i++){
let columnsArray = [];
var keys = Object.keys(data[i]);
for(k in Object.keys(data[i])){
if(data[i].hasOwnProperty(keys[k])){
columnsArray.push({
"data":keys[k]
});
}
}
return columnsArray;
}
}
$(document).ready(function() {
var table = $('#example').DataTable({
"columns": getColumns(),
"data": data
});
});
Working example. Hope that helps.
dataTable require json data in return from ajax response having following keys
1. data
2. draw
3. recordsTotal
4. recordsFiltered
Use this:
var data = list.Select(u => u.GetType()
.GetProperties()
.Select(p => p.GetValue(u, null)));
example
public class User
{
public int userId { get; set; }
public string name { get; set; }
}
public class Programm
{
static void Main()
{
var list = new List<User>();
list.Add(new User
{
userId = 1,
name = "name 1",
});
list.Add(new User
{
userId = 2,
name = "name 2",
});
var data = list.Select(u => u.GetType()
.GetProperties()
.Select(p => p.GetValue(u, null)));
Console.WriteLine(new JavaScriptSerializer().Serialize(new
{
data = data
}));
}
}
result
{
"data" : [
["1", "name 1"],
["2", "name 2"]
]
}

Categories