How can i make filter and add/update third, fourth level child of a mongodb document using C#.
I can add/Update till second level but not further. Please provide me a solution or any kind reference from where can get help. Is there any other way to do it except builders..Elemmatch.
Here is my classes and code:
namespace CrudWithMultilvelNestedDoc
{
public class Channel
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public string Id { get; set; }
public string Name { get; set; }
public Episode[] Episodes { get; set; }
}
public class Episode
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public string Id { get; set; }
public string Name { get; set; }
public Track[] Tracks { get; set; }
}
public class Track
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public string Id { get; set; }
public string Name { get; set; }
public Like[] Likes { get; set; }
}
public class Like
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public string Id { get; set; }
public string Name { get; set; }
}
}
//First Method
//var filter = Builders<Channel>.Filter.And(Builders<Channel>
// .Filter.Where(x => x.Id == "5e4606e6ae7b090688671416"), // OR &
// Builders<Channel>.Filter.ElemMatch(e => e.Episodes, Builders<Episode>
// .Filter.Eq(e => e.Id, "5e460851d29c1b3df4d27b7d")));
//Second Method
//var filter = Builders<Channel>.Filter.Eq(e => e.Id, "5e4606e6ae7b090688671416")
// & Builders<Channel>.Filter.ElemMatch(e => e.Episodes, Builders<Episode>.Filter.Eq(e => e.Id, "5e46071d385a672b0cea0f86"));
//Third Method
var filter = channelFilter.ElemMatch(e => e.Episodes, episodeFilter.ElemMatch(e=> e.Tracks, trackFilter.Eq(e => e.Id, "5e460dbe2bc5e70c9cfeac21")));
var data = collection.Find(filter);
//Update Filter
var update = Builders<Channel>.Update.Push("Episodes[-1].Tracks[-1].Likes", like);
var result = collection.UpdateOne(filter, update);
//Data
{"_id":"5e4606e6ae7b090688671416","Name":"Channel 1","Episodes":[{"_id":"5e46071d385a672b0cea0f86","Name":"Episode 1","Tracks":[{"_id":"5e460dbe2bc5e70c9cfeac21","Name":"Trak 1","Likes":[]},{"_id":"5e4612d60747a2121870c815","Name":"Trak 2","Likes":[]}]},{"_id":"5e460851d29c1b3df4d27b7d","Name":"Episode 2","Tracks":[{"_id":"5e460e307ca6843758ce814e","Name":"Trak 1","Likes":[]}]}]}
As per documentation:
The positional $ operator cannot be used for queries which traverse more than one array
So using -1 is not a way forward here. The approach you should take is the $ positional filtered operator.
There's no strongly-typed representation in C# so your code can look like below:
var filter = Builders<Channel>.Filter.Eq(x => x.Id, "5e4606e6ae7b090688671416");
var like = new Like() {Name = "new like", Id = "1"};
var episodeId = "5e46071d385a672b0cea0f86";
var trackId = "5e460dbe2bc5e70c9cfeac21";
var update = Builders<Channel>.Update.Push("Episodes.$[e].Tracks.$[t].Likes", like);
var arrayFilters = new List<ArrayFilterDefinition>();
ArrayFilterDefinition<BsonDocument> episodesFilter = new BsonDocument("e._id", new BsonDocument("$eq", episodeId));
ArrayFilterDefinition<BsonDocument> tracksFilter = new BsonDocument("t._id", new BsonDocument("$eq", trackId));
arrayFilters.Add(episodesFilter);
arrayFilters.Add(tracksFilter);
var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters };
var result = mongoDBCollection.UpdateOne(filter, update, updateOptions);
Related
I Create a web-site and I have a problem. When I'm tring to get datas from mongodb and convert it to list, I have an error "Cannot apply indexing with [] to an expression of type 'CategoryModel'"
this is classes
public class CategoryModel
{
[BsonId]
public ObjectId id { get; set; }
[BsonElement("title")]
[JsonProperty("title")]
public string Name { get; set; }
[BsonElement("slug")]
public string slug { get; set; }
[BsonElement("__v")]
public int __v { get; set; }
}
public class ProductsModel
{
[BsonId]
public ObjectId id { get; set; }
[BsonElement("title")]
public string Name { get; set; }
[BsonElement("desc")]
public string Desc { get; set; }
[BsonElement("price")]
public int price { get; set; }
[BsonElement("category")]
public CategoryModel category { get; set; }
[BsonElement("image")]
public string ImageURL { get; set; }
}
this is my conntroller
public class ProductsController:Controller
{
private readonly IConfiguration _configuration;
List<CategoryModel> categoriesList = new List<CategoryModel>();
List<ProductsModel> productsList = new List<ProductsModel>();
[HttpGet("products")]
public async Task<IActionResult> Product()
{
var client = new MongoClient("mongodb://localhost:27017/");
var database = client.GetDatabase("cmscart");
var collection = database.GetCollection<CategoryModel>("categories");
var result = await collection.Find(new BsonDocument()).ToListAsync();
foreach (var item in result)
{
categoriesList.Add(new CategoryModel() { Name = (string)item["[title]"] }); //here I have an error
}
//products
var client2 = new MongoClient("mongodb://localhost:27017");
var database2 = client2.GetDatabase("cmscart");
var collection2 = database2.GetCollection<ProductsModel>("products");
var result2 = await collection2.Find(new BsonDocument()).ToListAsync();
foreach (var item2 in result2)
{
productsList.Add(new ProductsModel() { Name = (string)item2["title"], Desc = (string)item2["desc"], price = (int)item2["price"], ImageURL = (string)item2["image"] }); // and here I have an error
}
return View(categoryProduct);
}
}
I found a lot of solutions but I don't understand why this error is appear, because if this trick do with SQL then I don't have this error
You should be able to use the deserialized object directly:
var client = new MongoClient("mongodb://localhost:27017/");
var database = client.GetDatabase("cmscart");
var collection = database.GetCollection<CategoryModel>("categories");
List<CategoryModel> categoriesList = await collection.Find(new BsonDocument()).ToListAsync();
//products
var collection2 = database2.GetCollection<ProductsModel>("products");
List<ProductsModel> products = await collection2.Find(new BsonDocument()).ToListAsync();
Also, don't use class properties for local variables, declare everything in the innermost scope possible (in general). Ideally the MongoClient or Database would be injected into the class constructor too. You don't want to be instantiating them in an action method and you definitely don't want configuration values in there.
Initially I was using automapper for this but its seems way harder for me to implement it.
Basically, I just want to return an empty list instead of null values. I can do this on projects level but not on teammates level. The API must not return a null because the UI that consumes it will have an error.
Sample of my implementation below:
Projects = !Util.IsNullOrEmpty(x.Projects) ? x.Projects : new List<ProjectsDto>(),
Ill highly appreciate if someone can guide me on how to manually map this with null/empty checking.
If you can also provide and example using automapper that too will be very helpful.
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public List<ProjectsDto> Projects { get; set; }
}
public class ProjectsDto
{
public string Status { get; set; }
public List<TeammatesDto> Teammates { get; set; }
}
public class TeammatesDto
{
public string TeammateName { get; set; }
public string PreviousProject { get; set; }
}
//Get by Id
var employee = await _context.Employees
.Where(x => x.id.Equals(request.Id)
.FirstOrDefaultAsync(cancellationToken);
//Map employee
EmployeeDto ret = new EmployeeDto()
{
Id = employee.id,
Name = employee.Name,
Projects = null //TODO: map manually
}
//Get all employees
var employees = await _context.Employees.AsNoTracking()
.ToListAsync(cancellationToken);
//Map here
IList<EmployeeDto> list = new List<EmployeeDto>();
foreach (var x in employees)
{
EmployeeDto dto = new EmployeeDto()
{
Id = x.id,
Name = x.Name,
Projects = null //TODO: map manually
};
list.Add(dto);
}
return list;
Instead of materializing full entities, do the following:
var query = _context.Employees
.Select(e = new EmployeeDto
{
Id = e.id,
Name = e.Name,
Projects = e.Projects.Select(p => new ProjectDto
{
Status = p.Status,
Templates = p.Templates.Select(t => new TemplateDto
{
TeammateName = t.TeammateName,
PreviousProject = t.PreviousProject
}).ToList()
}).ToList()
}
);
var result = await query.ToListAsync();
I have an issue with elasticsearch nest 2.x client
I have a one model like this:
public class ProductPrice
{
public string ProductImagePath { get; set; }
public string VideoPath { get; set; }
public bool ProfitRateIsSet { get; set; }
public decimal? ProfitRate { get; set; }
public decimal TLPrice { get; set; }
public string SearchKey { get; set; }
}
And i wrote this code lines,
var node = new Uri("http://localhost:9200");
var settings = new ConnectionSettings(node);
settings.PrettyJson(true);
var client = new ElasticClient(settings);
var createIndexDescriptor = new CreateIndexDescriptor("product-price")
.Mappings(ms => ms
.Map<ProductPrice>(m => m.AutoMap())
);
var response = client.CreateIndex(createIndexDescriptor);
var request2 = client.Search<ProductPrice>(s => s
.Index(ElasticConnect.Constants.Index.PRODUCT_PRICE)
.Type(ElasticConnect.Constants.Type.COUCHBASE_DOCUMENT)
.From(0)
.Size(10)
.Query(q => q
.Term("productID", "210068468")
)
);
search is success to find product but productprice model properties always null.
Where am I doing wrong?
I'm sure someone else has asked this but I searched on what I could think of to find the solution.
I've got the following data models to match tables in my SQL db:
public class ProfileDetailModel
{
public string id { get; set; }
public string name { get; set; }
public StyleList[] styleList { get; set; }
public FabricList[] fabricList { get; set; }
}
public class StyleList
{
public string id { get; set; }
public string name { get; set; }
}
public class FabricList
{
public string id { get; set; }
public string fabricName { get; set; }
}
This is the current query code:
var query = (from t in db.tblProfiles
select new ProfileDetailModel()
{
id = t.id,
name = t.name
});
var querylist = await query.ToListAsync();
(prototyped linq queries below for style and fabric)
var styleQuery = (from t in db.tblStyles
select new styleList()
{
id = t.id,
name = t.name
});
var fabricQuery = (from t in db.tblFabrics
select new fabricList()
{
id = t.id,
name = t.name
});
if (queryList.Count > 0)
{
var item = queryList[0];
item.styleList = styleQuery;
item.fabricList = fabricQuery;
}
I'll have one profileDetailModel with multiple items in styleList and in fabricList. EG.
ProfileDetailModel
Data: Pants
styleList: Bell Bottom, Straight Leg, Boot fit
fabricList: jean-blue, jean-black, plaid
All three above models are tables in my db. I could issue 3 separate queries to read the data then assemble after the fact. But is there a way I can do a linq query to include the two arrays in the main query in one shot?
Try this:
var newQuery = (from p in db.tblProfiles
select p)
.AsEnumerable()
.Select(x => new ProfileDetailModel()
{
id = x.id,
name = x.name,
styleList = styleQuery,
fabricList = fabricQuery
});
I am a beginner in MongoDB. Please see my models below.
public class Technology
{
public Technology()
{
ProductGroups = new List<ProductGroup>();
}
[BsonRepresentation(BsonType.ObjectId)]
public ObjectId _id { get; set; }
public string Name { get; set; }
public IEnumerable<ProductGroup> ProductGroups { get; set; }
}
public class ProductGroup
{
[BsonRepresentation(BsonType.ObjectId)]
public ObjectId _id { get; set; }
public string Name { get; set; }
}
Now the data shows like below.
I am try to add the ProductGroup ( it's a BsonDocument Collection) collection in Technology.
Use generic types where you can. Because this code parent["ProductGroups"] is dangerous place for any refactoring.
Your task can be done in one query
var productGroup = new ProductGroup { Id = ObjectId.GenerateNewId(), Name = model.Name };
var collection = database.GetCollection<Technology>("Technology");
var update = Builders<Technology>.Update.AddToSet(x => x.ProductGroups, productGroup);
await collection.FindOneAndUpdateAsync(x => x.Id == model._id, update);
Change your Model of Technology as
[BsonElementAttribute("productgroups")]
public IList<ProductGroup> ProductGroups{ get; set; }
Then,
var productGroup = new BsonDocument().Add("_id", productGroup_id).Add("Name", name);
var technologies = database.GetCollection("technology");
var technology = technologies.FindOneById(ObjectId.Parse(technology_id));
technology["productgroups"] = new BsonArray().Add(BsonValue.Create(productGroup));
technologies.Save(technology);
#CodingDefined I change my code as per the v2.0.1.27
Please see my code below. Thank you very much for your help.
var productGroup = new BsonDocument()
.Add("_id", ObjectId.GenerateNewId())
.Add("Name", model.Name);
BsonDocument parent = null;
var _parent = Collection.FindOneByIdAs(typeof(BsonDocument), model._id);
if (_parent != null)
{
parent = _parent.ToBsonDocument();
parent["ProductGroups"] = new BsonArray().Add(BsonValue.Create(productGroup));
Collection.Save(parent);
}
Please make sure, the new child record is not clearing the existing records
parent["ProductGroups"] = parent["ProductGroups"].AsBsonArray.Add(productGroup);