简体   繁体   中英

MongoDB search in embedded array

I am trying to store all my written java files in a MongoDB and so far I've applied a schema like this (incomplete entry):

{
"_id" : ObjectId("52b861c230044fd08d6c27c4"),
"interfaces" : [
    {
        "methodInterfaces" : [
            {
                "name" : "add",
                "name_lc" : "add",
                "returnType" : "Integer",
                "returnType_lc" : "integer",
                "parameterTypes" : [
                    "Integer",
                    "Integer"
                ],
                "parameterTypes_lc" : [
                    "integer",
                    "integer"
                ]
            },
            {
                "name" : "isValid",
                "name_lc" : "isvalid",
                "returnType" : "Boolean",
                "returnType_lc" : "boolean",
                "parameterTypes" : [
                    "Integer",
                    "Double"
                ],
                "parameterTypes_lc" : [
                    "integer",
                    "double"
                ]
            }
        ],
        "name" : "Calculator",
        "name_lc" : "calculator",
        "filename" : "Calculator.java",
        "filename_lc" : "calculator.java"
    }
],
"name" : "Calculator",
"name_lc" : "calculator",
"filename" : "Calculator",
"filename_lc" : "calculator",
"path" : "/xyz/Calculator.java",
"md5" : "6dec7e62c5e4f9060c7612c252cd741",
"lastModification" : ""

}

So far I am able to query a class that contains a method name, but I am not able to query a class with a certain name (let interfaces.name_lc="calculator") that must contain two methods with particular names (let's say "add" and "divide") which themselves should have two integer, resp. an integer and a double as parameters and both return an integer (don't question whether this is reasonable or not -- just for illustration purposes).

This is just one example; it can be more complex, of course.

I don't know how I can query for a particular class with method and specified parameters. I need to describe it sharp and want sharp results.

I am not able to construct a query, that would only return files like Calculator ( add(integer,integer):integer; divide(integer,double):integer; ) . I get, eg, OtherClass ( add():void; method(integer):integer; ) , which is not what I want. I am trying this for days now, and maybe one can enlighten me, how to solve this in MongoDB. Thanks a lot in advance!

I'm not sure you'll be able to do this in MongoDB with your document structure. The issue I ran into is around the parameters - I'm assuming you care about the order of the parameters (ie doSomething(String, int) is not the same as doSomething(int, String) ), and the query operators to check all the values in an array treat the array as a set, so a) order agnostic and b) eliminates duplicates (so doSomething(String, String) matches doSomething(String) ) (this is because I was using the $all keyword, see the documentation , especially the note at the bottom).

I managed to get a large part of the query you wanted, however, which might point you in the right direction.

{ "$and" : [ //putting these in an "and" query means all parts have to match
    { "interfaces.methodInterfaces" : 
        { "$elemMatch" : { "name" : "add"}}           //this bit finds documents where the method name is "add"
    } ,
    { "interfaces.methodInterfaces" : 
        { "$elemMatch" : { "returnType" : "Integer"}} // This bit matches the return type
    } , 
    { "interfaces.methodInterfaces.parameterTypes" : 
        { "$all" : [ "Integer" , "Integer"]}          //This *should* find you all documents where the parameter types matches this array. But it doesn't, as it treats it as a set
    }
]}

If you're querying via the Java driver, this looks like:

BasicDBObject findMethodByName = new BasicDBObject("interfaces.methodInterfaces",
                                     new BasicDBObject("$elemMatch", new BasicDBObject("name", "add")));
BasicDBObject findMethodByReturn = new BasicDBObject("interfaces.methodInterfaces",
                                       new BasicDBObject("$elemMatch", new BasicDBObject("returnType", "Integer")));
BasicDBObject findMethodByParams = new BasicDBObject("interfaces.methodInterfaces.parameterTypes",
                                       new BasicDBObject("$all", asList("Integer", "Integer")));
BasicDBObject query = new BasicDBObject("$and", asList(findMethodByName, findMethodByReturn, findMethodByParams));
DBCursor found = collection.find(query);

I haven't included matching the class name, as this didn't seem to be the tricky part - just build up another simple query for that an add it into the "$and".

Since the arrays to store parameter types are not giving what you want, I suggest you think about something a little more structured, although it's a bit unwieldy. Instead of

"parameterTypes" : [
    "Integer",
    "Integer"
]

Consider something like

"parameterTypes" : {
    "param1" : "Integer",
    "param2" : "Integer"
}

Then you won't be doing set operations, you can query each parameter individually. This means you'll also get them in the correct order.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM