Raven DB Hierarchy issue - ravendb

I've been through a few example but can't get this to work:
The end goal is to get the parent and child name together split by a hyphen.
I have an index:
public class Category_Hierarchy : AbstractIndexCreationTask<CategoryDocument>
{
public Category_Hierarchy()
{
Map = categories => from cat in categories
from parent in Recurse(cat, c => LoadDocument<CategoryDocument>("CategoryDocuments/" + c.ParentId))
select new
{
Id = cat.Id,
Name = string.Format("{0} - {1}", cat.Name, parent.Name)
};
}
}
I'm querying it like:
List<Category_Hierarchy> cats = session.Query<Category_Hierarchy>().ToList();
I currently store the parent Id so append the document name 'CategoryDocments/' to the LoadDocument not sure if this is the correct approach.

Related

Create relation hasMany() with composite field

Have the relation in model Product :
public function getDiscount()
{
return $this->hasMany(Discount::className(), ['id' => 'available_discount']);
}
Model has field available_discount, that stores data as 1;2;3, where 1;2;3 is discount ids.
Query Product::find()->joinWith('discount d')->where(['d.id' => [1,2,3]])->all()return products with key discount = [].
How i can return discounts as relation with ID 1, 2, 3 ?
Try this:
public function getDiscounts()
{
$idsAsArray = explode(';', $this->available_discount);
$query = Discount::find()->where(['id' => $idsAsArray]);
$query->multiple = true;
return $query;
}
And then get them via:
$product->discounts; // returns Discount[]
$product->getDiscounts()->count(); // gets the count of discount models.

Raven DB filter on subset of array items and sort on the cheapest of the filter results items

