Complex secondary index operations - rethinkdb

I can create a secondary index for this table:
{
contact: [
'example#example.com'
]
}
Like this: indexCreate('contact', {multi: true})
But can I create index for this:
{
contact: [
{
type: 'email',
main: true
value: 'andros705#gmail.com'
}
{
type: 'phone'
value: '0735521632'
}
]
}
Secondary index would only search in objects whose type is 'email' and main is set to 'true'

Here's how you might create such an index:
table.indexCreate(
'email',
row => row('contact').filter({type: 'email'})('value'),
{multi: true})
This works by using a multi-index. When the multi: true argument is passed to indexCreate, the index function is expected to return an array instead of a single value. Every element in that array can be used to look up the document in the index (using getAll or between). If the array is empty, the document will not show up in the index.

Related

keystonejs form a multi-column unique constraint

How to form a unique constraint with multiple fields in keystonejs?
const Redemption = list({
access: allowAll,
fields: {
program: relationship({ ref: 'Program', many: false }),
type: text({ label: 'Type', validation: { isRequired: true }, isIndexed: 'unique' }),
name: text({ label: 'name', validation: { isRequired: true }, isIndexed: 'unique' }),
},
//TODO: validation to check that program, type, name form a unique constraint
})
The best way I can think to do this currently is by adding another field to the list and concatenating your other values into it using a hook. This lets you enforces uniqueness across these three values (combine) at the DB-level.
The list config (and hook) might look like this:
const Redemption = list({
access: allowAll,
fields: {
program: relationship({ ref: 'Program', many: false }),
type: text({ validation: { isRequired: true } }),
name: text({ validation: { isRequired: true } }),
compoundKey: text({
isIndexed: 'unique',
ui: {
createView: { fieldMode: 'hidden' },
itemView: { fieldMode: 'read' },
listView: { fieldMode: 'hidden' },
},
graphql: { omit: ['create', 'update'] },
}),
},
hooks: {
resolveInput: async ({ item, resolvedData }) => {
const program = resolvedData.program?.connect.id || ( item ? item?.programId : 'none');
const type = resolvedData.type || item?.type;
const name = resolvedData.name || item?.name;
resolvedData.compoundKey = `${program}-${type}-${name}`;
return resolvedData;
},
}
});
Few things to note here:
I've removed the isIndexed: 'unique' config for the main three fields. If I understand the problem you're trying to solve correctly, you actually don't want these values (on their own) to be distinct.
I've also remove the label config from your example. The label defaults to the field key so, in your example, that config is redundant.
As you can see, I've added the compoundKey field to store our composite values:
The ui settings make the field appear as uneditable in the UI
The graphql settings block updates on the API too (you could do the same thing with access control but I think just omitting the field is a bit cleaner)
And of course the unique index, which will be enforced by the DB
I've used a resolveInput hook as it lets you modify data before it's saved. To account for both create and update operations we need to consult both the resolvedData and item arguments - resolvedData gives us new/updated values (but undefined for any fields not being updated) and item give us the existing values in the DB. By combining values from both we can build the correct compound key each time and add it to the returned object.
And it works! When creating a redemption we'll be prompted for the 3 main fields (the compound key is hidden):
And the compound key is correctly set from the values entered:
Editing any of the values also updates the compound key:
Note that the compound key field is read-only for clarity.
And if we check the resultant DB structure, we can see our unique constraint being enforced:
CREATE TABLE "Redemption" (
id text PRIMARY KEY,
program text REFERENCES "Program"(id) ON DELETE SET NULL ON UPDATE CASCADE,
type text NOT NULL DEFAULT ''::text,
name text NOT NULL DEFAULT ''::text,
"compoundKey" text NOT NULL DEFAULT ''::text
);
CREATE UNIQUE INDEX "Redemption_pkey" ON "Redemption"(id text_ops);
CREATE INDEX "Redemption_program_idx" ON "Redemption"(program text_ops);
CREATE UNIQUE INDEX "Redemption_compoundKey_key" ON "Redemption"("compoundKey" text_ops);
Attempting to violate the constraint will produce an error:
If you wanted to customise this behaviour you could implement a validateInput hook and return a custom ValidationFailureError message.

Change the index of an array in array push

In the application, we implemented the laravel-vue-pagination and in our pagination our columns are checkbox and paid amount(input field) where the checkbox is what user want to pay the item.
Once the page is load, it will save in one variable which is called items.
We have an event on clicking the checkbox where when the user check it will push to another variable which is called selected_items. When it is uncheck it will remove the value in the selected_items
getPerSelectedItems(i,purchase_item_id){
if(this.items.data[i].checked == true)
{
this.selected_items.push({
amount_due: this.items.data[i].amount_due,
balance: this.items.data[i].balance,
checked: this.items.data[i].checked,
date_due: this.items.data[i].date_due,
net_of_vat: this.items.data[i].net_of_vat,
number: this.items.data[i].number,
paid_amount: this.items.data[i].paid_amount,
purchase_item_id: this.items.data[i].purchase_item_id,
purchase_tag: this.items.data[i].purchase_tag,
selected_chart_of_account: { id: this.items.data[i].selected_chart_of_account.id, name: this.items.data[i].selected_chart_of_account.code+" : "+this.items.data[i].selected_chart_of_account.name },
selected_responsibility_center: { id: this.items.data[i].selected_responsibility_center.id, name: this.items.data[i].selected_responsibility_center.name },
selected_wht_list: { id: this.items.data[i].selected_wht_list.id, name: this.items.data[i].selected_wht_list.name, rate: this.items.data[i].selected_wht_list.rate },
total_amount_payable: this.items.data[i].total_amount_payable,
total_wht: this.items.data[i].total_wht,
transaction: this.items.data[i].transaction,
transaction_id: this.items.data[i].transaction_id,
wht_payable: this.items.data[i].wht_payable,
with_tax: this.items.data[i].with_tax
})
}else{
this.selected_items = this.selected_items.filter(function( obj ) {
// return obj.purchase_item_id !== this.items.data[i].purchase_item_id;
return obj.purchase_item_id !== purchase_item_id;
});
}
},
For example I checked the two items, the variable items will look like this.
Below is the selected_items.
Now my question is it possible to change the index of selected_items? In my example above, the selected_items should be like this
selected_items: [
5: {},
6: {}
]
Not like this
selected_items: [
0: {},
1: {}
]
Javascript array cant skip indexes or have "string indexes". A Javascript Array is exclusively numerically indexed. When you set a "string index", you're setting a property of the object.
If you set the index 5 and 6 of an array, it will look like this.
selected_items: [
0: undefined,
1: undefined,
2: undefined,
3: undefined,
4: undefined,
5: {},
6: {}
]
this.selected_items.push() will always set numeric key index.
You should use this to set a specific index.
this.selected_items[i] = this.items.data[i];

