Hi,
Thanks for your question.
I suppose that a “limit bandwidth”-optimization could be based on the provider
looking at all the instructions in the submitted instruction and then use that
information to constrain what bytecode patterns it exposes. A simple
ProviderStrategy would be the means of doing that.
Perhaps showing you what I think the Tuple API should look like would help.
This API would represent the primary way in which the TP VM interacts with the
structure/ provider. Thus, this is for all cookies in the cookie jar!
############################################################
public interface Tuple<A> extends Iterator<Tuple<A>> {
public boolean hasKey(Object key);
public boolean hasValue(Object value);
public <B> Tuple<B> get(Object key);
public A value();
public long count();
public boolean hasNext();
public Tuple<A> next();
public boolean match(Instruction instruction);
public Tuple apply(Instruction instruction);
}
############################################################
Structure neo4j = Neo4jStructureFactory.open(config1)
Tuple<Map<String,String> db = neo4j.root();
=> { type:graph | [V] }#1
//////
Let a =
{ type:vertex, name:marko, age:29 | [inE] [outE] }#1
a.count() => 1
a.value() =>
Map.of('type','vertex','name','marko','age',29)
a.get('type') => { 'vertex' }#1
a.get('name') => { 'marko' }#1
a.hasKey('blah') => false
a.match(Instruction.of('outE')) => true
//////
b = a.apply(Instruction.of('outE’))
{ type:edge, label:?string | [outV] [inV] }#?
b.count() => -1
b.hasKey('weight') => null // not false because all we
know is type:edge & label:?string about #? of things.
b.hasKey('type') => true
b.hasKey('label') => true
b.get('label') => { ?string }#? // ?string is something like
Unknown.of(Type.string())
//////
c = b.apply(Instruction.of('inV'))
{ type:vertex }#?
c.count() => -1
c.value() => Map.of('type','vertex')
c.hasNext() => true
c.next() => { type:vertex, name:stephen, age:17 | [inE] [outE] }
c.hasNext() => true
c.next() => { type:vertex, name:kuppitz | [inE] [outE] }
c.hasNext() => false
c.count() => 0
//////
d = { type:vertex, name:kuppitz | [inE] [outE] }
e = d.get('name')
{ kuppitz }#1
e.count() => 1
e.value() => 'kuppitz'
//////
Let f =
{ type:edge | [outV] [inV] [has,label,eq,?0] }?10
f.count() => 10
f.get('type') => { 'edge' }#10
f.match(Instruction.of('has','label',P.eq,'knows')) => true
//////
g = f.apply(Instruction.of('has','label',P.eq,'knows'))
{ type:edge, label:knows | [outV] [inV] }#1
g.count() => 1
g.hasNext() => true
g.next() => { type:edge, label:knows | [outV] [inV] }#1 // its iteration
is itself!
g.hasNext() => false // g lost the
reference
g.count() => 0
//////
Cool? Questions?
Thanks,
Marko.
http://rredux.com <http://rredux.com/>
> On May 17, 2019, at 6:57 AM, Stephen Mallette <[email protected]> wrote:
>
> This is a nicely refined representation of this concept. I think I've
> followed this abstractly since you first started discussing it, but I've
> struggled with the implementation of it and how it would best work (which
> is probably the reason I keep thinking that I"m not following the
> abstraction hehe). You nicely wrote this from the perspective of the
> individual providers which I think connected me more to the more concrete
> aspect of things, which leads me to this question: Does the provider send
> the instructions by looking at the query or do they just provide all the
> possible instructions and TP figures it out? (i feel like i've kinda read
> it both ways at different times).
>
> On Fri, May 17, 2019 at 8:12 AM Marko Rodriguez <[email protected]
> <mailto:[email protected]>>
> wrote:
>
>> Hello,
>>
>> This email is primarily for Kuppitz and Josh. Kuppitz offered me his
>> attention yesterday. I explained to him an idea I’ve been working on this
>> week. I’ve been frustrated lately because emails and IM are so hard to
>> express abstract ideas. Fortunately, Kuppitz was patient with me. Then he
>> got it. Then he innovated on it. I was elated.
>>
>> https://twitter.com/twarko/status/1129117666910674944
>> <https://twitter.com/twarko/status/1129117666910674944> <
>> https://twitter.com/twarko/status/1129117666910674944
>> <https://twitter.com/twarko/status/1129117666910674944>>
>>
>> Josh was interested in what this was all about. I had to go to leave for
>> hockey, but I gave him a fast break down. He sorta got the vibe, but wanted
>> to know more…..
>>
>> ########################################
>>
>> There is only one type of “tuple.”
>>
>> { }#?
>>
>> The notation says: there are objects, but I don’t know how many of them
>> there are…..if you want to know more, iterate.
>>
>> ########################################
>>
>> Let us begin…………..
>>
>>
>> ——————TP4 WITH PROVIDER A——————
>>
>> g.
>>
>> { [V] }#1
>>
>> There is one object. Thus, what you see is all that I know about this
>> object. In particular, what I know is that it can be mapped via the
>> bytecode instruction [V].
>>
>> Let us apply [V].
>>
>> { name:?string | [has,age,?0,?1] [has,id,eq,?0] }#?
>>
>> There are some number of objects. If you want to know what they are,
>> iterate. However, I am aware of a feature that they all share. I do know
>> for a fact (by the way I was designed by my creator ProviderA) that every
>> one of the objects has a name-key to some string value. Also, two has()
>> bytecode patterns are available.
>>
>> Let us apply [hasKey,name].
>>
>> { name:?string | [has,age,?0,?1] [has,id,eq,?0] }#?
>>
>> The instruction didn't match any of the available bytecode patterns. Thus,
>> the instruction has to evaluated. Did you need to iterate and filter out
>> those that don’t have a name-key? No. As I told you, I know that every one
>> of the objects has a name-key.
>>
>> Let us apply [has,id,eq,1].
>>
>> { name:marko, age:29 | [inE] [outE] }#1
>>
>> There is one thing. It has primitive key/value data — a name and an age.
>>
>> Let us apply [values,name].
>>
>> { marko }#1
>>
>> That bytecode instruction didn't match any the available bytecode
>> patterns. The instruction was evaluated and there is one thing: the string
>> “marko.”
>>
>> We did:
>>
>> g.V().hasKey(‘name’).hasId(1).values(‘name’)
>>
>> The query you provided used an index on id. How do we know that? You
>> didn’t have to iterate all the objects and filter on id. I was able to jump
>> from all vertices to the one with id=1.
>>
>> ——————TP4 WITH PROVIDER B——————
>>
>> { type:person, name:?string, age:?int | [has,name,eq,?0] }?10
>>
>> There are 10 objects. Some providers can’t determine how many objects
>> there are without full iteration. But, by the way I was designed, I know. I
>> also know that all the object have a type:person key/value. I also know
>> they all have a name-key and int-key with known value types.
>>
>> What am I?
>>
>> CREATE TABLE people {
>> name varchar(100),
>> age int
>> }
>> CREATE INDEX people_name_idx ON people (name);
>>
>> ——————TP4 WITH PROVIDER C——————
>>
>> g.V().has(‘name’,’marko’).has(‘age’,gt(20)).id()
>>
>> This is easy. My creator, ProviderC, provides multi-key indices. And when
>> the database instance was created, a (name,age)-index was created. Also,
>> because you only want the id of those vertices named marko whose age is
>> greater than 20, I don’t have to manifest the vertices, I can simply get
>> the id out of the index. This is what I provided for each instruction of
>> your query...
>>
>> 1. { type:graph | [V] }#1
>> 2. { type:vertex | [has,name,eq,?0] [has,age,?0,?1] [id] }#?
>> 3. { type:vertex, label:person, name:marko | [has,age,?0,?1] [id] }#?
>> 4. { type:vertex, label:person, name:marko, age:gt(20) | [id] }#?
>> 5. { type:int }#?
>>
>> Unlike ProviderA, all the objects in me have a type-key. It is just
>> something I like to do. Call it my quirk. Thus, on line #2, I know that
>> there are some number of vertex objects. And do you see my multi-property
>> index there? On line #3, I know for a fact that every one of those objects
>> has a name:marko entry. Finally, by line #5, I don’t know how many
>> id-objects there are, but I do know they are all integers. If you want to
>> know what they are, iterate.
>>
>> Below are the possible "bytecode pattern”-paths that are available off of
>> the graph object. At any point through this pattern, you could iterate.
>>
>> [V]
>> / | \
>> / [id]\
>> / \
>> [has,name,eq,?0] [has,age,?0,?1]
>> / \ / \
>> / \ / \
>> [has,age,?0,?1] [id] [has,name,eq,?0] [id]
>> | |
>> [id] [id]
>>
>>
>> *** In case the diagram above looks weird in your mail client:
>> https://gist.github.com/okram/f7f20a3c33aa7caca7c28e85fd16be3f <
>> https://gist.github.com/okram/f7f20a3c33aa7caca7c28e85fd16be3f
>> <https://gist.github.com/okram/f7f20a3c33aa7caca7c28e85fd16be3f>>
>>
>> ——————TP4 WITH PROVIDER D——————
>>
>> I support "vertex-centric indices.” For certain queries, I don’t have to
>> manifest/iterate the incident edges of a vertex to check their key/value
>> pairs. In particular, I have index all the incident knows-edges by their
>> weight property. Wanna know who marko knows well? Do this query:
>>
>> …outE(‘knows’).has(‘weight’,gt(0.85)).inV()
>>
>> { label:person, name:marko, age:29 | [outE] [inE] }#1
>> // [outE]
>> { weight:float? | [has,label,eq,?1] [inV] }#20
>> // [has,label,eq,knows]
>> { label:knows, weight:float? | [has,weight,?0,?1] [inV] }#15
>> // [has,weight,gt,0.85]
>> { label:knows, weight:gt(0.85) | [inV] }#15
>> // [inV]
>> { label:person }#15
>>
>> See. I didn’t create single edge! I do know there are 20 outgoing edges
>> from marko, but I didn’t manifest them. I then was able to jump to the
>> adjacent vertices. If you want to know about those, you can iterate….
>>
>> …label()
>>
>> { person }#15
>>
>> Haha. I don’t have to iterate to solve that. I know that all 15 adjacent
>> vertices are labeled as ‘person’. I was able to go from v[1] to 15 person
>> strings without manifesting any intermediate edges or vertices! I’m pretty
>> freakin’ sweet. How do I know that you ask? I’m an in-memory graph database
>> and my vertex-centric indices are just Java sets. Its cheap for me to
>> provide counts, so I do. Most other providers can’t do that. But I can.
>>
>> ——————TP4 WITH PROVIDER E——————
>>
>>
>> …out(‘knows’).values(‘name’)
>> ==compiles to==>
>> [outE][has,label,eq,knows][inV][values,name]
>>
>>
>> { name:marko, age:29 | [outE] [inE] }#1
>> // [outE]
>> { [has,label,eq,?1] [inV] }#20
>> // [has,label,eq,knows]
>> { label:knows | [inV] }#15
>> // [inV]
>> { label:person | [values,name] }#15
>> // [values,name]
>> { type:string }#15
>>
>> Did you see that? I didn’t manifest any incident edges nor adjacent
>> vertices and I was able to give you the name of all the people that marko
>> knows! Can you guess what features I have?
>>
>> * Incident edges are indexed by label.
>> * Certain properties of a vertex can be denormalized (stored
>> locally) to their adjacent neighbors.
>>
>> Thanks for reading,
>> Marko.
>>
>> http://rredux.com <http://rredux.com/> <http://rredux.com/
>> <http://rredux.com/>>