Assuming i have an parent class that I filter on various properties, one of which is a property that is an array of items .
Now say that i want to only return the parent item if my array of items as above a min value and below a max value ...that's fine i can work that bit out;
What if i then want to then sort on the filtered result set of those items
I made a c# fiddle example to show what im trying to achieve :
https://dotnetfiddle.net/mV4d28
(note that foo2 is returned first even though foo1 has items in its array that are less that those in foo2)
As i need to do this using a index i need the index to be able to compute the order by based on the filter criteria used in my query.
I know elasticsearch has an inner hits function that dose this and mongo has pipelines which also dose this so im sure Raven must have a way of doing this too ?
I was hoping using just index and a transform with prams i could achieve this so I tried it:
my index and transform look like this
public class familyTransfrom : AbstractTransformerCreationTask<ParentItem>
{
public class Result : ParentItem{
public double[] ChildItemValuesFiltered { get; set; }
}
public familyTransfrom(){
TransformResults = parents => from parent in parents
let filterMinValue = Convert.ToDouble(ParameterOrDefault("FilterMinValue", Convert.ToDouble(0)).Value<double>())
let filterMaxValue = Convert.ToDouble(ParameterOrDefault("FilterMaxValue", Convert.ToDouble(9999)).Value<double>())
select new Result{
ParentItemId = parent.ParentItemId,
ParentItemName = parent.ParentItemName,
ParentItemValue = parent.ParentItemValue,
//ChildItemValuesFiltered = parent.ChildItems.Where(p => p.ChildItemValues.Any(y => Convert.ToDouble(y) >= Convert.ToDouble(filterMinValue) && Convert.ToDouble(y) <= Convert.ToDouble(filterMaxValue))).SelectMany(t => t.ChildItemValues).ToArray<double>(),
ChildItemValuesFiltered = parent.ChildItems.SelectMany(p => p.ChildItemValues.Where(y => Convert.ToDouble(y) >= Convert.ToDouble(filterMinValue) && Convert.ToDouble(y) <= Convert.ToDouble(filterMaxValue))).ToArray<double>(),
ChildItems = Recurse(parent, x => x.ChildItems).Select(y => y).ToArray()
};
}
}
public class familyIndex : AbstractIndexCreationTask<ParentItem>{
public class Result : ParentItem {
public double[] ChildItemValues { get; set; }
}
public familyIndex(){
Map = parents => from parent in parents
select new Result{
ParentItemId = parent.ParentItemId,
ParentItemName = parent.ParentItemName,
ParentItemValue = parent.ParentItemValue,
ChildItemValues = parent.ChildItems.SelectMany(p => p.ChildItemValues.Select(y => y)).ToArray(),
ChildItems = Recurse(parent, x => x.ChildItems).Select(y => y).ToArray()
};
Index("ParentItemId", FieldIndexing.Analyzed);
Index("ParentItemName", FieldIndexing.Analyzed);
Index("ParentItemValue", FieldIndexing.Analyzed);
Index("ChildItemValues", FieldIndexing.Analyzed);
Index("ChildItems", FieldIndexing.Analyzed);
}
}
my query is as follows , (this is using the live raven playground so this should just work out of the box it you want to use it)
using (IDocumentStore store = new DocumentStore { Url = "http://live-test.ravendb.net/", DefaultDatabase = "altha" })
{
store.Initialize();
using (IDocumentSession session = store.OpenSession())
{
if(1 == 2){
//foreach (ParentItem element in data.OfType<ParentItem>()) {
// session.Store((ParentItem)element);
// session.SaveChanges();
//}
new familyIndex().Execute(store);
new familyTransfrom().Execute(store);
}else{
double filterMinValue = 3.0;
double filterMaxValue = 4.0;
var results = session
.Advanced
.DocumentQuery<familyIndex.Result,familyIndex>()
.WhereBetweenOrEqual("ChildItemValues", filterMinValue, filterMaxValue)
.SetResultTransformer<familyTransfrom, familyTransfrom.Result>()
.SetTransformerParameters(new Dictionary<string, RavenJToken> {
{ "FilterMinValue", filterMinValue },
{ "FilterMaxValue", filterMaxValue } })
.OrderBy("ChildItemValues")
.OfType<ParentItem>().ToList();
results.Dump();
}}
}
What i found was i cant use "ChildItemValuesFiltered" from the transform result as its not index. So unless i can order by the result of a transform ? i couldn't get this to work as although it filters it dosnt order correctly.
Is there another to achieve what i want using projections or intersection or rank or reduce try method ?
I was thinking if i had to perhaps i could use https://ravendb.net/docs/article-page/3.5/csharp/indexes/querying/sorting#custom-sorting
and do something like this:
public class SortByNumberOfCharactersFromEnd : IndexEntriesToComparablesGenerator
{
private readonly double filterMinValue;
private readonly double filterMinValue;
public SortByNumberOfCharactersFromEnd(IndexQuery indexQuery)
: base(indexQuery)
{
filterMinValue = IndexQuery.TransformerParameters["FilterMinValue"].Value<double>(); // using transformer parameters to pass the length explicitly
filterMaxValue = IndexQuery.TransformerParameters["FilterMaxValue"].Value<double>();
}
public override IComparable Generate(IndexReader reader, int doc)
{
var document = reader.Document(doc);
double[] childItemValues = (double[])document.GetValues("ChildItemValuesFiltered").Select(double.Parse).ToArray(); // this field is stored in index
return childItemValues.Where(x => x >= min && x <= max).Min();
}
}
then do a where filter and order by clause using index and transform passing in the same prams that i use in the where filter . however im not sure if this would work ?
More importantly im not sure how i go about getting the sort dll into the plugins ie what name space should the class go under, what name spaces dose it need to import, what assembly name dose it need to use etc
According to https://ravendb.net/docs/article-page/3.5/csharp/server/plugins/what-are-plugins i just need to drop the dll in and raven will this this up , however i cant seem to find what name space i need to reference for IndexEntriesToComparablesGenerator ?
im using linqpad 5 to test my stuff ...so in order to use the custom order i have to reference the class
any tips or advice or how to guild/examples welcome
so it didn't occur to me that i could do the filtering in the transform
TransformResults = parents => from parent in parents
let filterMinValue = Convert.ToDouble(ParameterOrDefault("FilterMinValue", Convert.ToDouble(0)).Value<double>())
let filterMaxValue = Convert.ToDouble(ParameterOrDefault("FilterMaxValue", Convert.ToDouble(9999)).Value<double>())
select new {
ParentItemId = parent.ParentItemId,
ParentItemName = parent.ParentItemName,
ParentItemValue = parent.ParentItemValue,
//ChildItemValuesFiltered = parent.ChildItems.Where(p => p.ChildItemValues.Any(y => Convert.ToDouble(y) >= Convert.ToDouble(filterMinValue) && Convert.ToDouble(y) <= Convert.ToDouble(filterMaxValue))).SelectMany(t => t.ChildItemValues).ToArray<double>(),
ChildItemValuesFiltered = parent.ChildItems.SelectMany(p => p.ChildItemValues.Where(y => Convert.ToDouble(y) >= Convert.ToDouble(filterMinValue) && Convert.ToDouble(y) <= Convert.ToDouble(filterMaxValue))).ToArray<double>(),
ChildItems = Recurse(parent, x => x.ChildItems).Select(y => y).ToArray()
} into r
where r.ChildItemValuesFiltered.Length > 0
orderby r.ChildItemValuesFiltered.Min()
select r;
This gives me what i wanted, here are the sample query:
http://live-test.ravendb.net/databases/altha/indexes/familyIndex?start=0&pageSize=25&resultsTransformer=familyTransfrom&tp-FilterMinValue=3&tp-FilterMaxValue=4
i cant take credit for this as guys at raven helped me but sharing the knowledge for others

