LiteDB Insert list with BsonRef - c#

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

Group By, Count and get Total by items inside list

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() };

Mongodb C# Update element in an multiple array with multiple values

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 the List In c# MongoDB.Driver

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"
}
]
}

Write query for get info from mongo from specific document structure

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.

How to get records if only all of the list elements included in nested collections?

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")
]
}
}
}

Categories

Resources