How to get all maxes from couchbase using map/reduce? - view

I've got a lot of records like:
{
"id": "1000",
"lastSeen": "2018-02-26T18:49:21.863Z"
}
{
"id": "1000",
"lastSeen": "2017-02-26T18:49:21.863Z"
}
{
"id": "2000",
"lastSeen": "2018-02-26T18:49:21.863Z"
}
{
"id": "2000",
"lastSeen": "2017-02-26T18:49:21.863Z"
}
I'd like to get the most recent records for all ids. So in this case the output would be the following(most recent record for ids 1000 and 2000):
{
"id": "1000",
"lastSeen": "2018-02-26T18:49:21.863Z"
}
{
"id": "2000",
"lastSeen": "2018-02-26T18:49:21.863Z"
}
With N1QL, this would be
SELECT id, MAX(lastSeen) FROM mybucket GROUP BY id
How would I do this using a couchbase view and map/reduce?
Thanks!

I am far from a regular user of map/reduce, and there may be more efficient JavaScript, but try this:
Map
function (doc, meta) {
emit(doc.id, doc.lastSeen);
}
Reduce
function reduce(key, values, rereduce) {
var max = values.sort().reverse()[0];
return max;
}
Filter: ?limit=6&stale=false&connection_timeout=60000&inclusive_end=true&skip=0&full_set=true&group_level=1
The idea is to sort all the values being emitted (lastSeen). Since they are ISO 8601 and can be lexigraphically sorted, sort() works just fine. You want the latest, so that's what the reverse() is for (otherwise you'd get the oldest).
The filter has a group_level of 1, so it will group by the doc.id field.

You can query by descending and reduce to first one on list as below:
Map:
function (doc, meta) {
emit(doc.id, doc.lastSeen);
}
Reduce:
function reduce(key, values, rereduce) {
return values[0];
}
Filter:
?inclusive_end=true&skip=0&full_set=&group_level=1&descending=true
This will eliminate the overhead of sorting the grouped values inside reduce function.

Related

How to create a HashMap with custom object as a key?

In Elasticsearch, I have an object that contains an array of objects. Each object in the array have type, id, updateTime, value fields.
My input parameter is an array that contains objects of the same type but different values and update times. Id like to update the objects with new value when they exist and create new ones when they aren't.
I'd like to use Painless script to update those but keep them distinct, as some of them may overlap. Issue is that I need to use both type and id to keep them unique. So far I've done it with bruteforce approach, nested for loop and comparing elements of both arrays, but I'm not too happy about that.
One of the ideas is to take array from source, build temporary HashMap for fast lookup, process input and later store all objects back into source.
Can I create HashMap with custom object (a class with type and id) as a key? If so, how to do it? I can't add class definition to the script.
Here's the mapping. All fields are 'disabled' as I use them only as intermidiate state and query using other fields.
{
"properties": {
"arrayOfObjects": {
"properties": {
"typ": {
"enabled": false
},
"id": {
"enabled": false
},
"value": {
"enabled": false
},
"updated": {
"enabled": false
}
}
}
}
}
Example doc.
{
"arrayOfObjects": [
{
"typ": "a",
"id": "1",
"updated": "2020-01-02T10:10:10Z",
"value": "yes"
},
{
"typ": "a",
"id": "2",
"updated": "2020-01-02T11:11:11Z",
"value": "no"
},
{
"typ": "b",
"id": "1",
"updated": "2020-01-02T11:11:11Z"
}
]
}
And finally part of the script in it's current form. The script does some other things, too, so I've stripped them out for brevity.
if (ctx._source.arrayOfObjects == null) {
ctx._source.arrayOfObjects = new ArrayList();
}
for (obj in params.inputObjects) {
def found = false;
for (existingObj in ctx._source.arrayOfObjects) {
if (obj.typ == existingObj.typ && obj.id == existingObj.id && isAfter(obj.updated, existingObj.updated)) {
existingObj.updated = obj.updated;
existingObj.value = obj.value;
found = true;
break;
}
}
if (!found) {
ctx._source.arrayOfObjects.add([
"typ": obj.typ,
"id": obj.id,
"value": params.inputValue,
"updated": obj.updated
]);
}
}
There's technically nothing suboptimal about your approach.
A HashMap could potentially save some time but since you're scripting, you're already bound to its innate inefficiencies... Btw here's how you initialize & work with HashMaps.
Another approach would be to rethink your data structure -- instead of arrays of objects use keyed objects or similar. Arrays of objects aren't great for frequent updates.
Finally a tip: you said that these fields are only used to store some intermediate state. If that weren't the case (or won't be in the future), I'd recommend using nested arrays to enable querying independently of other objects in the array.