get list of decision for a specific meetingtitle using linq asp.net

I have a database table. What I want is to get data using group by clause as I have used in below code.
Note that Decision is another table. now I want that all the decisions related to a specific Meeting Title should be shown in list.like
meetingtitle1=decision1,decison2,decison3
meetingtitle2=decision1,decison2
but below code returns only one decisiontitle.
public List<NewMeetings> GetAllMeetings()
{
var xyz = (from m in DB.MeetingAgenda
//join mp in Meeting on m.MeetingId equals mp.MeetingId
//where m.MeetingId == 2
group m by new { m.Meeting.MeetingTitle } into grp
select new NewMeetings
{
// meetingid = grp.Key.MeetingId,
meetingtitle = grp.Key.MeetingTitle,
decision = grp.Select(x => x.Decision.DecisionTitle).FirstOrDefault(),
total = grp.Count()
}).ToList();
List<NewMeetings> list = xyz.ToList();
return list;
}
public class NewMeetings
{
public int meetingid;
public string meetingtitle;
public string decision;
public int total;
}
Can somebody please tell me how to return a list of decisions to a specific Meetingtitle?
You are doing a FirstOrDefault on the list of decisions which obviously means you are only getting a single value. Instead you can join them all together into one longer string (separated by commas as you indicated in the question) by changing this line:
decision = grp.Select(x => x.Decision.DecisionTitle).FirstOrDefault(),
To this:
decision = string.Join(",", grp.Select(x => x.Decision.DecisionTitle)),
However, as the string.Join is not recognised by Linq to Entities, you need to do the string.Join after the data has been retrieved (i.e. after the ToList):
var xyz = (from m in DB.MeetingAgenda
group m by new { m.Meeting.MeetingTitle } into grp
select new
{
meetingtitle = grp.Key.MeetingTitle,
decisions = grp.Select(x => x.Decision.DecisionTitle),
total = grp.Count()
})
.ToList()
.Select(m => new NewMeetings
{
meetingtitle = m.meetingtitle,
decision = string.Join(",", m.decisions),
total = m.total
});

Linq to sql How to select elements from a list

I have two classes:
public class Artikel
{
int id{get;set;};
string name{get;set;};
}
public class NewArtikel
{
int id{get;set;};
List<Artikel> artikels{get;set;};
}
Now, I have a list of the second class
List<NewArtikel> myList = new List<NewArtikel>()
Is there are any posibility in LINQ to SQL to select all elements from myList with the needed Artikel.name
Yes
var allRequiredArtikels = myList.SelectMany(n => n.artikels)
.Where(a => a.name == "requiredName");
The SelectMany flattens all Artikels from all the NewArtikel elements, then the Where filters them by name.
Okay so, if you want the NewArtikels that have an Artikel with the required name you could do.
var newArtikels = myList.Where(n => n.artikels
.Any(a => a.name == "requiredName"));
List<NewArtikel> myFinalList = (from artikel in myList.artikels where artikel.name = "requiredName"
select new NewArtikel(){
Id= artikel.Id
}).ToList();

How to generate id for easily sorting purpose in SQL Server?

