Nested query not working in Cosmos DB more than 1 level deep - mongodb-query

I can't get Cosmos DB to find a record more than 1 level deep in a query:
Document:
{
"CustomerId": 1,
"Orders": [
{
"OrderId": 1,
"OrderCode": "ONE",
"OrderName": "Order One",
"Products": [
{
"ProductCode": "ONE",
"ProductName": "Product One"
}
]
}
]
}
Query: db.customers.find({ "Orders.Products.ProductCode": "ONE" })
It works in MongoDB shell, but not in CosmosDB. Am I missing something obvious?

I think the key of the issue is that there are nested arrays in your document, and the query you provide may only apply to the following document:
"CustomerId" : 1,
"Orders" : {
"OrderId" : 1,
"OrderCode" : "ONE",
"OrderName" : "Order One",
"Products" : {
"ProductCode" : "ONE",
"ProductName" : "Product One"
}
}
You could refer to MongoDB official documentation and use $elemmatch operator to query fields in nested documents.
query:
{ "Orders": { $elemMatch: { Products : {$elemMatch : {ProductCode : "ONE" } } } } }
Hope it helps you.

Cosmosdb does not have something to query in nested document. you need to use ARRAY_CONTAINS built-in function. So it will be something like this:
SELECT c.Orders.Products.ProductCode, c.Orders.Products.ProductName
FROM c
WHERE c.Orders.Products.ProductCode = "ONE" or ARRAY_CONTAINS(c.Orders.Products, { "ProductCode": "ONE" })

Related

Using $or selector, There is no index available for this selector

I'd like to retrieve
document with _id of 1
OR
document with value === 13 AND anotherValue === 56
Error:
There is no index available for this selector.
This is my query:
{
"selector": {
"$or": [
{
"_id": "1"
},
{
"value": "13",
"anotherValue": "56"
}
]
}
}
Indexes setup:
Your available Indexes:
special: _id
json: value, anotherValue
json: _id, value, anotherValue
For this query you need to add a selector to get all the IDs like so:
{
"selector": {
"_id": {"$gt":null},
"$or": [
{
"_id": "1"
},
{
"value": "13",
"anotherValue": "56"
}
]
}
}
You can learn more here:
https://cloudant.com/blog/mango-json-vs-text-indexes/
And this SO post:
index and query items in an array with mango query for cloudant and couchdb 2.0
Alternatively, you can add a text index on all fields:
{
"index": {},
"type": "text"
}
And then your original selector should work.

Sorting ElasricSearch based on size of array type field

I have a ElasticSearch cluster on which I have to perform a sort query based on the size of the object array field 'contents'.
So far I have tried,
{
"size": 10,
"from": 0,
"fields" : ['name'],
"query": {
"match_all": {}
},
"sort" : {
"script" : {
"script" : "doc['contents'].values.length",
"order": "desc"
}
}
}
The above query gives me SearchPhaseExecutionException. The ES query is made from client side using elasticsearch.angular.js.
Any kind of help will be appreciate.
The security has changed for scripts in versions 1.2.x. In ES_HOME/config/scripts create a file called script_score.mvel and add the script:
doc.containsKey('content') == false ? 0 : doc['content'].values.size()
Restart Elasticsearch and change your query to:
{
"size": 10,
"from": 0,
"query": {
"match_all": {}
},
"sort": {
"_script": {
"script": "script_score",
"order": "desc",
"type" : "string"
}
}
}
For more information take a look here:
http://www.elasticsearch.org/blog/scripting-security/

SQL to mongodb conversion

