Skip to content
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

How i can select document #1885

Closed
uMaxmaxmaximus opened this issue Jun 21, 2016 · 13 comments
Closed

How i can select document #1885

uMaxmaxmaximus opened this issue Jun 21, 2016 · 13 comments

Comments

@uMaxmaxmaximus
Copy link

uMaxmaxmaximus commented Jun 21, 2016

i use arangojs for nodejs

userDocument = {
  name: 'Ololo',
  age: 11,
  sessions: [
    {key: 324342},
    {key: 675604}
  ]
}

how i can select this document by session key ?

logic: if any object from sessions array have key 324342, select this user

@uMaxmaxmaximus
Copy link
Author

uMaxmaxmaximus commented Jun 21, 2016

await collection.byExample({sessions: [{key: 'trololo'}]}) ???
logic dictates that that's true =)

@jsteemann
Copy link
Contributor

byExample() will look for an exact match, but an array with two members is different from an array with a single member.
You can use AQL for this, e.g. db._query("FOR doc IN collection FILTER 'trololo' IN doc.sessions[*].key RETURN doc");

@uMaxmaxmaximus
Copy link
Author

uMaxmaxmaximus commented Jun 21, 2016

@jsteemann i create ORM for your database https://www.npmjs.com/package/ormjs
Please create js style selectors for queries?

collection.found({'doc.sessions[*].key': 'Trololo'}) // why not create this

I can write a sql query builder. need one? =)

@Simran-B
Copy link
Contributor

While it may be tempting to support query patterns in the fashion of byExample(), they would all be very limited in scope and have to deal with ambiguities - in your suggested .found() method, it wouldn't be possible to retrieve any attribute with a dot in the key, unless it supported backslash escapes or a similar escaping facility. AQL is the way to go, because you can express even very complicated queries without ambiguities / ugly escaping.

Regarding ORM, make sure you read the following discussion: arangodb/arangojs#215

There is already a query builder for JavaScript (e.g. Node.js): https://github.com/arangodb/aqbjs
It emits AQL in the end. You can write AQL directly however, unless some kind of lists of variable length shall be generated...

@uMaxmaxmaximus
Copy link
Author

uMaxmaxmaximus commented Jun 22, 2016

@jsteemann how i can select this document by session key, and type == 'mobile' ?

userDocument = {
  name: 'Ololo',
  age: 11,
  sessions: [
    {key: 324342, type: 'mobile'},
    {key: 675604, type: 'pc'}
  ]
}

logic: if any object from sessions array have key 324342 AND this session type is 'mobile', select this user

FOR doc IN collection FILTER
  324342 IN doc.sessions[*].key AND 'mobile' IN doc.sessions[*].type
      RETURN doc

this is not correct, i need key and type in ONE session

maybe like this:

FOR doc IN collection 
  FILTER (
      FOR session in doc.sessions FILTER
         session.key == 324342 AND session.type == 'mobile' RETURN TRUE
  ) RETURN doc

no?

@dothebart
Copy link
Contributor

Hi,
If you want to ensure both are in the same sub object, you can do it like this:

FOR doc IN collection
   LET count=(FOR user IN doc.sessions FILTER
           session.key == 324342 AND session.type == 'mobile' RETURN)
   FILTER COUNT(count) > 0
     RETURN doc

However, you should note that this query can not make use of indices, which is going to limit scalability.

You should rather use one document per session, and join in the payload you need:

LET userSessions = (FOR sess IN sessions 
    FILTER  sess.key == 324342 AND sess.type == 'mobile' RETURN sess.userID)
 FOR doc IN collection FILTER doc.userID in userSessions RETURN doc

You then can then create an index on sessions.key and doc.userID which will give you acceptable execution times even if your data grows.

@baslr
Copy link
Contributor

baslr commented Jun 22, 2016

you could also use a graph to map userDoc -> session. Then the query is

LET userSessions = (
  FOR v IN 1..1 OUTBOUND 'userDoc._id' userToSessionEdges
  FILTER 324342 IN v.key AND 'mobile' IN v.type
  RETURN v)

@baslr
Copy link
Contributor

baslr commented Jun 22, 2016

@Simran-B how AQL handles it?
I am only aware of

FOR doc IN docs
FILTER doc['.a.b.c'] == 'foo' // doc..a.b.c -> Query: collection not found (a)
RETURN doc

@uMaxmaxmaximus
Copy link
Author

uMaxmaxmaximus commented Jun 22, 2016

It seems easier to create a 2 collections, Sessions and Users

sessionDoc = {
userId: String
key: String
}

session = await Sessions.getByExample({key:key})
user = await Users.get(session.userId)

It will be faster because of the indexing?

@Simran-B
Copy link
Contributor

Simran-B commented Jun 22, 2016

@baslr: without bind parameters:

doc.a.b.c
doc["a"]["b"]["c"]

With bind parameters:

doc.@x.@y.@z // {"x": "a", "y": "b", "z": "c"}
doc[@x][@y][@z] // {"x": "a", "y": "b", "z": "c"}
doc.@x // {"x": ["a", "b", "c"]}

Note that there is a problem with attributes that contain dots in 2.x. Above examples work in 3.x and you can also have dots in the attribute keys without problems.

@uMaxmaxmaximus: Another variant, that cannot use indexes either however, is to use inline filters. This can be handy to return just the matching sessions, e.g.

FOR doc IN asd
    LET session = doc.sessions[* FILTER CURRENT.key == 1234 AND CURRENT.type == "desktop"]
    FILTER LENGTH(session)
    RETURN MERGE(doc, {sessions: session})

@uMaxmaxmaximus
Copy link
Author

uMaxmaxmaximus commented Jun 22, 2016

how can I index user.sessions[*].key and make it unique?

@Simran-B
Copy link
Contributor

It's currently not possible to index sub-objects of array elements. You can index the individual elements of an array, but not a certain attribute of objects nested in the array. In order to index your data and add a unique constraint you will have to change your data model (pull out sessions into a separate collection and link them with users).

@uMaxmaxmaximus
Copy link
Author

uMaxmaxmaximus commented Jun 22, 2016

@Simran-B Look how elegant it is done in my ORM:

import Model from '../core/model'


class User extends Model {
    static schema = {
        name: String,
        age: Number,
    }

    async startSession() {
        let key = Math.random() + ''
        await Session.add({user: this, key: key})
        return key
    }

    static async getBySessionKey(key) {
        let session = await Session.findOne({key: key})
        return await session.user
    }
}


class Session extends Model {
    static schema = {
        key: String,
        user: User,
    }
}

Using

(async function () {
    let user = await User.add({name: 'Ашот', age: 3333})
    let key = await user.startSession()

    let usr = await User.getBySessionKey(key)
    console.log(usr) // tadaa
})()

What do you think? Elegantly? =)

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

No branches or pull requests

5 participants