Couchdb view filtering by date - view

I have a simple document named Order structure with the fields id, name,
userId and timeScheduled.
What I would like to do is create a view where I can find the
document.id for those who's userId is some value and timeScheduledis
after a given date.
My view:
"by_users_after_time": {
"map": "function(doc) { if (doc.userId && doc.timeScheduled) {
emit([doc.timeScheduled, doc.userId], doc._id); }}"
}
If I do
localhost:5984/orders/_design/Order/_view/by_users_after_time?startKey="[2012-01-01T11:40:52.280Z,f98ba9a518650a6c15c566fc6f00c157]"
I get every result back. Is there a way to access key[1] to do an if
doc.userId == key[1] or something along those lines and simply emit on the
time?
This would be the SQL equivalent of
select id from Order where userId =
"f98ba9a518650a6c15c566fc6f00c157" and timeScheduled >
2012-01-01T11:40:52.280Z;
I did quite a few Google searches but I can't seem to find a good tutorial
on working with multiple keys. It's also possible that my approach is
entirely flawed so any guidance would be appreciated.

You only need to reverse the key, because username is known:
function (doc) {
if (doc.userId && doc.timeScheduled) {
emit([doc.userId, doc.timeScheduled], 1);
}
}
Then query with:
?startkey=["f98ba9a518650a6c15c566fc6f00c157","2012-01-01T11:40:52.280Z"]
NOTES:
the query parameter is startkey, not startKey;
the value of startkey is an array, not a string. Then the double quotes go around the username and date values, not around the array.
I emit 1 as value, instead of doc._id, to save disk-space. Every row of the result has an id field with the doc._id, then there's no need to repeat it.

don't forget to set an endkey=["f98ba9a518650a6c15c566fc6f00c157",{}], otherwise you get the data of all users > "f98ba9a518650a6c15c566fc6f00c157"

The answer actually came from the couchdb mailing list:
Essentially, the Date.parse() doesn't like the +0000 on the timestamps. By
doing a substring and removing the +0000, everything worked.
For the record,
document.write(new Date("2012-02-13T16:18:19.565+0000")); //Outputs Invalid
Date
document.write(Date.parse("2012-02-13T16:18:19.565+0000")); //Outputs NaN
But if you remove the +0000, both lines of code work perfectly.

Related

JSONata group by multiple fields and count

Does anyone know how to user jsonata to group by multiple fields and count ?
https://docs.jsonata.org/
I can't figure it out, I have been using the jsonata online playground but I can't seem to get anywhere.
I have an array of objects and I want to group by multiple fields and count
I have been doing this earlier in mysql and it is very simple but now when the "records" are json objects I am bit lost and need help
The result should be a new array with the grouped fields + count of the grouped fields
Thanks,
Jani
It is a bit hacky solution, but I think it does what you want:
$${
API_TYPE & API_VERSION & METHOD_NAME: {
"API_TYPE": [API_TYPE][0],
"API_VERSION": [API_VERSION][0],
"METHOD_NAME": [METHOD_NAME][0],
"COUNT": $count($)
}
} ~> $each(function($value) {$value})
The trick is to use the grouping feature, and group by all three parameters by concatenating them to a single string.
See it in action: https://stedi.link/L1a9Tiv

Proper Upsert (Atomic Update Counter Field or Insert Document) with RethinkDB

After looking at some SO questions and issues on RethinkDB github, I failed to come to a clear conclusion if atomic Upsert is possible?
Essentially I would like to perform the same operation as ZINCRBY using Redis.
If member does not exist in the sorted set, it is added with increment
as its score (as if its previous score was 0.0). If key does not
exist, a new sorted set with the specified member as its sole member
is created.
The current implementation appears to differ from almost all databases that I have used. With the data being replaced or inserted not updated. This is a simple use case, like update the last visit, update the number of clicks, update a product quantity. So I must be missing something very obvious, because I cannot see a simple way to do this.
Yes, it is possible. After get on the key, perform an atomic replace. Something like this might work:
function set_or_increment_score(player, points){
return r.table('scores').get(player).replace(
row =>
{ id: player,
score: r.branch(
row.eq(null),
points,
row('score').add(points))
});
}
It has the following behaviour:
> set_or_increment_score("alice", 1).run(conn)
{ inserted: 1 }
> set_or_increment_score("alice", 2).run(conn)
{ replaced: 1 }
It works because get returns null when the document doesn't exist, and a replace on a non-existing document tuns into an insert. See the documentation for replace
So I end up using the following code to go around the no Update issue.
r.db("test").table("t").insert(
{id:"A", type:"player", species:"warrior", score:0, xp:0, armor:0},
{conflict: function(id, oldDoc, newDoc) {
return newDoc.merge(oldDoc).merge(
{armor: oldDoc("armor").add(1)});
}
}
)
Do you think this is more readable/elegant or do you see any issues with the code compared to your sample?

storing business hours in Parse DB

