How do I join and project a multi-map index? - ravendb

I'm struggling to get my head around this one and I know the way to do this is through a custom index. Essentially, I have several collections that share some common properties, one of which is "system id" which describes a many-to-one relationship, e.g.
// Id() = "component:a"
{
"Name": "Component A"
"SystemId": "system:foo"
}
// Id() = "resource:a"
{
"Name": "Resource A",
"SystemId": "system:foo"
}
So these are two example objects which live in two different collections, Components and Resources, respectively.
I have another collection called "Notifications" and they have a RecipientID which refers to the Id of one of the entities described above. e.g.
// Id() = "Notifications/84-A"
{
"RecipientID": "component:a",
"Message": "hello component"
}
// Id() = "Notifications/85-A"
{
"RecipientID": "resource:a",
"Message": "hello resource"
}
The query that I want to be able to perform is pretty straight forward -- "Give me all notifications that are addressed to entities which have a system of ''" but I also want to make sure I have some other bits from the entities themselves such as their name, so a result object something like this:
{
"System": "system:foo",
"Notifications": [{
"Entity": "component:a",
"EntityName": "Component A",
"Notifications": [{
"Id": "Notifications/84-A",
"Message": "hello component"
}]
}, {
"Entity": "resource:a",
"EntityName": "Resource A",
"Notifications": [{
"Id": "Notifications/85-A",
"Message": "hello resource"
}]
}]
}
Where I am with it right now is that I'm creating a AbstractMultiMapIndexCreationTask which has maps for all of these different entities and then I'm trying to use the reduce function to mimic the relationship.
Where I'm struggling is with how these map reduces work. They require that the shape is identical between maps as well as the reduce output. I've tried some hacky things like including all of the notifications as part of the map and putting dummy values where the properties don't match, but besides it making it really hard to read/understand, I still couldn't figure out the reduce function to make it work properly.
How can I achieve this? I've been digging for examples, but all of the examples I've come across make some assumptions about how references are arranged. For example, I don't think I can use Include because of the different collections (I don't know how Raven would know what to query), and coming from the other direction, the entities don't have enough info to include or load notifications (I haven't found any 'load by query' function). The map portion of the index seems fine, I can say 'give me all the entities that share this system, but the next part of grabbing the notifications and creating that response above has eluded me. I can't really do this in multiple steps either, because I also need to be able to page. I've gone in circles a lot and I could use some help.

How about indexing the related docs ?
Something like this (a javascript index):
map("Notifications", (n) => {
let c = load(n.RecipientID, 'Components');
let r = load(n.RecipientID, 'Resources');
return {
Message: n.Message,
System: c.SystemId || r.SystemId,
Name: c.Name || r.Name,
RelatedDocId: id(c) || id(r)
};
})
Then you can query on this index, filter by the system value, and get all matching notifications docs.
i.e. sample query:
from index 'yourIndexName'
where System == "system:foo"
Info about related documents is here:
RavenDB Demo
https://demo.ravendb.net/demos/csharp/related-documents/index-related-documents
Documentation
https://ravendb.net/docs/article-page/5.4/csharp/indexes/indexing-related-documents

Related

HubSpot API — Automatically bulk delete tasks/contacts/deals (or anything) using Make