I have two fields in mongodb, A and B
I would like to perform the following sql query in mongo
SELECT DISTINCT A FROM table WHERE B LIKE 'asdf'
EDIT for clarification
foo ={
bar: [{
baz:[
‘one’,
‘two'
]
},{...}
]
}
I would like to select distinct foo objects where bar.baz contains ‘one’.  
The query:
db.runCommand({
    "distinct": "foo",
    "query": {
        “bar.baz": “one"
    },
    "key": “bar.baz"
});
This query, oddly enough, returns foo objects who's bar.baz /doesnt/ contain ‘one’.
There seems to be a misunderstanding here of how the MongoDB distinct command works or indeed how any query works with arrays.
I am going to consider that you actually have documents that look something like this:
{
"_id" : ObjectId("5398f8bf0b5d1b43d3e26816"),
"bar" : [
{
"baz" : [
"one",
"two"
]
},
{
"baz" : [
"three"
]
},
{
"baz" : [
"one",
"four"
]
}
]
}
So the query that you have run, and these two forms are equivalent:
db.runCommand({
"distinct": "foo",
"query": { "bar.baz": "one" },
"key": "bar.baz"
})
db.foo.distinct("bar.baz", { "bar.baz": "one" })
Returns essentially this:
[ "four", "one", "three", "two" ]
Why? Well, because you asked it to. Let's consider a declarative way of describing what you actually invoked.
Your "query" essentially says 'Find me all the "documents" that have "bar.baz" equal to "one" ' then you are asking 'And return me all of the "distinct" values for "bar.baz"
So the "query" part of your statement does exactly that, and matched "documents" and not array members that match the value you specified. In the above example you are then asking for the "distinct" values of "bar.baz", which is exactly what you get, with there only being the value of "one" returned once from all of the values of "bar.baz".
So "query" statements do not "filter" array contents they just "match" where the condition exists. The above document matches the condition and "bar.baz" has a value of "one", and twice even. So selecting the distinct "foo" or basically the document is really:
db.foo.find({ "bar.baz": "one" })
Matching all documents that meet the condition. This is how embedding works, but perhaps you wanted something like filtering the results. So looking at returning only those items of "bar" whose "baz" has a value of "one" you would do:
db.collection.aggregate([
// Matches documents
{ "$match": { "bar.baz": "one" } },
// Unwind to de-normalize arrays as documents
{ "$unwind": "$bar" },
// Match to "filter" documents without "bar.baz" matching "one"
{ "$match": { "bar.baz": "one" } },
// Maybe group back to document with the array
{ "$group": {
"_id": "$_id",
"bar": { "$push": "$bar" }
}}
])
The result of this .aggregate() statement is the document without the member of "bar" that does not contain "one" under "baz":
{
"_id" : ObjectId("5398f8bf0b5d1b43d3e26816"),
"bar" : [
{
"baz" : [
"one",
"two"
]
},
{
"baz" : [
"one",
"four"
]
}
]
}
But then suppose you actually want just the element "bar.baz" equal to "one" and the total count of those occurrences over your whole collection, then you would want to do this:
db.collection.aggregate([
// Matches documents
{ "$match": { "bar.baz": "one" } },
// Unwind to de-normalize arrays as documents
{ "$unwind": "$bar" },
// And the inner array as well
{ "$unwind": "$bar.baz" },
// Then just match and filter out everything but the matching items
{ "$match": { "bar.baz": "one" } },
// Group to get the count
{ "$group": {
"_id": "$bar.baz",
"count": { "$sum": 1 }
}}
])
And from our single document collection sample you get:
{ "_id": "one", "count": 2 }
As there are two occurrences of that matching value.
As for your SQL at the head of your question, that really doesn't apply to this sort of data. The more practical example would be something with data like this:
{ "A": "A", "B": "BASDFJJ" }
{ "A": "A", "B": "ASDFTT" }
{ "A": "B", "B": "CASDF" }
{ "A": "B", "B": "DKITB" }
So the "distinct" values of "A" where "B" is like "ASDF", again using aggregate and noting you are not wildcarding on either side:
db.foo.aggregate([
{ "$match": { "B": "ASDF" } },
{ "$group": { "_id": "$A" } }
])
Which essentially produces:
{ "_id": "A" }
Or with wildcards on either side "%ASDF%" this is a $regex query to match:
db.foo.aggregate([
{ "$match": { "B": { "$regex": "ASDF" } } },
{ "$group": { "_id": "$A" } }
])
So only two results:
{ "_id": "A" }
{ "_id": "B" }
Where if you were "counting" the distinct matches then you would see 2 and 1 as the counts respectively according to the documents that matched.
Take a further look at the SQL Mapping Chart and the SQL to Aggregation Mapping Chart contained within the documentation. It should help you in understanding how common actions actually translate.