Need some help with the infrastructure with storing business hours for a location on Parse.com, i already tried it as a separate Class called BusinessHours, where each row has a pointer to the Location class. Having a minimum of 7 rows for each day of the week for 1 location, the objects count comes to +10.000
than in swift i do this to determine if the location is open now
for hour in hours {
if hour.isClosedAllDay {
isOpen = "closed".localized
}else{
let now = NSDate()
if now.hasDayOffset(hour.weekday, closeWeekDay: hour.nextWeekday) {
if hour.open != nil && hour.close != nil {
let open = now.hourDateFromString(hour.open!, offset: now.dayOpenOffset(hour.weekday, closeWeekDay: hour.nextWeekday))
let close = now.hourDateFromString(hour.close!, offset: now.dayCloseOffset(hour.weekday, closeWeekDay: hour.nextWeekday))
if now.isBetween(open, close: close) {
isOpen = "open".localized
timeOfBusiness = hour.time!
break
}
}
}
}
}
Is there a better way to do this than to have thousands of rows for Business Hours only? I was thinking of adding a object field to the Location Class for the hours but don't know if that is the right way to go either.
Depending on how you want to edit and change the details, and the complexities of multiple opening times per day, I'd consider not using multiple columns and rows. Instead, you could simply store a JSON string in a single column which contains all of the required details.
Obviously you wouldn't be able to use this for querying, so if you need to do that then you need to keep something more like your current solution.
If you don't need querying, or you need simple querying like 'is it open at all on a Monday' then a combined solution, supported by cloud code so the app doesn't need lots of knowledge of the JSON, could work well. For instance you could have columns for general open hours each day and then details in JSON, so you can get a rough answer by querying and then check the exact detail before presentation / usage of the result.
I ended up doing it like this in an array field called businessHours in my Location class:
[
{"close":"20:00Z","open":"12:00Z","time":"09:00 - 17:00","isClosedAllDay":false,"nextWeekday":1,"weekday":1},
{"close":"20:00Z","open":"12:00Z","time":"09:00 - 17:00","isClosedAllDay":false,"nextWeekday":2,"weekday":2},
{"close":"20:00Z","open":"12:00Z","time":"09:00 - 17:00","isClosedAllDay":false,"nextWeekday":3,"weekday":3},
{"close":"20:00Z","open":"12:00Z","time":"09:00 - 17:00","isClosedAllDay":false,"nextWeekday":4,"weekday":4},
{"close":"20:00Z","open":"12:00Z","time":"09:00 - 17:00","isClosedAllDay":false,"nextWeekday":5,"weekday":5},
{"close":"20:00Z","open":"12:00Z","time":"09:00 - 17:00","isClosedAllDay":false,"nextWeekday":6,"weekday":6},
{"close":"20:00Z","open":"12:00Z","time":"09:00 - 17:00","isClosedAllDay":false,"nextWeekday":7,"weekday":7}
]
and then looping through the objects as a NSDictionary.
thanks Wain!

Substring with spacebar search in RavenDB

I'm using such a query:
var query = "*" + QueryParser.Escape(input) + "*";
session.Query<User, UsersByEmailAndName>().Where(x => x.Email.In(query) || x.DisplayName.In(query));
With the support of a simple index:
public UsersByEmailAndName()
{
Map = users => from user in users
select new
{
user.Email,
user.DisplayName,
};
}
Here I've read that:
"By default, RavenDB uses a custom analyzer called
LowerCaseKeywordAnalyzer for all content. (...) The default values for
each field are FieldStorage.No in Stores and FieldIndexing.Default in
Indexes."
The index contains fields:
DisplayName - "jarek waliszko" and Email - "my_email#domain.com"
And finally the thing is:
If the query is something like *_email#* or *ali* the result is fine. But while I use spacebar inside e.g. *ek wa*, nothing is returned. Why and how to fix it ?
Btw: I'm using RavenDB - Build #960
Change the Index option for the fields you want to search on to be Analyzed, instead of Default
Also, take a look here:
http://ayende.com/blog/152833/orders-search-in-ravendb
Lucene’s query parser interprets the space in the search term as a break in the actual query, and doesn’t include it in the search.
Any part of the search term that appears after the space is also disregarded.
So you should escape space character by prepending the backslash character before whitespace character.
Try to query *jarek\ waliszko*.
So.., I've came up with an idea how to do it. I don't know if this is the "right way" but it works for me.
query changes to:
var query = string.Format("*{0}*", Regex.Replace(QueryParser.Escape(input), #"\s+", "-"));
index changes to:
public UsersByEmailAndName()
{
Map = users => from user in users
select new
{
user.Email,
DisplayName = user.DisplayName.Replace(" ", "-"),
};
}
I've just changed whitespaces into dashes for the user input text and spacebars to dashes in the indexed display name. The query gives expected results right now. Nothing else really changed, I'm still using LowerCaseKeywordAnalyzer as before.

Finding Last Name Using LINQ

I am trying to get last name using linq in visual studio. In my database, i have the Field name like "FullName".
In this Field I have value like "Subbu Cargos"
I Wanna Display the "Cargos" in my textbox.
How can i make a simple linq query?
Would it be over simple to say:
return FullName.Split(' ').Last()
?
I would suggest breaking it up into different fields - Firstname, Middlename, lastname, Title - and rebuilding the name on the fly when you come to display it.
If you're still determined to use one field, then consider a query like:
string s = "Subba Cargos";
var lastnames = from name in s.Split(new Char[] { ' ' }).Last<string>()
select name;
I would suggest not trying to parse out the last name. Like you say, first and last names could be switched around, someone might have a second name, or a last name that consists of multiple words (“van Dijk”), or may not have entered a last name at all.
Check out this article: Falsehoods Programmers Believe About Names
If you still want to do this however, try something like this:
customers.Select(c => c.FullName.Split(' ').Last());
You might not be able to this on the server side. In that case:
customers
.Select(c => c.FullName)
.ToList()
.Select(n => n.Split(' ').Last());
Untested, but this should give a rough idea.
You could also do it like this:
customers
.Select (b => b.FullName.Substring ((b.FullName.IndexOf(' ') + 1)));

Resources