Hi and thanks in advance everyone!
I have a collection of the following objects:
public class ItemsModel
{
public List<int> IdCollection { get; set; }
public string Name { get; set; }
public int Weight { get; set; }
}
List<ItemsModel> col = ...;
I want to optimally store this with LiteDb and be able to modify the records.
Each ItemsModel has a unique Name+Weight set.
In the entire col, the elements of the IdCollection are also unique.
Body example:
List<ItemsModel>:
[{
IdCollection: [1,3,5,6,...],
Name: "first name",
Weight: 10
},
{
IdCollection: [2,4,...],
Name: "second name",
Weight: 5
}]
I want to index by Id
I want to expand into two tables for easy storage in LiteDb:
[{
_id: 1,
NameAndWeight: {&ref: "names"}
},
{
_id: 2,
NameAndWeight: {&ref: "names"}
},
{
_id: 3,
NameAndWeight: {&ref: "names"}
},
...
]
[{
Name: "first name",
Weight: 10
},
{
Name: "second name",
Weight: 5
}]
For this I have to make new storage classes:
public class ItemsModel
{
[BsonId]
public int Id { get; set; }
[BsonRef("names")]
public NamesModel NameAndWeight { get; set; }
}
public class NamesModel
{
[BsonId(true)]
public ObjectId Id { get; set; }
public string Name { get; set; }
public int Weight { get; set; }
}
But next step I'm having trouble...
Tell me, can I somehow save data using Insert array and Include in one operation?
Or should I use foreach to first write the NamesModel in "names" DB, get the generated _id, then write the ItemsModel with a link to the NamesModel already written to the database?
using (var db = new LiteDatabase(_strConnection))
{
var itemsDb = db.GetCollection<ItemsModel>("items");
var namesDb = db.GetCollection<NamesModel>("names");
itemsDb.EnsureIndex(x => x.Id, true);
foreach (var group in col)
{
var name = new NamesModel(group.Name, group.Weight);
namesDb.Insert(name);
var itemDb = group.IdCollection.Select(el => new ItemsModel(el, name));
var h = itemsDb.Insert(itemDb);
}
}
it is too long(
Now I did like this:
using (var db = new LiteDatabase(_strConnection))
{
var itemsDb = db.GetCollection<ItemsModel>("items");
var namesDb = db.GetCollection<NamesModel>("names");
itemsDb.EnsureIndex(x => x.Id, true);
namesDb.EnsureIndex(x => x.Name);
var temp = col.Select(el => (el.IdCollection, new NamesModel(el.Name, el.Weight))).ToList();
namesDb.Insert(temp.Select(el => el.Item2));
var temp2 = temp.SelectMany(gr => gr.IdCollection.Select(el => new ItemsModel(el, gr.Item2)));
eventsIdDB.Insert(temp2);
}
Performed basic operations in linq to reduce the number of hits in liteDb
Related
I have an Account object like:
public class Account
{
public int Id { get; set; }
public List<Elegibilities> Elegibilities { get; set; }
}
public class Elegibilities
{
public int Id { get; set; }
public string Label { get; set; }
public bool Value { get; set;}
}
My repository returns a List of Account. List<Account>
I want to group by and count by items in Elegibilities and the output would be like:
{
Total: 30,
[
{
Id : 1,
Label: "Elegibility1",
Count: 10
},
{
Id : 2,
Label: "Elegibility2",
Count: 20
}
]
}
I'm having issues because it's a list inside another list.
Is it possible to solve in a single LINQ query?
Look's like you don't want any data related to the Accounts,
This gives you a simple flat list to work with.
var flatList = accounts.SelectMany(a => a.Elegibilities);
This could be another example if you needed to include accounts related data to the output.
var group = from a in accounts
from el in a.Elegibilities
group el by el.Id into elGroup
select new
{
Id = elGroup.Key,
Count = elGroup.Count(),
Label = elGroup.First().Label
};
In the end you can access your group totals.
var result = new { Totals = group.Count() , Elegibilities = group.ToList() };
I want to update the single document in collection with the guid as filter and update value is cityType. Every guid has different citytype here i have used 3 types it may be more.
So please give a right implementation using c# code.
Models:
public class Country
{
[BsonId]
public ObjectId Id { get; set; }
public int CountryId {get; set; }
public IEnumerable<States> States { get; set; }
}
public class States
{
public Guid Guid { get; set; }
public CityType CityType { get; set; }
}
Enum CityType
{
Unknown = 0,
Rural = 1,
Urban = 2
}
Existing Collection:
{
"_id": ObjectId("6903ea4d2df0c5659334e763"),
"CountryId": 200,
"States": [
{
"Guid": "AFCC4BE7-7585-5E46-A639-52F0537895D8",
"CityType": 0,
},
{
"Guid": "208FB603-08C7-46D9-B0C0-7AF4F691A96D",
"CityType": 0,
}
}
Input:
List<States>()
{
new States()
{
Guid = "AFCC4BE7-7585-5E46-A639-52F0537895D8",
CityType = CityType.Rural
},
new States()
{
Guid = "208FB603-08C7-46D9-B0C0-7AF4F691A96D",
CityType = CityType.Urban
}
}
Expected:
{
"_id": ObjectId("6903ea4d2df0c5659334e763"),
"CountryId": 200,
"States": [
{
"Guid": "AFCC4BE7-7585-5E46-A639-52F0537895D8",
"CityType": 1,
},
{
"Guid": "208FB603-08C7-46D9-B0C0-7AF4F691A96D",
"CityType": 2,
}
}
This is the method I have tried:
public async Task<bool> UpdateType(int countryId, IEnumerable<States> states)
{
var collection = connectionFactory.GetCollection<Country>(collectionName);
var cityTypes = states.Select(x => x.CityType);
var filter = Builders<Country>.Filter.Empty;
var update = Builders<Country>.Update.Set("States.$[edit].CityType", cityTypes);
var arrayFilters = new List<ArrayFilterDefinition>();
foreach (var state in states)
{
ArrayFilterDefinition<Country> optionsFilter = new BsonDocument("state.Guid", new BsonDocument("$eq", state.Guid));
arrayFilters.Add(optionsFilter);
}
var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters };
var result = await collection.UpdateOneAsync(filter, update, updateOptions);
return result;
}
hope all details I have added here. Thanks in advance.
You don't have to loop through it:
Let's say you have a Class1 like this:
class Question : AuditableEntity {
public string Text { get; set; }
public List<string> Tags { get; set; } = new List<string>();
so you just say:
await collection.UpdateOneAsync(
someFilter,
Builders<Class1>.Update
.Set(f => f.Text, request.Question.Text)
.Set(f => f.Tags, request.Question.Tags));
How do I insert Adds many objects to the List In c# MongoDB.Driver?
my c# Entity
/// <summary>LogTest</summary>
public class VisitLog
{
/// <summary>MongoDB特有的字段</summary>
[MongoDB.Bson.Serialization.Attributes.BsonElement("_id")]
[JsonConverter(typeof(ObjectIdConverter))]
public MongoDB.Bson.ObjectId MongoId { get; set; }
/// <summary>YMD datetime</summary>
public int Yymmdd { get; set; }
/// <summary>Visitor</summary>
public string Visitor { get; set; }
/// <summary>VisitInfos</summary>
public List<VisitInfo> VisitInfos { get; set; }
}
In the MongoDBCode Like the code
// 1
{
"_id": ObjectId("5f506eb02000a9b52d72a600"),
"Yymmdd": NumberInt("20200903"),
"Visitor": "360spider",
"VisitInfos": [ ]
}
i will add objects to the "VisitInfos": [ ]
How do I insert Adds many objects to the List In c# MongoDB.Driver?
Way 1: insert only one object. my test code is:
var filter = Builders<VisitLog>.Filter.Eq("_id", item.MongoId);
var update = Builders<VisitLog>.Update.Push("VisitInfos", new VisitInfo { Visitor = Visitor, Browser = "IE", Ip = "192.168.1.1", Createtime = DateTime.Now.ToUnixTimeLocalIslong() });
var result = BB.UpdateOne(filter, update);
The Way 2: i want to insert InsertManyAsync
var items = BB.Find(x => x.Yymmdd.Equals(Yymmdd) && x.Visitor.Equals(Visitor)).Project<VisitLog>(fields).ToList();
if (items.Count > 0)
{
var item = items[0];
var VisitInfos = new List<VisitInfo>();
for (int j = 0; j < 10000; j++)
{
VisitInfos.Add(new VisitInfo { Visitor = Visitor, Browser = "IE", Ip = "192.168.1.1", Createtime = DateTime.Now.ToUnixTimeLocalIslong() });
}
var filter = Builders<VisitLog>.Filter.Eq("_id", item.MongoId);
var update = Builders<VisitLog>.Update.Push("VisitInfos", VisitInfos);
var result = BB.UpdateOne(filter, update);
}
the way 2 is failed.
please help me.
this very much.....
On the Builders<Order>.Update there's a PushEach which accepts an IEnumerable. This is equivalent to doing:
{ $push: { scores: { $each: [ 90, 92, 85 ] } } }
https://docs.mongodb.com/manual/reference/operator/update/push/#append-multiple-values-to-an-array
For simplicity here's an example of an order and order items.
In MongoDB we'll have:
> db.orders.find()
{ "_id" : ObjectId("5f50aef4d7d9f967d0322932"), "Items" : [ ] }
Then we'll execute the following C# Code.
var client = new MongoClient();
var db = client.GetDatabase("test");
var items = db.GetCollection<Order>("orders");
var filter = Builders<Order>.Filter.Empty;
var update = Builders<Order>.Update.PushEach(x => x.Items, new[]
{
new OrderItem{Name = "Order 1", Price = 10.1M},
new OrderItem{Name = "Order 2", Price = 20.2M}
});
await items.UpdateOneAsync(filter, update);
public class Order
{
public ObjectId Id { get; set; }
public List<OrderItem> Items { get; set; }
= new List<OrderItem>();
}
public class OrderItem
{
public string Name { get; set; }
public decimal Price { get; set; }
}
Now if we take a look at our document in MongoDB we'll have the 2 items added to our array
db.orders.find().pretty()
{
"_id" : ObjectId("5f50aef4d7d9f967d0322932"),
"Items" : [
{
"Name" : "Order 1",
"Price" : "10.1"
},
{
"Name" : "Order 2",
"Price" : "20.2"
}
]
}
Imagine I have this document structure:
DocumentOne
{
string Id { get; set; };
string InnerId { get; set; }
DocumentTwo[] InnerDocuments { get; set; }
}
DocumentTwo
{
string Id { get; set; }
string AnotherField { get; set; }
}
I try to write query to filter documents by condition DocumentOne.InnerId != DocumentTwo.Id in .net using mongodb driver.
I tried to use
Builder<DocumentOne>.Filter.ElemMatch(x => x.InnerDocuments, y => y.Id != ???)
but I cannot access InnerId in this query (question marks)
If I try to use Fluent Syntax like
database.Find(x => x.InnerDocuments.Contains(y => y.Id != x.InnerId))
or
database.Find(!x => x.InnerDocuments.Any(y => y.Id != x.InnerId))
I got error message from driver.
How I need to re-write this query?
There's 2 ways to do this, either by aggregation or using a find with a expression.
We'll take a look at aggregation, as to me it flows a bit easier.
So starting off wtih we'll have 2 models for MongoDB like you said
public class DocumentOne
{
public string Id { get; set; }
public string InnerId { get; set; }
public DocumentTwo[] InnerDocuments { get; set; }
}
public class DocumentTwo
{
public string Id { get; set; }
public string AnotherField { get; set; }
}
Then we'll need a second projection to keep track of things later down the line when we unwind the inner documents:
public class DocumentOneProjection
{
public string Id { get; set; }
public string InnerId { get; set; }
public DocumentTwo InnerDocuments { get; set; }
}
So we'll throw some data in to MongoDB to play around with
var client = new MongoClient();
var database = client.GetDatabase("test");
var collection = database.GetCollection<DocumentOne>("documents");
await database.DropCollectionAsync(collection.CollectionNamespace.CollectionName);
await collection.InsertManyAsync(new[]
{
new DocumentOne()
{
Id = "1", InnerId = "10", InnerDocuments = new[]
{
new DocumentTwo()
{
Id = "11"
}
}
},
new DocumentOne()
{
Id = "2", InnerId = "20", InnerDocuments = new[]
{
new DocumentTwo()
{
Id = "20"
}
}
},
new DocumentOne()
{
Id = "3", InnerId = "30", InnerDocuments = new[]
{
new DocumentTwo()
{
Id = "30"
},
new DocumentTwo()
{
Id = "31"
}
}
}
});
Then we'll be able to create an aggregation query
var items = await collection.Aggregate()
.Unwind(x => x.InnerDocuments)
.AppendStage<BsonDocument>(
#"{ $addFields: { matchInner: { $cmp: [ ""$InnerId"", ""$InnerDocuments._id"" ] } } }")
.Match("{ matchInner: { $ne : 0 } }") // 0 if the two values are equivalent.
.AppendStage<DocumentOneProjection>(
#"{ $unset: ""matchInner"" }")
.ToListAsync();
This query starts off with unwinding all the inner documents, this will create a document for each document in the array (https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/)
Then it creates a new field that we will then create a match on later, this uses a compare ($cmp - https://docs.mongodb.com/manual/reference/operator/aggregation/cmp/)
Then we just match the new field and then execute the query to get back the documents.
I want to filter my query using MongoDb c# driver. I have a query list so I need to filter records if all the list item included in the sub collection of a collection.
public class Hotels
{
[BsonId]
// standard BSonId generated by MongoDb
public ObjectId InternalId { get; set; }
public string Id { get; set; }
public string Name { get; set; }
public List<int> Amenities { get; set; }
}
I have a query parameter "amenities" as string then splitted with respect to ",".
if (!string.IsNullOrEmpty(amenties))
{
var amenityList =Array.ConvertAll<string,int>( amenties.Split(","),int.Parse).ToList();
filter &= Builders<Hotels>.Filter.Where(r => r.Amenities.All(i => amenityList.Contains(i)));
}
var result =_context.GetCollection<Hotels>(typeof(Hotels).Name).Find(filter);
Throws exception : unsupported filter. Thus, how can I fix this query ?
Thanks
your Where expression is wrong. have a look at the last line of the following code for the correct expression.
using MongoDB.Entities;
using System;
using System.Linq;
namespace StackOverflow
{
public class Program
{
public class Hotel : Entity
{
public string Name { get; set; }
public int[] Amenities { get; set; }
}
private static void Main(string[] args)
{
new DB("test");
(new[] {
new Hotel{ Name= "hotel one", Amenities = new[] {1, 3, 5, 7 } },
new Hotel{ Name= "hotel two", Amenities = new[] {2, 4, 6, 8 } }
}).Save();
string amenities = "2,4,6,8";
int[] amenityList = amenities.Split(",").Select(a => Convert.ToInt32(a)).ToArray();
var result = DB.Find<Hotel>()
.Many(h => amenityList.All(a => h.Amenities.Contains(a)));
}
}
}
it generates the following find query:
"command": {
"find": "Hotel",
"filter": {
"Amneties": {
"$all": [
NumberInt("2"),
NumberInt("4"),
NumberInt("6"),
NumberInt("8")
]
}
}
}