How to group GraphQL query result with individual key as array

I'm using GraphQL with .NET Core. I have query like below. As I'm new in GraphQL.NET, I can't understand how to group individual key as array.
`{
readingQuery{
readingsDBFilter(buildingId: 30, objectId: 1, datafieldId: 1, startTime: "02-05-201812-00-00-AM", endTime: "30-05-201811-59-00-PM"){
value,
unix
}
}
}`
I have Output Like this
`{
"data": {
"readingQuery": {
"readingsDBFilter": [
{
"value": 0.66,
"unix": 1525254180000
},
{
"value": 0.68,
"unix": 1525254240000
}
]
}
}
}`
But, Is it possible to return result like this from query.
`{
"data": {
"readingQuery": {
"readingsDBFilter": [
{
"value":[ 0.66, 0.68],
"unix": [1525254180000, 1525254240000]
}
]
}
}
}`
Looks like you need to group values from different records
I guess you have two option here
1) try to group it on SQL level (maybe better to create dateview)
2) do it on runtime level, in code. from my point of view - it's bad. any grouping in code it's much slower then the same operation in db-level

Nested query in Strapi GraphQL

I have a document structured as follows, more or less:
post {
_id
title
isPublished
}
user {
_id
username
name
[posts]
}
I know I can query fields like postConnection and userConnection with the aggregate subfield in order to query a count of all objects. But how do I get the total count of all posts by a given user?
I was able to come up with this:
{
postsConnection(where: {isPublished: true}){
groupBy{
author{
key
connection{
aggregate{
count
}
}
}
}
}
}
But this returns (expectedly) something like this:
{
"data": {
"postsConnection": {
"groupBy": {
"author": [
{
"key": "5c9136976238de2cc029b5d3",
"connection": {
"aggregate": {
"count": 5
}
}
},
{
"key": "5c99d5d5fcf70010b75c07d5",
"connection": {
"aggregate": {
"count": 3
}
}
}
]
}
}
}
}
As you can see, it returns post counts for all authors in an array. What I need is to be able to return the count for only one specific user and not by _id (which is what the key field seems to map to) but by another unique field I have in the users collection, i.e. username.
Is that possible?
Need to pass in a parameter to either the query or the field to return specific data

Elasticsearch: range query for count of nested array matches

I'm storing room objects in an index like this:
{
"name":"room1",
"availability":"10",
"reservations": [
{
"start_date": "2019-09-12",
"end_date": "2019-09-15",
},
{
"start_date": "2019-09-17",
"end_date": "2019-09-19"
}
]}
Given a new startDate and endDate,
how can I match all rooms where room.availability is greater than the
number of reservations that overlap with these dates?
Have you tried using a range query and a script query to only return the document according to your predicate ?
elastic.co/guide/en/elasticsearch/reference/current/query-dsl-script-query.html

Just few hours with Couchdb and... when I create a view how do get the key?

I am trying to learn Couchdb and have a very very newbie question. I have following two documents
{
"type": "type1",
"code": "10",
"name": "ten",
},
{
"type": "type2",
"code": "20",
"name": "twenty",
}
I have created a view as following
function(doc) {
emit(doc.type, {"code":doc.code, "name":doc.name});
}
The above function works fine but I would like to get the key instead of writing as following example which doesn't work:
function(doc) {
emit(doc.type, {key(doc.code):doc.code, key(doc.name):doc.name});
}
How do I do that???
Simple solution
I'm not sure this is what you're after but you can do this:
function(doc) {
emit(doc.type, doc);
}
Then all the fields (including type but also _id, _rev…) are available without having to type them explicitly.
Full solution
key(doc.code):doc.code does not look better than "code":doc.code to me, but if you really want to avoid duplication, you can do:
function(doc) {
var elem = {}, keys = ["code", "name"];
for (var i in keys) {
elem[keys[i]] = doc[keys[i]];
}
emit(doc.type, elem);
}
It seems overkill unless you have a long list of keys.

Resources