CosmosDB generic OrderBy query or get last data first - sql

I'm using the code from azure project, when CosmosDB is created first time. It has a nice generic GetItemsAsync method.
I have modified it a little to have following:
public static async Task<List<T>> GetItemsAsync(Expression<Func<T, bool>> predicate, int maxItemCount = -1)
{
IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),
new FeedOptions { MaxItemCount = maxItemCount })
.Where(predicate)
.AsDocumentQuery();
List<T> results = new List<T>();
while (query.HasMoreResults)
{
results.AddRange(await query.ExecuteNextAsync<T>());
if (maxItemCount != -1)
{
break;
}
}
return results;
}
What I want to do is, in some calls to this method, I need to add OrderBy query. The problem is I have couple collections in same CosmosDB and fields are different. When I want to list one of the collections I just want to get latest ones.
So in this example, I actually send count as 50 so I will get only 50, but I also want to get recent ones. The above code returns oldest 50 as expected. Is there any solution this? I'm guessing there might be a way to get latest data first and when I call for 50, it would be latest 50 records.
Other thing I tried, was to create another method called GetItemsWithOrderByAsync and tried to give Expression>, but it requires so much modification in whole class to support TKey.

Ok, after playing around, I came up with the following as a solution. Created new method and:
public static async Task<List<T>> GetItemsWithOrderByAsync(Expression<Func<T, bool>> predicate, Expression<Func<T, object>> orderByPredicated, int maxItemCount = -1)
{
IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),
new FeedOptions { MaxItemCount = maxItemCount }).
Where(predicate).
OrderByDescending(orderByPredicated).
AsDocumentQuery();
List<T> results = new List<T>();
while (query.HasMoreResults)
{
results.AddRange(await query.ExecuteNextAsync<T>());
if (maxItemCount != -1)
{
break;
}
}
return results;
}

Related

Looping through Dapper.Net query results without the use of models

I am still new to C# and I am struggling to find a solution to my problem. My SQL dapper query returns a table (based on my understanding though it is not really a table if it is IEnumerable unlike what I am use to working with ADO and recordsets) with three columns col1, col2, and col3 and has multiple rows. I need to loop through this query result for each row and test the values (ie, a foreach loop where I check row(0).field1=5, row(1).field1 = 5 for each row, etc) do what I need to do. This seems so basic but I all the dapper tutorials I see do not show examples for this and if they do they seem to utilize class objects rather than accessing the results directly (if thats even possible or do you have to map the results to a model?) My code is as follows:
String query = "exec dbo.storeProcedure #jsonData, #mainDocJSON, #supportingDocsJSON";
IEnumerable queryResult;
using (var connection = new SqlConnection(connectionString))
{
queryResult = connection.Query(query, new { jsonData = jsonData, mainDocJSON = mainDocJSON, supportingDocsJSON = supportingDocsJSON });
}
I also end up returning IEnumerable results from the controller this code resides in so I send it back to the user in JSON using the following.
return Ok(queryResult);
connection.Query return a IEnumerable, why dont we create a class to map the set from ? Dapper is a micro-ORM, but still... ORM.
For ex: Your table return 3 column Id, Name, CreatedDate.
// declare a class to map the result first
public class ResultHolderDto
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreatedDate { get; set; }
}
// query somewhere
// This will return IEnumerable<ResultHolderDto>, feel free to play around as normal
var queryResult = await connection.QueryAsync<ResultHolderDto>(query, new { jsonData = jsonData, mainDocJSON = mainDocJSON, supportingDocsJSON = supportingDocsJSON });
foreach(var item in queryResult)
{
var col1Value = queryResult.Id;
var col2Value = queryResult.Name;
var col3Value = queryResult.CreatedDate;
// Then do something with col1Value, col2Value, col3Value...
}

Delete elements from a List using iterator

