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

MongoDB: support $elemMatch feature #115

Closed
rocketraman opened this issue Mar 10, 2012 · 13 comments
Closed

MongoDB: support $elemMatch feature #115

rocketraman opened this issue Mar 10, 2012 · 13 comments

Comments

@rocketraman
Copy link
Contributor

@rocketraman rocketraman commented Mar 10, 2012

In MongoDB, if one wishes to apply multiple criteria to embedded documents within an array, one has to use the $elemMatch feature. See "Matching with $elemMatch" at http://www.mongodb.org/display/DOCS/Dot+Notation+%28Reaching+into+Objects%29.

It would be cool if QueryDSL was able to support this query notation.

A good example is given at the bottom of the page above:

// Document 1
{ "foo" : [
      {
        "shape" : "square",
        "color" : "purple",
        "thick" : false
      },
      {
        "shape" : "circle",
        "color" : "red",
        "thick" : true
      }
] }


// Document 2
{ "foo" : [
      {
        "shape" : "square",
        "color" : "red",
        "thick" : true
      },
      {
        "shape" : "circle",
        "color" : "purple",
        "thick" : false
      }
] }

In order to query for a document containing a purple square, the mongodb query is:

db.foo.find({foo: {"$elemMatch": {shape: "square", color: "purple"}}})

@timowest
Copy link
Member

@timowest timowest commented Mar 10, 2012

This query notation can't be supported directly, but we could figure out what would be an intuitive Querydsl way to express conditions that map in to $elemMatch

The collection.any() doesn't work since it creates always a new alias

The problem with join(...) is that implies maybe something like a reference in Mongodb.

@rocketraman
Copy link
Contributor Author

@rocketraman rocketraman commented Mar 11, 2012

Yeah, I'm not sure what would fit best into the existing DSL.

Perhaps something like: joinNested(...) ?

@timowest
Copy link
Member

@timowest timowest commented Mar 11, 2012

It appears the constraints also need to be in one place, so maybe something like this

joinNested(doc.foo, foo).on(foo.shape.eq("square"), foo.color.eq("purple"))

This should be quite transparent, but is it intuitive?

@rocketraman
Copy link
Contributor Author

@rocketraman rocketraman commented Mar 11, 2012

  1. joinNested is a bit confusing. How about anyEmbedded, which seems closer to the similar any syntax used by collections.
  2. I guess the second parameter to joinNested/anyEmbedded is an alias? If the user needs to specify the typed fields from the Q-class, is the alias useful?

How about this:

anyEmbedded(doc.foo).on(doc.foo.shape.eq("square"), doc.foo.color.eq("purple"))

(where I guess doc is a static import on QDoc)

@timowest
Copy link
Member

@timowest timowest commented Mar 11, 2012

I guess an alias is needed, because foo is a Collection typed property.

Maybe

QDoc doc = QDoc.doc;
// where doc.foo property is of type Collection<Foo>
anyEmbedded(doc.foo, foo).on(foo.shape.eq("square"), foo.color.eq("purple"))

or

anyEmbedded(doc.foo).on(doc.foo.any().shape.eq("square"), doc.foo.any().eq("purple"))
@rocketraman
Copy link
Contributor Author

@rocketraman rocketraman commented Mar 11, 2012

The first of those seems much clearer to me -- the second seems to have too many redundant "any"'s. However, in the first, where does the foo variable come from in the parameters to the on method?

anyEmbedded(doc.foo, foo).on(foo.shape.eq("square"), foo.color.eq("purple"))
                             ^^^                     ^^^
@timowest
Copy link
Member

@timowest timowest commented Mar 11, 2012

I also prefer the first one, the second was just an example how it would look like without extra variables. The used variables are

QDoc doc = QDoc.doc;
QFoo foo = QFoo.foo;

You can't used QDoc.doc.foo directly, since it is Collection typed. At least if I understand the elemMatch semantics correctly.

@rocketraman
Copy link
Contributor Author

@rocketraman rocketraman commented Mar 11, 2012

Ok, that makes sense.

timowest added a commit that referenced this issue Mar 25, 2012
@timowest
Copy link
Member

@timowest timowest commented Mar 25, 2012

Here is a related test case:

@Test
public void ElemMatch() {
    // { "addresses" : { "$elemMatch" : { "street" : "Aakatu1"}}}
    assertEquals(1, query().anyEmbedded(user.addresses, address).on(address.street.eq("Aakatu1")).count());
    // { "addresses" : { "$elemMatch" : { "street" : "Aakatu1" , "postCode" : "00100"}}}
    assertEquals(1, query().anyEmbedded(user.addresses, address).on(address.street.eq("Aakatu1"), address.postCode.eq("00100")).count());
    // { "addresses" : { "$elemMatch" : { "street" : "akatu"}}}
    assertEquals(0, query().anyEmbedded(user.addresses, address).on(address.street.eq("akatu")).count());
    // { "addresses" : { "$elemMatch" : { "street" : "Aakatu1" , "postCode" : "00200"}}}
    assertEquals(0, query().anyEmbedded(user.addresses, address).on(address.street.eq("Aakatu1"), address.postCode.eq("00200")).count());
}
@rocketraman
Copy link
Contributor Author

@rocketraman rocketraman commented Mar 27, 2012

Sweet.

@timowest
Copy link
Member

@timowest timowest commented Apr 20, 2012

Released in 2.5.0

@timowest timowest closed this Apr 20, 2012
@rocketraman
Copy link
Contributor Author

@rocketraman rocketraman commented Nov 22, 2012

Spring Data MongoDB uses the javax.persistence.Embedded annotation... I notice the test case is using the Morphia annotation. Is it a change in QueryDSL to recognize the javax.persistence.Embedded annotation as well?

@svmgarg
Copy link

@svmgarg svmgarg commented Dec 26, 2018

  1. joinNested is a bit confusing. How about anyEmbedded, which seems closer to the similar any syntax used by collections.
  2. I guess the second parameter to joinNested/anyEmbedded is an alias? If the user needs to specify the typed fields from the Q-class, is the alias useful?

How about this:

anyEmbedded(doc.foo).on(doc.foo.shape.eq("square"), doc.foo.color.eq("purple"))

(where I guess doc is a static import on QDoc)

What if doc.foo is a map and properties like "shape", "color","thick" are map key's then you won't be able to do doc.foo.shape anymore.
Also, I am using Predicate and in that i don't see anyEmbedded.
Please find stackoverflow question.
https://stackoverflow.com/questions/53929308/filter-over-map-values-in-spring-data-mongo-query-dsl

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

Successfully merging a pull request may close this issue.

None yet
3 participants