I'm making an website and I have problem generate the parent/child tree like:
Page 1
---Sub page 1
------Sub page 1 - 1
Page 2
Page 3
---Sub page 3
------Sub page 3 - 1
------Sub page 3 - 2
If I use UID, it's impossible to write the "ORDER BY" t-sql to make the tree. I'm thinking of a function that can generate the ID (varchar) such as:
001000000
---001001000
------001001001
002000000
003000000
---003001000
------003001001
------003001002
I would skip the custom Id and store parent/child relationships in the database. Then use a recursive query to build your tree.
Look into the nested set model for hierarchies. Joe Celko has a book which covers this in addition to other ways to model trees and hierarchies in SQL. With the nested set model you would have something like this:
CREATE TABLE Tree_Nodes
(
lft INT NOT NULL,
rgt INT NOT NULL,
name VARCHAR(40) NOT NULL,
CONSTRAINT PK_Tree_Nodes PRIMARY KEY CLUSTERED (lft, rgt)
)
INSERT INTO Tree_Nodes (lft, rgt, name)
SELECT 1, 6, 'Page 1' UNION ALL
SELECT 2, 5, 'Sub page 1' UNION ALL
SELECT 3, 4, 'Sub page 1 - 1' UNION ALL
SELECT 7, 8, 'Page 2' UNION ALL
SELECT 9, 16, 'Page 3' UNION ALL
SELECT 10, 15, 'Sub page 3' UNION ALL
SELECT 11, 12, 'Sub page 3 - 1' UNION ALL
SELECT 13, 14, 'Sub page 3 - 2'
Then to get the result that you're trying to get, it's simply:
SELECT
lft,
rgt,
name
FROM
Tree_Nodes
ORDER BY
lft
For web-pages, it's straightforward to use the URL for this. Toby's idea is also okay, but it is probably more complicated than you need (although I am actually doing what he's telling you to, in another application, so I'm not dissing it, in right circumstances). Your scheme would actually work, too.
However, why would you want a function to do it for you? In a flat scheme of things, you can be happy to let AUTO_INCREMENT do your work for you; but in a hierarchy you want to decide where things go, and no function can do it for you.
Maybe you can store the parent id in the table and instead of trying to do this in the table, simply return the rows and use recursion to build you're tree as follows, for optimization after the first run, you could store the tab order and the sort order in a property you assign on the first recursive run in you're data class. Here is an example...
class Program
{
static void Main(string[] args)
{
Program program = new Program();
List<Data> rows = new List<Data>();
program.CreateData(rows);
Console.WriteLine("Data ...");
program.ShowData(rows);
Console.WriteLine("");
Console.WriteLine("Data as tree ...");
program.ShowDataAsTree(rows);
Console.WriteLine("");
}
public void ShowData(List<Data> rows)
{
foreach (Data row in rows)
{
Data parent = rows.Find(item => item.Id == row.Parent);
Console.WriteLine(String.Format("Name = {0}, Parents Name = {1}", row.Text, parent == null ? "" : parent.Text));
}
}
public void ShowDataAsTree(List<Data> rows)
{
rows.Sort((item1, item2) => item1.Text.CompareTo(item2.Text));
ShowDataSortedWrk(rows, Guid.Empty, 0);
}
public void ShowDataSortedWrk(List<Data> rows, Guid parentId, int tab)
{
foreach (Data row in rows)
{
if (row.Parent == parentId)
{
for (int i = 0; i < tab; i++)
{
Console.Write("\t");
}
Console.WriteLine(row.Text);
ShowDataSortedWrk(rows, row.Id, tab + 1);
}
}
}
public void CreateData(List<Data> rows)
{
Data alice = new Data(Guid.Empty, "Alice");
rows.Add(alice);
Data eric = new Data(Guid.Empty, "Eric");
rows.Add(eric);
Data mike = new Data(alice.Id, "Mike");
rows.Add(mike);
rows.Add(new Data(mike.Id, "Mark"));
rows.Add(new Data(eric.Id, "Jane"));
rows.Add(new Data(alice.Id, "Emma"));
rows.Add(new Data(mike.Id, "Fred"));
rows.Add(new Data(alice.Id, "Perry"));
rows.Add(new Data(eric.Id, "Julie"));
rows.Add(new Data(eric.Id, "Paul"));
}
}
public class Data
{
public Data(Guid parent, string text)
{
this.Id = Guid.NewGuid();
this.Parent = parent;
this.Text = text;
}
public Guid Id
{
get;
set;
}
public Guid Parent
{
get;
set;
}
public String Text
{
get;
set;
}
}