There is a List<Integer> tabulist that contains values [2,0,5].
Also, there is a List this.getRoutes() which contains keys [0,1,2,3,4,5].
I need to delete elements [2,0,5] from this.getRoutes().
So, as a result I must get the following entries in this.getRoutes():
[1,3,4]
Here is my code:
iter = this.getRoutes().iterator();
while(iter.hasNext())
{
Route r = iter.next();
if (tabulist.contains(r.getRouteKey()))
{
iter.remove();
}
}
The problem is that r.getRouteKey() is always 0. Therefore, I am always deleting the first elements from this.getRoutes(). I don't understand why the iterator does not move to [1,2,3,4,5].
How to solve this issue? Maybe I should also delete values from tabulist?
I didn't test my code, but here are 3 variations on the theme. You should test them in case I made a mistake, but they give an idea of the general idea.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
public class Class1
{
public static List<int> RemoveItems1(List<int> OriginalValues, List<int> ValuesToRemove)
{
List<int> result = new List<int>();
foreach (object item_loopVariable in OriginalValues) {
item = item_loopVariable;
if ((!ValuesToRemove.Contains(item))) {
result.Add(item);
}
}
return result;
}
public static List<int> RemoveItems2(List<int> OriginalValues, List<int> ValuesToRemove)
{
List<int> result = OriginalValues;
foreach (object item_loopVariable in ValuesToRemove) {
item = item_loopVariable;
if ((OriginalValues.Contains(item))) {
result.Remove(item);
}
}
return result;
}
public static List<int> RemoveItems3(List<int> OriginalValues, List<int> ValuesToRemove)
{
List<int> result = OriginalValues;
foreach (object item_loopVariable in from item1 in ValuesToRemovewhere (OriginalValues.Contains(item1))) {
item = item_loopVariable;
result.Remove(item);
}
return result;
}
}
The first one adds only elements to get to a result. Like I said in my comment.
The second one removes elements from a result that is set to the parameter Originalvalues. The last one is basically the same as number two, but uses LINQ.
I'm using static methods because then this can be used in any situation and you don't need to instantiate a class to do this. Which adds extra unnecessary code.

How to incorporate ASP.NET Web API ODATA into ActiveRecord/NHibernate

In ASP.NET Web API, it allows you to write ODATA queries in the url string to specify which data you want to return from a method. However the part that I'm having a hard time grasping is that ODATA works to filter an IQueryable collection of C# objects, not the database table itself.
This is impractical because really you would want to filter at the database level, as it would be horrible to return all objects from the database, load them into a C# IQueryable list, and then have the ODATA filter that list.
Here is the code, which uses NHibernate and Castle Active Record for data access:
public IQueryable<Message> GetAll()
{
return from m in MessageData.FindAllQueryable()
select ConvertToView(m);
}
public static IQueryable<Message> FindAllQueryable()
{
var criteria = DetachedCriteria.For<Message>()
.CreateAlias("MessageRecipients", "mr")
.AddOrder(new Order("Id", false));
return ActiveRecordMediator<Message>.FindAll(criteria).AsQueryable();
}
The end result of this code would be it returning all messages from the database. How do I allow the ODATA to perform its filters upon the database itself? Otherwise this whole concept of ODATA is completely impractical for real world situations.
Keep in mind, you're dealing with an IQueryable, which is not a physical list of objects. It is a query which has the potential to be executed and result in a list of entities. These queries can also be chained together:
var query = customRepository.Where(x => x.CustomerName == "John"); //no results generated
var query2 = query.Where(x => x.Salary > 100000); // still no results generated
var results = query2.ToList(); // now results are generated
You should be able to just return an instance of Session.Query from NHibernate. The OData functionality will then chain the criteria additional based on the URL and then enumerate the results back out.
Here is how I ended up getting it working. Much thanks to Andreas for leading me to NHibernate.OData.
In my controller action I get the odata from the url and pass it into my data access function:
public IQueryable<Message> GetAll(int authUserId, int userId, DateTime? startDate, DateTime? endDate)
{
LogWriter.Write(String.Format("Getting all messages for user {0}", userId));
//get messages and convert to view.
return from m in MessageData.FindAll(userId, startDate, endDate, GetOData())
select new Message(m);
}
protected string GetOData()
{
var odata = this.Request.RequestUri.Query;
odata = odata.Substring(odata.IndexOf("$"), odata.Length - odata.IndexOf("$"));
odata = odata.Replace("%20", " ");
return odata;
}
Inside the data access method, we get the NHibernate session and call session.ODataQuery:
public static IQueryable<Message> FindAll(int userId, DateTime? startDate, DateTime? endDate, string odata)
{
ICriteria query = GetSession().ODataQuery<Message>(odata);
var detachedCriteria = new ConvertedDetachedCriteria(query)
.CreateAlias("MessageRecipients", "mr")
.Add(Restrictions.Or(
Restrictions.Eq("SenderUserId", userId),
Restrictions.Eq("mr.Key.RecipientId", userId)
));
return FindAllQueryable(detachedCriteria);
}
public static ISession GetSession()
{
var factory = ActiveRecordMediator.GetSessionFactoryHolder().GetSessionFactories()[0];
return factory.OpenSession();
}
public static IQueryable<T> FindAllQueryable(DetachedCriteria criteria)
{
return ActiveRecordMediator<T>.FindAll(criteria).AsQueryable();
}
Also, a simple ConvertedDetachedCriteria class is needed to convert from ICriteria to DetachedCriteria.
public class ConvertedDetachedCriteria : DetachedCriteria
{
public ConvertedDetachedCriteria(ICriteria criteria)
: base((CriteriaImpl)criteria, criteria)
{
var impl = (CriteriaImpl)criteria;
impl.Session = null;
}
}
Hopefully this will help someone else. I now can write odata queries against my asp.net web api methods and have them filter at the db level, which is much more useful than filtering iqueryable c# objects!!
Adding to #Justin answer, without ActiveRecord one could just return something like
public IQueryable<Message> Get()
{
ICriteria query = _unitOfWork.CurrentSession.ODataQuery<Message>(GetOData());
return query.Future<Location>().AsQueryable<Location>();
}
// Taken from #Justin's answer
protected string GetOData()
{
var odata = this.Request.RequestUri.Query;
odata = odata.Substring(odata.IndexOf("$"), odata.Length - odata.IndexOf("$"));
odata = odata.Replace("%20", " ");
return odata;
}
This should put you up for testing!

