Skip to content

[query] Full support for manipulating multivalue properties #90

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 18, 2014

Conversation

dantleech
Copy link
Member

Adds support for updating multivalue properties with the UPDATE query:

UPDATE: This PR now includes support for evaluating functions as values over the previous syntax for updating multivalues properties. Currently only array functions are implemented.

UPDATE: This PR now uses functions exclusively for setting and manipulating array valyes.

The functions are based on the Postgres array functions.

array_remove($fieldSpec, 'ValueToRemove') // remove by value
array_replace($fieldSpec, 'ValueToReplace', 'Replacement'); // replace by valyue
array_append($fieldSpec, 'ValueToAppend); // append value

Other functions:

array('asdf', 'asdf', 'asdf') // returns an array value (i.e. `SET a.tags = array('foo', 'bar')`)
array_set($fieldSpec, 3, 'foo') // set index 3 to "foo"
UPDATE [nt:unstructured] AS a SET a.tags = array_replace(a.tags, 'Trains', 'Rockets') WHERE a.tags = 'Trains'
UPDATE [nt:unstructured] AS a SET a.tags = array('Rockets', 'Dragons') WHERE a.tags = 'Trains'
UPDATE [nt:unstructured] SET tags = array_replace(tags, 'Planes', 'Rockets') WHERE tags = 'Planes'
UPDATE [nt:unstructured] AS a SET a.tags = array_remove(a.tags, 'Planes') WHERE a.tags = 'Planes'
UPDATE [nt:unstructured] AS a SET a.tags = array_set(a.tags, 0, NULL) WHERE a.tags = 'Planes'
UPDATE [nt:unstructured] AS a SET a.tags = array_append(a.tags, 'Kite') WHERE a.tags = 'Planes'
UPDATE [nt:unstructured] AS a SET a.tags = array_set(a.tags, 1, 'Kite'), a.tags = array_set(a.tags, 2, 'foobar') WHERE a.tags = 'Planes'

See also documentation: phpcr/phpcr-docs#17

@dantleech
Copy link
Member Author

Depends on https://github.com/phpcr/phpcr-utils/pull/121/files
and is related to: #81

@dantleech
Copy link
Member Author

//cc @dbu

@dbu
Copy link
Member

dbu commented Aug 28, 2014

nice one!

how exactly do the first examples work? do they replace Trains, or all tags? (i would expect to replace all). and setting an index to NULL removes the value from the list, right? i think you can't store null as a value. and a.tags = NULL would remove the whole list?
what is the difference between test 1 and 3? you search for Trains and then Planes, but set the same thing.

@dbu
Copy link
Member

dbu commented Aug 28, 2014

i was wondering if using an absolute index as you do is valid. from reading i think it is:
http://www.day.com/specs/jcr/2.0/10_Writing.html#10.4.2.5%20Multi-value%20Properties%20and%20Null

however, i can't really think of a good use case for that. what is much more likely is that i want to replace the value i search for with something new. would there be a way to do either a complete string replacement or even a regex pattern replace that would apply to the specific value in the list? (i am thinking of things like: update all Plane tags to Airplane to standardize tags)

@dantleech
Copy link
Member Author

  • The first replaces the index containing Planes with Rockets
  • The second replaces the entire value (as we specify an array in SET)
  • One andf three are indeed the same :P
  • Setting to NULL will remove the value at the matching index.

I think the use case for the index thing would be very specific but the use case for the [] syntax is generally useful, and given that, parsing an index is easy.

If you specify SET foo = "bar" WHERE foo = "baz" then this will replace the single value matching baz in the value of the multivalue property.

And the node at "/cms/articles/article1" should have the property "tags" with value "Rockets" at index "0"
And the node at "/cms/articles/article1" should have the property "tags" with value "Dragons" at index "1"

Scenario: Update single multivalue
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as Replace a multivalie index by value

@dbu
Copy link
Member

dbu commented Aug 29, 2014

Ah cool, sounds useful. What happens if i do "where a.tags = 'a' and a.tags = 'b'"? Should we not allow the replace syntax in that case? Or come up with something like regexp do for match groups?

@dantleech
Copy link
Member Author

Hmm, actually now you say that I am unsure about this syntax for replacing values, we are mixing the two sides of the query.

Maybe something like:

// @ Planes
UPDATE [nt:foo] SET a.tags[@"Planes"] = 'Bikes' WHERE a.tags = 'Planes';

// quoted value means select index by value
UPDATE [nt:foo] SET a.tags["Planes"] = 'Bikes' WHERE a.tags = 'Planes';

// two squre brackets means select index by value
UPDATE [nt:foo] SET a.tags[["Planes"]] = 'Bikes' WHERE a.tags = 'Planes';

This is pushing the syntax a bit of course -- are there any precedents for this type of thing in other languages?

@dbu
Copy link
Member

dbu commented Sep 1, 2014

i am -1 on the second option, because it looks like "Planes" would be the index in a hashmap, while it is actually a value. the [[ look a bit weird. i think the @ best shows the meaning. maybe perl already has something like this, being the language of cryptic shortcuts?

@dantleech
Copy link
Member Author

See discussion: #81

@dantleech
Copy link
Member Author

Updated to use functions.

@dantleech
Copy link
Member Author

Note it would be good to also implement functions like CONCAT, JOIN, LOWERCASE, etc. In fact we could implement all of the MySQL / Postgres functions.

The problem is that while we could implement them the SELECT statement SELECT LOWERCASE(a.title) it would be really pushing it to implement this on the criteria WHERE LOWERCASE(a.title) = 'asdf'.

Using functions in SELECT could also be a good strategy for retrieving the node path and score, e.g. SELECT PATH(a), SCORE(a) FROM [fooar] AS a.

// 'name' => 'tags',
// 'value' => new FunctionOperand(new ColumnOperand('a', 'tags'), array('asd', 'dsa')),
// ),
// ));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needed or not?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

technically yes. but I can't find a way to make the assertion work without refactoring the code .. maybe that wouldn't be such a bad idea.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this test in the end. This is one of the things I don't like about PHPSpec, its not that flexible.

@dbu
Copy link
Member

dbu commented Oct 8, 2014

do you have open questions or is this ready? functions for select statements are in conflict with staying compatible with jcr, and they would not work with jackalope-jackrabbit as we need to run the search on the server.

@dantleech
Copy link
Member Author

I think it needs a cleanup :) Functions would never be used with the SELECT, only with UPDATE.

@dantleech dantleech force-pushed the update_query_indexes branch from c60428f to 8e013c3 Compare October 8, 2014 13:55
@dantleech dantleech force-pushed the update_query_indexes branch from 8e013c3 to a42c2e2 Compare October 9, 2014 05:28
@dantleech
Copy link
Member Author

Updated. Will merge it later.

@dantleech
Copy link
Member Author

Hmm. Updated the description of this PR, I have now removed the [] operators and all array operations are now effected as functions.

@dantleech dantleech force-pushed the update_query_indexes branch from 783fa22 to c77d386 Compare October 12, 2014 07:42
array_shift($values);
return $values;
},
'array_set' => function ($operand, $current, $index, $value) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if array_set is a good name, i.e.

SET a.tags = array_set(a.tags, 'foo', 'bar');

Maybe:

array_replace_index(a.tags, 0, 'foo')

Would make more sense.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could also use array_replace in both cases, and if its an int its the index, if its a string its the value to replace. hm, sounds really confusing though when you accidentally pass a number as string...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I think we should keep setting indexes to values and replacing values with values should remain as separate functions.

@dantleech dantleech force-pushed the update_query_indexes branch from c77d386 to 9d9e560 Compare October 12, 2014 07:53
@dantleech
Copy link
Member Author

using array() does work..

@dantleech
Copy link
Member Author

ping @dbu - what do you think? arrays are now set with a function (foo = array('val1', 'val2')), and I am not sure about the naming of the function indicated above..

dantleech added a commit that referenced this pull request Oct 18, 2014
[query] Full support for manipulating multivalue properties
@dantleech dantleech merged commit f2c597b into master Oct 18, 2014
@dantleech dantleech deleted the update_query_indexes branch October 18, 2014 17:58
@dantleech
Copy link
Member Author

Merged. I think the function name and syntax are OK.

# Delete a multivalue index
UPDATE [nt:unstructured] SET a.tags = array_set(a.tags, 0, NULL)

# Set a multivalue index
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would this be overwriting or insert at that position?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overwriting

@dbu
Copy link
Member

dbu commented Oct 20, 2014

sorry, only found the time to review now. i would suggest these names:

  • array_insert_at (numeric index, any location. what if location > length?)
  • array_append (add to end. i guess rather than prepend, one would use insert_at(0) )
  • array_remove_at (remove by index)
  • array_replace (replace all occurences of value)
  • array_remove (remove all occurences of value)
    you could now also do array_insert_after|before if you want. its less useful though.

@dantleech
Copy link
Member Author

I was probably a bit to hasty in merging as the tests are now failing, will have another look, insert_at would be a new operation right? I don't think I have covered that, only replacing indexes.

@dbu
Copy link
Member

dbu commented Oct 20, 2014

indeed, so i would call it array_replace_at instead of array_set to make it more explicit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants