is CouchDB a good fit? - database

I'm working on a to do list with a simple collaboration feature.
Basically a user says that he wants to share a group of todo items.
The structure is that a item belongs to a group and that group is available to a set of users.
Now to get the list of todo items for a specific user, I need to figure out all items in groups the user has access too.
So basically the structure looks somewhat like this:
user - group - item
What i want is given a user id I want the items for all the groups the user has access to.
From my early experiments it seems to be somewhat problematic to produce a view with the desired output.
Is CouchDB a good fit, or should I be looking for another database to work with?

If I understand it correctly, each group has a list of users and a list of items. Then, for each group, you can emit a row for each (user, item) pair:
emit(userId, { _id: itemId });
Querying the view with key="userId", should give the correct result.
For example, if a group document has the following structure:
{
"_id": "Group_gid1",
"userIds": ["User_uid1", ..., "User_uidN"],
"itemIds": ["Item_iid1", ..., "Item_iidM"]
}
Your map function can be something like (not tested!!!):
function (doc) {
var i, ii, userId, j, jj, itemId, ids = doc._id.split('_');
if (ids[0] === 'Group' && doc.userIds && doc.itemIds) {
for(i = 0, ii = doc.userIds.length, jj = doc.itemIds.length; i < ii; i += 1) {
userId = doc.userIds[i];
for(j = 0; j < jj; j += 1) {
itemId = doc.itemIds[j];
emit(userId, { _id: itemId });
}
}
}
}
If you query it with key="User_1"&include_docs=true it should return all items in the groups of the user with _id=="User_1".

Related

How to create a nested array of objects from flat array of objects?