How can I use Lucene's PriorityQueue when I don't know the max size at create time?

I built a custom collector for Lucene.Net, but I can't figure out how to order (or page) the results. Everytime Collect gets called, I can add the result to an internal PriorityQueue, which I understand is the correct way to do this.
I extended the PriorityQueue, but it requires a size parameter on creation. You have to call Initialize in the constructor and pass in the max size.
However, in a collector, the searcher just calls Collect when it gets a new result, so I don't know how many results I have when I create the PriorityQueue. Based on this, I can't figure out how to make the PriorityQueue work.
I realize I'm probably missing something simple here...
PriorityQueue is not SortedList or SortedDictionary.
It is a kind of sorting implementation where it returns the top M results(your PriorityQueue's size) of N elements. You can add with InsertWithOverflow as many items as you want, but it will only hold only the top M elements.
Suppose your search resulted in 1000000 hits. Would you return all of the results to user?
A better way would be to return the top 10 elements to the user(using PriorityQueue(10)) and
if the user requests for the next 10 result, you can make a new search with PriorityQueue(20) and return the next 10 elements and so on.
This is the trick most search engines like google uses.
Everytime Commit gets called, I can add the result to an internal PriorityQueue.
I can not undestand the relationship between Commit and search, Therefore I will append a sample usage of PriorityQueue:
public class CustomQueue : Lucene.Net.Util.PriorityQueue<Document>
{
public CustomQueue(int maxSize): base()
{
Initialize(maxSize);
}
public override bool LessThan(Document a, Document b)
{
//a.GetField("field1")
//b.GetField("field2");
return //compare a & b
}
}
public class MyCollector : Lucene.Net.Search.Collector
{
CustomQueue _queue = null;
IndexReader _currentReader;
public MyCollector(int maxSize)
{
_queue = new CustomQueue(maxSize);
}
public override bool AcceptsDocsOutOfOrder()
{
return true;
}
public override void Collect(int doc)
{
_queue.InsertWithOverflow(_currentReader.Document(doc));
}
public override void SetNextReader(IndexReader reader, int docBase)
{
_currentReader = reader;
}
public override void SetScorer(Scorer scorer)
{
}
}
searcher.Search(query,new MyCollector(10)) //First page.
searcher.Search(query,new MyCollector(20)) //2nd page.
searcher.Search(query,new MyCollector(30)) //3rd page.
EDIT for #nokturnal
public class MyPriorityQueue<TObj, TComp> : Lucene.Net.Util.PriorityQueue<TObj>
where TComp : IComparable<TComp>
{
Func<TObj, TComp> _KeySelector;
public MyPriorityQueue(int size, Func<TObj, TComp> keySelector) : base()
{
_KeySelector = keySelector;
Initialize(size);
}
public override bool LessThan(TObj a, TObj b)
{
return _KeySelector(a).CompareTo(_KeySelector(b)) < 0;
}
public IEnumerable<TObj> Items
{
get
{
int size = Size();
for (int i = 0; i < size; i++)
yield return Pop();
}
}
}
var pq = new MyPriorityQueue<Document, string>(3, doc => doc.GetField("SomeField").StringValue);
foreach (var item in pq.Items)
{
}
The reason Lucene's Priority Queue is size limited is because it uses a fixed size implementation that is very fast.
Think about what is the reasonable maximum number of results to get back at a time and use that number, the "waste" for when the results are few is not that bad for the benefit it gains.
On the other hand, if you have such a huge number of results that you cannot hold them, then how are you going to be serving/displaying them? Keep in mind that this is for "top" hits so as you iterate through the results you will be hitting less and less relevant ones anyway.

ROW_NUMBER() and nhibernate - finding an item's page

given a query in the form of an ICriteria object, I would like to use NHibernate (by means of a projection?) to find an element's order,
in a manner equivalent to using
SELECT ROW_NUMBER() OVER (...)
to find a specific item's index in the query.
(I need this for a "jump to page" functionality in paging)
any suggestions?
NOTE: I don't want to go to a page given it's number yet - I know how to do that - I want to get the item's INDEX so I can divide it by page size and get the page index.
After looking at the sources for NHibernate, I'm fairly sure that there exists no such functionality.
I wouldn't mind, however, for someone to prove me wrong.
In my specific setting, I did solve this problem by writing a method that takes a couple of lambdas (representing the key column, and an optional column to filter by - all properties of a specific domain entity). This method then builds the sql and calls session.CreateSQLQuery(...).UniqueResult(); I'm not claiming that this is a general purpose solution.
To avoid the use of magic strings, I borrowed a copy of PropertyHelper<T> from this answer.
Here's the code:
public abstract class RepositoryBase<T> where T : DomainEntityBase
{
public long GetIndexOf<TUnique, TWhere>(T entity, Expression<Func<T, TUnique>> uniqueSelector, Expression<Func<T, TWhere>> whereSelector, TWhere whereValue) where TWhere : DomainEntityBase
{
if (entity == null || entity.Id == Guid.Empty)
{
return -1;
}
var entityType = typeof(T).Name;
var keyField = PropertyHelper<T>.GetProperty(uniqueSelector).Name;
var keyValue = uniqueSelector.Compile()(entity);
var innerWhere = string.Empty;
if (whereSelector != null)
{
// Builds a column name that adheres to our naming conventions!
var filterField = PropertyHelper<T>.GetProperty(whereSelector).Name + "Id";
if (whereValue == null)
{
innerWhere = string.Format(" where [{0}] is null", filterField);
}
else
{
innerWhere = string.Format(" where [{0}] = :filterValue", filterField);
}
}
var innerQuery = string.Format("(select [{0}], row_number() over (order by {0}) as RowNum from [{1}]{2}) X", keyField, entityType, innerWhere);
var outerQuery = string.Format("select RowNum from {0} where {1} = :keyValue", innerQuery, keyField);
var query = _session
.CreateSQLQuery(outerQuery)
.SetParameter("keyValue", keyValue);
if (whereValue != null)
{
query = query.SetParameter("filterValue", whereValue.Id);
}
var sqlRowNumber = query.UniqueResult<long>();
// The row_number() function is one-based. Our index should be zero-based.
sqlRowNumber -= 1;
return sqlRowNumber;
}
public long GetIndexOf<TUnique>(T entity, Expression<Func<T, TUnique>> uniqueSelector)
{
return GetIndexOf(entity, uniqueSelector, null, (DomainEntityBase)null);
}
public long GetIndexOf<TUnique, TWhere>(T entity, Expression<Func<T, TUnique>> uniqueSelector, Expression<Func<T, TWhere>> whereSelector) where TWhere : DomainEntityBase
{
return GetIndexOf(entity, uniqueSelector, whereSelector, whereSelector.Compile()(entity));
}
}
public abstract class DomainEntityBase
{
public virtual Guid Id { get; protected set; }
}
And you use it like so:
...
public class Book : DomainEntityBase
{
public virtual string Title { get; set; }
public virtual Category Category { get; set; }
...
}
public class Category : DomainEntityBase { ... }
public class BookRepository : RepositoryBase<Book> { ... }
...
var repository = new BookRepository();
var book = ... a persisted book ...
// Get the index of the book, sorted by title.
var index = repository.GetIndexOf(book, b => b.Title);
// Get the index of the book, sorted by title and filtered by that book's category.
var indexInCategory = repository.GetIndexOf(book, b => b.Title, b => b.Category);
As I said, this works for me. I'll definitely tweak it as I move forward. YMMV.
Now, if the OP has solved this himself, then I would love to see his solution! :-)
ICriteria has this 2 functions:
SetFirstResult()
and
SetMaxResults()
which transform your SQL statement into using ROW_NUMBER (in sql server) or limit in MySql.
So if you want 25 records on the third page you could use:
.SetFirstResult(2*25)
.SetMaxResults(25)
After trying to find an NHibernate based solution for this myself, I ultimately just added a column to the view I happened to be using:
CREATE VIEW vw_paged AS
SELECT ROW_NUMBER() OVER (ORDER BY Id) AS [Row], p.column1, p.column2
FROM paged_table p
This doesn't really help if you need complex sorting options, but it does work for simple cases.
A Criteria query, of course, would look something like this:
public static IList<Paged> GetRange(string search, int rows)
{
var match = DbSession.Current.CreateCriteria<Job>()
.Add(Restrictions.Like("Id", search + '%'))
.AddOrder(Order.Asc("Id"))
.SetMaxResults(1)
.UniqueResult<Paged>();
if (match == null)
return new List<Paged>();
if (rows == 1)
return new List<Paged> {match};
return DbSession.Current.CreateCriteria<Paged>()
.Add(Restrictions.Like("Id", search + '%'))
.Add(Restrictions.Ge("Row", match.Row))
.AddOrder(Order.Asc("Id"))
.SetMaxResults(rows)
.List<Paged>();
}