I would like to automatically bulk delete all tasks older than a month in HubSpot (we have more than 10,000 tasks!), instead of doing it one by one. I tried looking on the internet but it doesn’t seem that HubSpot has any functionalities like it. Thus, I tried to implement such scenario using Make (formerly Integromat) unsuccessfully.
Answering to my question for knowledge purposes.
I managed to create a scenario allowing me to automatically bulk delete tasks (or anything) based on a certain set of criteria using Make (formerly Integromat). I had to use HubSpot’s API and Flow Control tools to achieve such result.
The scenario looks like the following:
Module 1: API Call
Search for all tasks based on a certain set of criteria (here, all tasks created before the last 30 days).
If you wish to search for another object (such as contacts or deals), you can take a look at the CRM Search API for all available search requests. You can also browse through the Properties API to get a comprehensive list of available properties.
URL: /crm/v3/objects/tasks/search
Method: POST
Body:
{
"limit": "5",
"properties": [
"hs_task_subject",
"hs_task_type",
"hs_timestamp"
],
"filterGroups": [
{
"filters": [
{
"propertyName": "hs_task_status",
"operator": "EQ",
"value": "NOT_STARTED"
},
{
"propertyName": "hs_createdate",
"operator": "LT",
"value": "{{formatDate(addDays(now; -30); "x")}}"
}
]
}
]
}
Module 2: Repeater
Initial Value: 0
Repeats: {{if(module1.body.total = null; 1; module1.body.total / 100)}} (if total is less than 100, do not repeat)
Step: {{ifempty(module1.body.paging.next.after; 100)}} (automatically sets it to the first module’s after value, otherwise to 100 if after` value is empty)
You can find out more about properties and search limitations here and here. Basically, the repeater allows you to loop over all HubSpot pages.
Module 3: Sleep
Sleep module to prevent RateLimitError
Module 4: API Call
Same as Module 1, except that you must add an after parameter to include the repeater’s value.
+ "after": "{{module2.i}}"
Module 5: Iterator
Iterate over Module 4’s results array: {{module4.body.results}}.
Module 6: API Call
Delete tasks using the ID returned by the iterator.
{
"inputs":[
{
"id":"{{module5.id}}"
}
]
}
Voilà !

Not getting results field after querying task by ID in Lookback API

I'm trying to query tasks by ObjectID to get their most recent snapshots. I'm trying the API out, and am not getting the expected results that I was hoping for after reading the docs. I don't get a results field in the response object. Here's my code:
_loadTaskSnapshot: function() {
let snapshot = Ext.create('Rally.data.lookback.SnapshotStore', {
context: {
workspace: '/workspace/2290039850'
},
"find": {
"ObjectID": 34858774310,
"_ValidFrom": {
"$gte": "2016",
"$lt": "2017"
}
},
"fields": ["Name", "Estimate", "ToDo", "TimeSpent"],
});
return snapshot.load();
}
This is the store with 18 snapshots for the specified task. The first snapshot is opened. You can see there is no results field where I could find the Name, Estimate, ToDo, and TimeSpent:
Alejandro, you are asking for the changes in the fields, not the values of the fields. This is a common misconception with the lookback api. There is a special way to get the current values shown in the help pages available inside Agile Central.
Any information returned is actually held within the object underneath 'raw' and 'data'. Each of those may not contain any values if there has been no 'changes' to those fields at the time the snapshot was taken.

REST API: fields of objects in a list of objects in response JSON

Suppose we are building one-page app with two views: list view and detail view.
In list view, we present a list of objects with just their names and maybe some more minimal data.
In detail view, we present all possible fields of particular object.
Hence the question: when we GET /api/items/, should we or should not to JSON-encode all fields of the objects listed, or just those presented in list view?
In other words, if we show list of food like
Name Price
Potato 1
Milk 2
does our API need to respond with JSON like this:
{
[
{
"name": "Potato",
"quantity": "1 kg",
"origin": "Egypt",
"manufacturer": "Egypt Farmers",
"price": 1,
"packaging": "String bag",
"_type": "Food"
},
{
"name": "Milk",
"quantity": "1 litre",
"origin": "Finland",
"manufacturer": "Valio",
"price": 2,
"packaging": "Tetra Pak",
"_type": "Food"
},
]
}
or like this:
{
[
{
"name": "Potato",
"price": 1,
"_type": "Food"
},
{
"name": "Milk",
"price": 2,
"_type": "Food"
},
]
}
The RESTful API should concentrate on the resources that are represented, not necessarily how those resources are used.
In a master/detail scenario, typically the master will contain details of the master object, and include a list of its details (including a link to the API for each detail resource. So /api/items/ might look like this:
{
items: [
{ name: 'item 1', href: '/api/items/1' },
{ name: 'item 2', href: '/api/items/2' }
]
}
The detail resource would contain properties of an individual item in the items list. So the /api/items/{itemName} api might look like this:
{
name: 'item 1',
color: 'blue',
weight: 100,
id: '/api/items/1'
}
So this would probably be closest to your second scenario. There are a number of advantages to this model: it probably matches the domain model that your api is accessing, it makes each api very simple and single-purpose, it's easy to scale, even to very large lists. The disadvantage is that it may lead to more complexity on the client.
The answer as usual may be: it all depends ;)
In case of the connection is limited or unstable (e.g. mobile connection like LTE or even wifi) the best idea is to return the whole list of resources with all fields filled and use the same data on both views. In the company I work for we often take this approach since our backend almost always provide data for mobile applications.
The second idea is to use a mechanism called field or resource expansion. In general a request is made to the endpoint and fields of resources to be returned are included in this request:
/api/items?fields=(name, quantity, origin, whatever)
This mechanism is very convenient since you can use this endpoint to server multiple views without any performance loss.
Personally I'd use two endpoints. An /api/items/ endpoint with field/resource expansion mechanism built-in (with a limited list of fields that can be expanded) and the second one /api/items/{itemID}/ to return a particular item with all the data. This is also the most RESTful approach.

Displaying Only Certain Columns/Properties with Keen DataViz

I'm wondering how to display only certain columns on my table when I'm trying to do data visualization. Right now, every time that I do a table, it shows every single property or column. I tried to figure out how to only show certain columns properties by guessing based on the documentation for extraction within the keen.io docs (I've commented out my wrong guess)
var foo = new Keen.Query("extraction", {
eventCollection: "Purchases",
targetProperty: "zip",
// I've commented out line 7 because it doesn't work, it was just my guess on syntax...
// how do I only show certain columns of the table (columns are properties)
// vs. showing the entire table?
// propertyNames: ["zip","businessname"]
filters: [
{
"property_name" : "zip",
"operator" : "eq",
"property_value" : "80016"
},
{
"property_name" : "city",
"operator" : "eq",
"property_value" : "Aurora"
}]
});
client.draw(foo, document.getElementById("chart-09"), {
chartType: "table",
title: "Table"
});
https://gist.github.com/mrtannerjones/ba0cbd7340f2e016fcf2 - here is a link to the above code except on github in case something isn't formatted correctly.
Sorry if this question seems terribly basic or simple, I'm still really new at learning how to code.
Found the fix with a little help from David in the Keen.io Google group. I actually was just missing a comma after the 'property names', but what's more interesting to me is that the data visualization works with both camel case and with underscore separators. property_names and propertyNames both seem to work.

Does the JIRA REST API require submitting a transition ID when transitioning an issue?

If I POST an issue transition like this:
{
"fields" : {
"resolution" : {
"name" : "Fixed"
}
}
}
...I get this error:
{
"errorMessages" : ["Missing 'transition' identifier"],
"errors" : {}
}
This seems to imply that I need to include a transition ID along with my list of changed fields. https://stackoverflow.com/a/14642966/565869 seems to say the same. Fine.
However, transition IDs appear to be global. It's not enough to look up the highest transition ID for this issue and increment it; such an ID is probably in use elsewhere. At some expense, I could get the highest transaction ID used anywhere in the system; this might be 68,000 at this moment. But if I were then to use transaction ID 68,001 there's a real chance that a GUI user would attempt a transition of their own and use this ID before I could.
I could use transaction IDs in the range of 1,000,001 and up, but if the JIRA web GUI uses the highest previously used transaction ID when generating new IDs I'll just collide in this range instead of the 68,000 range. I could use 69,000 and trust that there won't be a thousand transitions in the length of time it takes to get the highest transaction ID.
These both seem terribly clumsy, however. Is there no way to post a transition and let JIRA generate its own unique ID? I don't need to retrieve the generated IDs, I just want to update issues' statuses and resolutions.
You're getting mixed up a bit. So lets see if I can explain it better for you.
To transition a JIRA Issue, you use the Transition ID to identify what transition to apply to the issue. You aren't specifying an ID for a transaction or a transition ID to identify that the transition occurred, JIRA takes care of this for you.
The easiest way to understand it is to see it.
So first you can look at what transitions are available to an Issue by doing a GET to the API Call:
/rest/api/2/issue/${issueIdOrKey}/transitions
Example:
/rest/api/2/issue/ABC-123/transitions
Which will show something like this:
{
"expand": "transitions",
"transitions": [
{
"id": "161",
"name": "Resolve",
"to": {
"description": "A resolution has been taken, and it is awaiting verification by reporter. From here issues are either reopened, or are closed.",
"iconUrl": "https://localhost:8080/images/icons/statuses/resolved.png",
"id": "5",
"name": "Resolved",
"self": "https://localhost:8080/rest/api/2/status/5"
}
}
]
}
So you can see only 1 transition is available for issue ABC-123 and it has an ID of 161.
If you were to browse to that JIRA Issue through the GUI, you would see only 1 Transition available and it would match the API Call. In fact if you inspected the element you should see it having an a tag and in the href something like action=161
So should you want to transition this issue, you'll need to do a POST to the following URL:
/rest/api/2/issue/ABC-123/transitions
With JSON like this:
{
"update": {
"comment": [
{
"add": {
"body": "Bug has been fixed."
}
}
]
},
"fields": {
"assignee": {
"name": "bob"
},
"resolution": {
"name": "Fixed"
}
},
"transition": {
"id": "161"
}
}
Which uses the transition ID found from the call that shows all transitions. I also update the resolution and assignee and add comments at the same time.
That make a bit more sense?