I am still learning and need some guidance on how to form a complex nested array of objects from a JSON flat file.
Here is the current input:
[
{"id":"US-AL","state":"Alabama","industry":"All","category":"Cable Related Services","itemid":"12290","item":"Basic Cable Services","answer":"Exempt","explanation":"The sale of these services is not subject to sales tax.","citation":"Ala. Code sec. 40-23-1; Ala. Code sec. 40-23-2"},
{"id":"US-AL","state":"Alabama","industry":"All","category":"Cable Related Services","itemid":"12291","item":"Pay Per View","answer":"Exempt","explanation":"The sale of these services is not subject to sales tax.","citation":"Ala. Code sec. 40-23-1; Ala. Code sec. 40-23-2"},
{"id":"US-AL","state":"Alabama","industry":"Retail","category":"Sales Tax Holidays","itemid":"12524","item":"All Disaster Preparedness Supply","answer":"Exempt","explanation":"Alabama provides for an annual state sales tax holiday for severe weather preparedness items. Counties and municipalities are allowed to provide an exemption from local sales and use taxes from the same items during the same weekend as the state holiday.","citation":"Ala. Admin. Code r. 810-6-3-.66."},
{"id":"US-AL","state":"Alabama","industry":"Retail","category":"Sales Tax Holidays","itemid":"12525","item":"All Energy star qualified products","answer":"N/A","explanation":"Alabama does not provide a sales tax holiday for energy efficient products.","citation":"N/A"}
]
Here is the format I want it in:
[
{
"id":"US-AL",
"state":"Alabama",
"industries" [
{
"industry":"All",
"categories" [
{
"category":"Cable Related Services",
items [
{"itemid":"12290","item":"Basic Cable Services","answer":"Exempt","explanation":"The sale of these services is not subject to sales tax.","citation":"Ala. Code sec. 40-23-1; Ala. Code sec. 40-23-2"},
{"itemid":"12291","item":"Pay Per View","answer":"Exempt","explanation":"The sale of these services is not subject to sales tax.","citation":"Ala. Code sec. 40-23-1; Ala. Code sec. 40-23-2"}
],
"category":"Sales Tax Holidays",
items [
{"itemid":"12524","item":"All Disaster Preparedness Supply","answer":"Exempt","explanation":"Alabama provides for an annual state sales tax holiday for severe weather preparedness items. Counties and municipalities are allowed to provide an exemption from local sales and use taxes from the same items during the same weekend as the state holiday.","citation":"Ala. Admin. Code r. 810-6-3-.66."}
]
}
],
"industry":"Sales" ...
"id":"US-AR",
"state":"Arizona" ...
]
I've tried using .map, .reduce, .filter ...
Using this example, I was able to get one level formatted, but not sure if this is the right method or if there is an easier way to accomplish this.
var grouped = utm.reduce((r, o) => {
r[o.industry] = r[o.industry] || [];
r[o.industry].push(o);
return r;
}, {});
var rs = Object.keys(grouped).map(industry => ({ industry, categories: grouped[industry] }));
I found a great answer to this from this SO post: Convert flat array of objects into nested array of objects
I tried this and it does exactly what I was trying to do.
// Groups a flat array into a tree.
// "data" is the flat array.
// "keys" is an array of properties to group on.
function groupBy(data, keys) {
if (keys.length == 0) return data;
// The current key to perform the grouping on:
var key = keys[0];
// Loop through the data and construct buckets for
// all of the unique keys:
var groups = {};
for (var i = 0; i < data.length; i++)
{
var row = data[i];
var groupValue = row[key];
if (groups[groupValue] == undefined)
{
groups[groupValue] = new Array();
}
groups[groupValue].push(row);
}
// Remove the first element from the groups array:
keys.reverse();
keys.pop()
keys.reverse();
// If there are no more keys left, we're done:
if (keys.length == 0) return groups;
// Otherwise, handle further groupings:
for (var group in groups)
{
groups[group] = groupBy(groups[group], keys.slice());
}
return groups;
}
Then I specify what fields I want to be grouped here:
var UtmDataByState = groupBy(utm, ["id","industry","category"]);
Worked great! Thanks Steve :)

How do I make a mutableArrayValue that I can access through a relationship?

I've added a to-many relationship called listItems between my entities Person and ListItem in my data model, and then successfully added objects to a given Person using
let personSelectedListItems = person.mutableSetValue(forKey: "listItems")
personSelectedListItems.addObjects(from: selectedListItems)
print("personSelectedListItems after addObjects contains \(personSelectedListItems)")
Where selectedListItems is an array of objects of type ListItem. The print statement gives:
personSelectedListItems after addObjects contains Relationship 'listItems' on managed object (0x6080000d2050) <PersonMO: 0x6080000d2050> (entity: Person; id: 0xd000000000040002 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/Person/p1> ; data: {
age = Ky;
firstName = Uykyu;
image = <89504e47 0d0a1a0a 0000000d 49484452 000002cc 00000333 08020000 003d00d3 35000000 01735247 4200aece 1ce90000 001c>;
isVisited = 0;
lastName = Kuyuy;
listItems = (
"0xd000000005140000 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/ListItem/p325>",
"0xd000000005680000 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/ListItem/p346>",
"0xd0000000023c0000 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/ListItem/p143>"
);
locationCity = Uyk;
locationState = You;
notes = "";
phoneNumber = "";
score = nil;
}) with objects {(
<ListItemMO: 0x6000000b6c20> (entity: ListItem; id: 0xd000000005140000 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/ListItem/p325> ; data: {
category = Meta;
isSelected = 1;
listItem = "Similar values";
listItemStatus = 1;
listItemWeight = 1;
people = (
"0xd000000000040002 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/Person/p1>"
);
}),
<ListItemMO: 0x6000000af900> (entity: ListItem; id: 0xd000000000080000 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/ListItem/p2> ; data: {
category = Appearance;
isSelected = 1;
listItem = Attractive;
listItemStatus = 3;
listItemWeight = 1;
people = (
"0xd000000000040002 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/Person/p1>"
);
}),
<ListItemMO: 0x6000000b27e0> (entity: ListItem; id: 0xd0000000023c0000 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/ListItem/p143> ; data: {
category = Behavior;
isSelected = 1;
listItem = "Good grammar";
listItemStatus = 1;
listItemWeight = 1;
people = (
"0xd000000000040002 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/Person/p1>"
);
})
)}
This is good, because I want to have these objects created for each Person, but it's bad because it's an unordered set that I'm seemingly not able to access. I'd like for it to be a mutable array so I can access each object at an index, but when I do
let personSelectedListItems = person.mutableArrayValue(forKey: "listItems")
personSelectedListItems.addObjects(from: selectedListItems)
print("personSelectedListItems after addObjects contains \(personSelectedListItems)")
I get this error message:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'NSManagedObjects of entity 'Person' do not support -mutableArrayValueForKey: for the property 'listItems''
Which is puzzling, since I don't know why mutableSetValueForKey: was was supported but the array version wasn't. I'd really like to be able to access the objects through the relationship, ideally through dot notation. What is the best way to go about this?
Core Data to-many relationships are always represented by sets. Part of the support for this is that NSManagedObject implements mutableSetValue(forKey:), which is one of the documented ways to access the relationship. Since to-many relationships are sets, NSManagedObject does not support mutableArrayValue(forKey:), as that error message indicates.
If you want an ordered relationship, mark it as ordered in the data model editor, and then you can use NSOrderedSet. It's still not an array but it does have ordering.
Both #Wain and #TomHarrington pointed me in the right direction on this, but here's what the answer to my question was (after some searching and tinkering):
First and foremost, I needed to mark the to-many relationship as 'ordered' in the Data Model Inspector of my data model file. The second thing was to change
let personSelectedListItems = person.mutableSetValue(forKey: "listItems")
personSelectedListItems.addObjects(from: selectedListItems)
print("personSelectedListItems after addObjects contains \(personSelectedListItems)")
to
let personSelectedListItems = person.mutableOrderedSetValue(forKey: "listItems")
personSelectedListItems.addObjects(from: selectedListItems)
print("personSelectedListItems after addObjects contains \(personSelectedListItems)")
so that mutableOrderedSetValue would reflect the ordered nature of the relationship. After that, I had a returned set that was in order. Hurray! But now how do I access and change those individual objects through the relationship? That brings me to the third thing - I had to downcast the set to my class (called ListItemMO). To grab the first object in the ordered set and then change a property through the relationship, it looks like this:
if let listItemSet = personVar.listItems?.object(at: 0) as? ListItemMO {
listItemSet.listItemStatus = 0
}

Split list of countries in alphabetical order using Angularjs

I want to split a list of countries in alphabetical order using Angularjs.
Somewhat like A : America , Australia B: Brazil, Bhutan ...
The list is coming from a table named countries. I tried to apply angularjs filter on the first alphabet of the country name but failed. Do I have to create a custom filter for it?
Ok I am going to answer my own question finally got it working.
I was pulling a list of countries from a postgres db in the following format:
{"alpha2":"ao","alpha3":"ago","numeric":"024","country":"Angola"}
I wanted to split the list of countries in alphabetical order like this :
[Example] http://infoplease.com/countries.html#ALPHA-A
I finally got it working by writing a custom filter:
angular.module('countryFilter', []).filter('cfilter', function() {
return function(input,x) {
var groups = [];
for(var i = 0; i < input.length; i++) {
if (input[i].country.substring(0,1) == x)
groups.push(input[i]);
} return groups; }
});
and using it as :
ng-repeat="data in countries | cfilter:'A'
{{data.country}}
Do let me know if there is any better way for doing this...
This is another possible solution without the use of a filter
http://www.bennadel.com/blog/2456-Grouping-Nested-ngRepeat-Lists-In-AngularJS.htm
He creates a new array with a label and the data. The advantage is that in the template you don't have to name all the possible letters.
For my own I take the first letter of the client from the object and store that in the label. In that way I only get the letters I used.
In customer I have stored an array with objects.
for(var i = 0; i < customers.length; i++){
var customer = customers[i];
if(customer[ 'title' ].substring(0,1) !== groupValue){
var group = {
label : customer[ 'title' ].substring(0,1),
customers : []
};
groupValue = customer.title.substring(0,1);
groups.push( group );
};
group.customers.push( customer );
};
Sort Item List is a jQuery plugin that creates iOS-style sticky headers similar to those seen in the Music and Contacts apps on Apple devices
check link :
http://www.codingprogrammer.com/tutorialdemo/jquery-tutorial/sort-items-alphabetically-jquery-2/

Audit of what records a given user can see in SalesForce.com

I am trying to determine a way to audit which records a given user can see by;
Object Type
Record Type
Count of records
Ideally would also be able to see which fields for each object/record type the user can see.
We will need to repeat this often and for different users and in different orgs, so would like to avoid manually determining this.
My first thought was to create an app using the partner WSDL, but would like to ask if there are any easier approaches or perhaps existing solutions.
Thanks all
I think that you can follow the documentation to solve it, using a query similar to this one:
SELECT RecordId
FROM UserRecordAccess
WHERE UserId = [single ID]
AND RecordId = [single ID] //or Record IN [list of IDs]
AND HasReadAccess = true
The following query returns the records for which a queried user has
read access to.
In addition, you should add limit 1 and get from record metadata the object type,record type, and so on.
I ended up using the below (C# using the Partner WSDL) to get an idea of what kinds of objects the user had visibility into.
Just a quick'n'dirty utility for my own use (read - not prod code);
var service = new SforceService();
var result = service.login("UserName", "Password");
service.Url = result.serverUrl;
service.SessionHeaderValue = new SessionHeader { sessionId = result.sessionId };
var queryResult = service.describeGlobal();
int total = queryResult.sobjects.Count();
int batcheSize = 100;
var batches = Math.Ceiling(total / (double)batcheSize);
using (var output = new StreamWriter(#"C:\test\sfdcAccess.txt", false))
{
for (int batch = 0; batch < batches; batch++)
{
var toQuery =
queryResult.sobjects.Skip(batch * batcheSize).Take(batcheSize).Select(x => x.name).ToArray();
var batchResult = service.describeSObjects(toQuery);
foreach (var x in batchResult)
{
if (!x.queryable)
{
Console.WriteLine("{0} is not queryable", x.name);
continue;
}
var test = service.query(string.Format("SELECT Id FROM {0} limit 100", x.name));
if(test == null || test.records == null)
{
Console.WriteLine("{0}:null records", x.name);
continue;
}
foreach (var record in test.records)
{
output.WriteLine("{0}\t{1}",x.name, record.Id);
}
Console.WriteLine("{0}:\t{1} records(0)", x.name, test.size);
}
}
output.Flush();
}

Convert list of pairs into array groups in AS3

I have an ActionScript 3 array that lists pairs of items like this:
pairs[0] = Array('ItemA', 'ItemB');
pairs[1] = Array('ItemA', 'ItemC');
pairs[2] = Array('ItemC', 'ItemD');
pairs[3] = Array('ItemC', 'ItemE');
pairs[4] = Array('ItemF', 'ItemG');
pairs[5] = Array('ItemF', 'ItemH');
And I need to loop over the array in some way to find all overlapping pairs (any pairs that share common pairs).
For example, ItemA is paired with ItemB and ItemC, so they belong in a group together. ItemC is also paired with ItemD and ItemE so they also need to be a part of the first group.
ItemF, ItemG and ItemH do not overlap with any of the items fromt he first group, so they need to be put into their own group.
The resulting array would need to be something like:
groups[0] = Array('ItemA', 'ItemB', 'ItemC', 'ItemD', 'ItemE');
groups[1] = Array('ItemF', 'ItemG', 'ItemH');
Thanks for any help and suggestions!
Edit:
A little bit of a back story; I'm trying to group together movie clips that overlap each other in 2D to create groups or clusters (might be a better word).
So if I have 3 movieclips on the stage and ClipA overlaps with ClipB and ClipB overlaps ClipC (but ClipA doesn't directly overlap ClipC) they should all be grouped together as they are all a part of the same cluster. This way should a new clip overlap any single item in a cluster, it will be added to that group's array.
I've already got the code worked out to find overlapping elements which is producing this pairs list, now I need to condense it into tidy groups.
An algorithm like the example below should work.
NOTE: This is not the most efficient or concise way to write this code (it's certainly more repetitive than it needs to be), but I wanted to keep it clear and simple for this example. [Also, I haven't tested this code--it's presented as pseudo-code only--so if you find an error, please just let me know, and I'll fix it]
var idx:Object = new Object;
var groups:Array = new Array();
for( var i:int = 0; i<pairs.length; ++i ) {
var onePair:Array = pairs[i];
// which groups do the two items belong to?
var g1:Array = idx[onePair[0]];
var g2:Array = idx[onePair[1]];
if( !g1 ) {
// if item #1 is not yet in a group, then add it to item #2's
// existing group, or if neither group exists yet, just create a new one
g1 = g2;
if( !g1 ) {
g1 = [];
groups.push(g1);
}
g1.push( onePair[0] );
// ensure that the idx properly reflects the location of the new item
idx[onePair[0]] = g1;
}
// now do the same for the second item... but g1 will never be null, so
// this case is a little simpler.
if( !g2 ) {
g2 = g1;
g2.push( onePair[1] );
idx[onePair[1]] = g2;
}
if( g1 != g2 ) {
// now, if they're not already the same group, then merge the two
// groups, and update the idx to reflect the merge.
for( var z:int=0; z<g2.length; ++z ) {
idx[g2[z]] = g1;
g1.push( g2[z] );
g2.splice(0);
}
}
}
groups will end up being an array of arrays, just like you asked for -- but there will be a few empty arrays that can be discarded. Just prune (or ignore) the empty ones, and you'll have your groups.
the basic idea here, is that idx provides a lookup table that indicates, throughout the indexing process, for any given item, which group it's in (if any). This allows us to determine whether an item has been encountered previously or not, and if so, to utilize it's existing group.
You can use an Object to keep the track of the association of a pair iten and a group, the key will be each item of your pair.
Here a litle snippet that make the works :
var pairs:Array=[];
pairs[0] = ['ItemA', 'ItemB'];
pairs[1] = ['ItemA', 'ItemC'];
pairs[2] = ['ItemC', 'ItemD'];
pairs[3] = ['ItemC', 'ItemE'];
pairs[4] = ['ItemF', 'ItemG'];
pairs[5] = ['ItemF', 'ItemH'];
// will contain group created
var groups:Array=[];
// will contain association between a pair item and a group
var pair2group:Object={};
// function that turn pairs into groups
function makeGroups(pairs:Array):void{
var pairLen:int = pairs.length;
for (var i:int=0;i<pairLen;i++){
var pair:Array = pairs[i];
var item1:String = pair[0];
var item2:String = pair[1];
var group:Array = pair2group[item1];
// is first pair item already in a group
if (group == null) {
// no so create a new group
group=[];
// create the association
pair2group[item1] = group;
// add the item to the group we have created
group.push(item1);
// add it to all the groups
groups.push(group);
}
// is the second pair item into a grouo
if (pair2group[item2] == null) {
// no so add it to the group where the first item belong
group.push(item2);
// create the association for the second item
pair2group[item2] = group;
}
}
}
// ---- test
makeGroups(pairs);
trace(groups.length);
trace(groups[0]);
trace(groups[1]);
After lots of playing around here's the solution I came up with.
This will take an 2D overlapArray that has pairs and produce a group list with unique values.
I used a in_array() function to duplicate PHP's handy function for finding if an item is already in an array.
for each(var pair:Array in overlapArray) {
var pairInGroup = false;
for each(var group:Array in overlapArrayGroups) {
if(in_array(pair[0],group) || in_array(pair[1],group)) {
if(!in_array(pair[0],group)) {
group.push(pair[0]);
}
if(!in_array(pair[1],group)) {
group.push(pair[1]);
}
pairInGroup = true;
}
}
if(!pairInGroup) {
overlapArrayGroups.push(pair);
}
}
The in_array() function:
public static function in_array( needle:String, haystack:Array ):Boolean {
for( var a = 0; a < haystack.length; a++ ) {
if( haystack[a] == needle ) {
return true;
} else if( haystack[a] is Array ) {
return in_array(needle, haystack[a]);
}
}
return false;
}

Resources