ElasticSearch for Attribute(Key) value data set

I am using Elasticsearch with Haystacksearch and Django and want to search the follow structure:
{
{
"title": "book1",
"category" : ["Cat_1", "Cat_2"],
"key_values" :
[
{
"key_name" : "key_1",
"value" : "sample_value_1"
},
{
"key_name" : "key_2",
"value" : "sample_value_12"
}
]
},
{
"title": "book2",
"category" : ["Cat_3", "Cat_2"],
"key_values" :
[
{
"key_name" : "key_1",
"value" : "sample_value_1"
},
{
"key_name" : "key_3",
"value" : "sample_value_6"
},
{
"key_name" : "key_4",
"value" : "sample_value_5"
}
]
}
}
Right now I have set up an index model using Haystack with a "text" that put all the data together and runs a full text search! In my opinion this is not the a well established search 'cause I am not using my data set structure and hence this is some kind odd.
As an example if for an object I have a key-value
{
"key_name": "key_1",
"value": "sample_value_1"
}
and for another object I have
{
"key_name": "key_2",
"value": "sample_value_1"
}
and we it gets a query like "Key_1 sample_value_1" comes I get a thoroughly mixed result of objects who have these words in their fields rather than using their structures.
P.S. I am totally new to ElasticSearch and better to say new to the search technologies and challenges. I have searched the web and SO button didn't find anything satisfying. Please let me know if there is something wrong with my thoughts and expectations from these search engines and if there is SO duplicate question! And also if there is a better approach to design a database for this kind of search
Read the es docs on nested mappings and do something like this:
"book_type" : {
"properties" : {
// title, cat mappings
"key_values" : {
"type" : "nested"
"properties": {
"key_name": {
"type": "string", "index": "not_analyzed"
},
"value": {
"type": "string"
}
}
}
}
}
Then query using a nested query
"nested" : {
"path" : "key_values",
"query" : {
"bool" : {
"must" : [
{
"term" : {"key_values.key_name" : "key_1"}
},
{
"match" : {"key_values.value" : "sample_value_1"}
}
]
}
}
}

Flattening af collection to properties on document in RavenDB

I have some documents in RavenDB with a collection of attributes, which I would like to flatten using an index.
The document structure is something similar to:
{
"Id": "test/1",
"Name": "Some name",
"Attributes": [
{ "Key": "FirstAttr", "Value": "FirstValue" },
{ "Key": "SecondAttr", "Value": "SecondValue" }
]}
My desired output is:
{
"Id": "test/1",
"Name": "Some name",
"FirstAttr": "FirstValue",
"SecondAttr": "SecondValue"
}
Is this possible in RavenDB?
Thanks a bunch!
Ok - answering my own question so others can benefit:
The CreateField method seems to be what I need in the map part in my index:
from t in docs.Test
select new {
t.Id,
t.Name,
_ = t.Attributes.Select(x => this.CreateField(x.Key, x.Value, true, true))
}
Can you do this during the map-reduce?
Map = docs =>
from doc in docs
select new {
Id = doc.Id,
Name = doc.Name,
FirstAttr = (doc.Attributes.ContainsKey("FirstAttr") ? doc.Attributes["FirstAttr"] : null,
SecondAttr = (doc.Attributes.ContainsKey("SecondAttr") ? doc.Attributes["SecondAttr"] : null
};