Use one of the two incoming observable stream to filter data in angular

I have the below code I have the formcontrol value (its a multicheckbox list), the values is an array of true/false, [true, false, true, true, ...]. This is one of the many lists
I also have the data list - collection of objects of structure {id: number, desc: string, disabled: boolean, selected: boolean}
I need to retrieve the id matching the true by matching index, and set an observable
I have the below code
valueChanged(e: any, name: string, key: string, valuesArray: string) {
this.hasChanged = true;
from(name).pipe(
debounceTime(200),
withLatestFrom(this[name].value), // this[name] is form array, gives [true, false, false,..]
mergeMap(values => forkJoin(
of(values),
this[valuesArray].find(val => val.name === name).data
)),
mergeMap(([values, data]) => {
flatMap((val, i) => val ? data[i].id : null),
filter(id => id !== null),
map(ids => {
this.selectedFilters[key] = ids;
switch (key) {
case 'groupId':
this.curGroup.next(ids);
break;
}
});
})
);
}
I need help on the line flatmap where I want to use values, for each of the value (true/false), if val[i] is true, include data[i].id in the output i want he id array of true values [1,2,4,5,..]. I get the error
Argument of type '([values, data]: [[string, unknown], unknown]) => void' is not assignable to parameter of type '(value: [[string, unknown], unknown], index: number) => ObservableInput'.
Type 'void' is not assignable to type 'ObservableInput'.ts(2345)
Need help on how to use solve this issue. Thanks in advance.

Add/modify text between parentheses

I'm trying to make a classified text, and I'm having problem turning
(class1 (subclass1) (subclass2 item1 item2))
To
(class1 (subclass1 item1) (subclass2 item1 item2))
I have no idea to turn text above to below one, without caching subclass1 in memory. I'm using Perl on Linux, so any solution using shell script or Perl is welcome.
Edit: I've tried using grep, saving whole subclass1 in a variable, then modify and exporting it to the list; but the list may get larger and that way will use a lot of memory.
I have no idea to turn text above to below one
The general approach:
Parse the text.
You appear to have lists of space-separated lists and atoms. If so, the result could look like the following:
{
type => 'list',
value => [
{
type => 'atom',
value => 'class1',
},
{
type => 'list',
value => [
{
type => 'atom',
value => 'subclass1',
},
]
},
{
type => 'list',
value => [
{
type => 'atom',
value => 'subclass2',
},
{
type => 'atom',
value => 'item1',
},
{
type => 'atom',
value => 'item2',
},
],
}
],
}
It's possible that something far simpler could be generated, but you were light on details about the format.
Extract the necessary information from the tree.
You were light on details about the data format, but it could be as simple as the following if the above data structure was created by the parser:
my $item = $tree->{value}[2]{value}[1]{value};
Perform the required modifications.
You were light on details about the data format, but it could be as simple as the following if the above data structure was created by the parser:
my $new_atom = { type => 'atom', value => $item };
push #{ $tree->{value}[1]{value} }, $new_atom;
Serialize the data structure.
For the above data structure, you could use the following:
sub serialize {
my ($node) = #_;
return $node->{type} eq 'list'
? "(".join(" ", map { serialize($_) } #{ $node->{value} }).")"
: $node->{value};
}
Other approaches could be available depending on the specifics.

Upsert hash in mongodb. Remove unused keys

From the Mongo documentation:
If you specify multiple field-value pairs, $set will update or create
each field.
I have a mongoid document like this:
class MyCounter
include Mongoid::Document
field :date, type: Date
field :properties, type: Hash
end
and when I try to change the properties like this:
hash = {properties.0 => 50, properties.1 => 100 }
MyCounter.where(something).find_and_modify(
{ '$set' => hash, {'upsert' => 'true', new: true}
)
it keeps the old keys on the property hash.
What's the correct way to completely replace (and create a new document if it doesn't exist) an hash in a document?
EDIT
I'm currently doing this which is dumb:
MyCounter.where(
date: date
).find_and_modify(
{ '$unset' => { properties: nil} }, {'upsert' => 'true', new: true}
)
MyCounter.where(
date: date
).find_and_modify(
{ '$set' => hash }, {'upsert' => 'true', new: true}
)
Just don't use $set. By only passing field: value pairs all the fields are replaced (except the _id field)
This should work fine.
MyCounter.where(
date: date
).find_and_modify(
hash, {'upsert' => 'true', new: true}
)
http://docs.mongodb.org/manual/reference/method/db.collection.update/#example-update-replace-fields

Resources