ElasticSearch bool search using array - arrays

I would like to be able to exclude a number of nodes in my ElasticSearch query. Currently this code is working perfectly, but it will only exclude two nodes ($nodeId1 and $nodeId2).
How would I change this query so I can use an array which would contain as many node IDs as I want? Thank you.
$data_string = '{
"from" : 0, "size" : "' . $maximumResults . '",
"query": {
"filtered": {
"query": {
"match" : {
"thread.title" : {
"query" : "' . $threadTitle . '",
"operator": "or"
}
}
},
"filter": {
"bool" : {
"must" : [],
"must_not" : [
{
"term" : {"thread.discussion_id" : "' . $currentThreadId . '"}
},
{
"term" : {"thread.node" : "' . $nodeId1 . '"}
},
{
"term" : {"thread.node" : "' . $nodeId2 . '"}
}
],
"should" : []
}
}
}
}
}';

You can use the terms filter:
$data_string = '{
"from" : 0, "size" : "' . $maximumResults . '",
"query": {
"filtered": {
"query": {
"match" : {
"thread.title" : {
"query" : "' . $threadTitle . '",
"operator": "or"
}
}
},
"filter": {
"bool" : {
"must" : [],
"must_not" : [
{
"term" : {"thread.discussion_id" : "' . $currentThreadId . '"}
},
{
"terms" : {"thread.node" : ["' . $nodeId1 . '", "' . $nodeId2 . '", "' . $nodeId3 . '"}
}
],
"should" : []
}
}
}
}
}';

Related

Mongo matching only where first element of array has certain field value

I have a query below that extracts a couple of values from a large nested document. It tells me the user id and the first item name for each order.
This works fine, however I want it to only return the record where the first item's name is not null and is not blank. I can't figure out how to add a second query to the $match operator below to achieve this
db.getCollection('Orders').aggregate
([
{ $match : { "Items.1" : { $exists : true }}, ???},
{ $project: {
_id:0,
'UserId': '$User.EntityId',
'ItemName': {$arrayElemAt: ['$Items.Details.ItemName', 0]}
}
}
]);
Edited to show sample document
{
"_id" : "order-666156",
"State" : "ValidationFailed",
"LastUpdated" : {
"DateTime" : ISODate("2017-09-26T08:54:16.241Z"),
"Ticks" : NumberLong(636420128562417375)
},
"SourceOrderId" : "666156",
"User" : {
"EntityId" : NumberLong(34450),
"Name" : "Bill Baker",
"Country" : "United States",
"Region" : "North America",
"CountryISOCode" : "US",
},
"Region" : null,
"Currency" : null,
"Items" : [
{
"ClientOrderId" : "18740113",
"OrigClientOrderId" : "18740113",
"Quantity" : NumberDecimal("7487.0"),
"TransactDateTime" : {
"DateTime" : Date(-62135596800000),
"Ticks" : NumberLong(0)
},
"Text" : null,
"LocateRequired" : false,
"Details" : {
"ItemName" : "Test Item 1",
"ItemCost" : 1495.20
}
},
{
"ClientOrderId" : "18740116",
"OrigClientOrderId" : "18740116",
"Quantity" : NumberDecimal("241.0"),
"TransactDateTime" : {
"DateTime" : Date(-62135596800000),
"Ticks" : NumberLong(0)
},
"Text" : null,
"LocateRequired" : false,
"Details" : {
"ItemName" : "Test Item 2",
"ItemCost" : 2152.64
}
}
]
}
You need to add the two conditions to your existing $match (not null and not blank) to check the Items as:
$match : { "Items.1" : { $exists : true, "$ne": null,"$ne":""}
If you want to check the element Items[0].Details.ItemName you can doing using the operator $and
{ $match : {
$and: [
{"Items.1" : { $exists : true }},
{"Items.Details.ItemName" : { $ne : null}},
{"Items.Details.ItemName" : { $ne : ""}},
]
}},

MongoDB update $ update operator with multiple array selector

I'm trying to update an element inside an array using the $ update operator. The document contains 2 array fields and I have to query both to select the correct document.
The collection is called locations and the document looks like this:
{
"_id" : "XqEQYpitGFG3nnf3C",
"wallpapers" : [
{
"_metadata" : {
"master" : "vwb22W4MhkqtvAp89",
"isMaster" : false
},
"role" : "master",
"_id" : ""
},
{
"_metadata" : {
"master" : "vwb22W4MhkqtvAp89",
"isMaster" : false
},
"role" : "clone",
"_id" : ""
},
{
"_metadata" : {
"master" : "vwb22W4MhkqtvAp89",
"isMaster" : false
},
"role" : "pod",
"_id" : ""
}
],
"ancestors" : [
"vwb22W4MhkqtvAp89",
"tqzqfum9uMs47xcHW",
"b4d83aqTkq6TGvXts",
"XqEQYpitGFG3nnf3C"
]
}
The update operator looks like this:
db.getCollection('locations').update(
{
"ancestors": "b4d83aqTkq6TGvXts",
"wallpapers": {
"$elemMatch": {
"role": "clone",
"_metadata.master": "vwb22W4MhkqtvAp89"
}
}
},
{
"$set": {
"wallpapers.$": {
"_id": "D33WNZh7Bg4itPdhk",
"_metadata": {
"master": "b4d83aqTkq6TGvXts",
"isMaster": false
},
"role": "clone"
}
}
}
)
So I would like to have the element in the wallpapers array replaced. However, the result I get is:
{
"_id" : "XqEQYpitGFG3nnf3C",
"wallpapers" : [
{
"_metadata" : {
"master" : "vwb22W4MhkqtvAp89",
"isMaster" : false
},
"role" : "master",
"_id" : ""
},
{
"_metadata" : {
"master" : "vwb22W4MhkqtvAp89",
"isMaster" : false
},
"role" : "clone",
"_id" : ""
},
{
"_id" : "D33WNZh7Bg4itPdhk",
"_metadata" : {
"master" : "b4d83aqTkq6TGvXts",
"isMaster" : false
},
"role" : "clone"
}
],
"ancestors" : [
"vwb22W4MhkqtvAp89",
"tqzqfum9uMs47xcHW",
"b4d83aqTkq6TGvXts",
"XqEQYpitGFG3nnf3C"
]
}
So it replaces the wrong element.
It seems that the position .$ refers to is the one from the selector of the ancestors field.
Is this a bug or a limitation? Is there a solution (e.g. anything like .$1 and .$2 ?
I'm using MongoDB 3.2.6.
In your update operation query, use the dot notation as:
db.getCollection('locations').update(
{
"ancestors": "b4d83aqTkq6TGvXts",
"wallpapers.role": "clone", // <--- dot notation
"wallpapers._metadata.master": "vwb22W4MhkqtvAp89" // <-- dot notation
},
{
"$set": {
"wallpapers.$": {
"_id": "D33WNZh7Bg4itPdhk",
"_metadata": {
"master": "b4d83aqTkq6TGvXts",
"isMaster": false
},
"role": "clone"
}
}
}
)

Why I can't get the full document form array?

I have this document in stored in my collection:
{ "_id" : ObjectId("5707b95b8415b224a48a0b2d"),
"companyId" : ObjectId("570269639caabe24e4e4043e"),
"descriptions" : [
{ "id" : ObjectId("5707b95b8415b224a48a0b2a"), "description" : "test" },
{ "id" : ObjectId("570cd8164fff3a20f88c0dc9"), "description" : "test1" },
{ "id" : ObjectId("570ce6ba4fff3a052c8c570f"), "description" : "etr" },
{ "id" : ObjectId("570cf1b64fff3a1a14d71716"), "description" : "43" },
{ "id" : ObjectId("570cf1b64fff3a1a14d71717"), "description" : "43" },
{ "id" : ObjectId("570cf1b64fff3a1a14d71719"), "description" : "345" }
],
"options" : [
{ "descriptionId" : ObjectId("5707b95b8415b224a48a0b2a"), "description" : "test" },
{ "descriptionId" : ObjectId("5707b95b8415b224a48a0b2a"), "description" : "test1" }
]
}
Now I'm trying to get the objects from the options array that are matching the descriptionId and here is how I'm doing it
db.CustomFields.find({companyId: ObjectId("570269639caabe24e4e4043e")},{"options.descriptionId": ObjectId("5707b95b8415b224a48a0b2a")})
But the result contains only the descriptionId - the description property is missing.
here is how the result looks like:
{ "_id" : ObjectId("5707b95b8415b224a48a0b2d"),
"options" : [
{ "descriptionId" : ObjectId("5707b95b8415b224a48a0b2a") },
{ "descriptionId" : ObjectId("5707b95b8415b224a48a0b2a") }
]
}
Why my query is not returning the full document from the array, but only a part of it? Can you give me a push?
EDIT
This is what I'm expecting to get from the query
{ "_id" : ObjectId("5707b95b8415b224a48a0b2d"),
"options" : [
{ "descriptionId" : ObjectId("5707b95b8415b224a48a0b2a", "description" : "test") },
{ "descriptionId" : ObjectId("5707b95b8415b224a48a0b2a", "description" : "test1") }
]
}
You need to include the other query with "options.descriptionId" together with the companyId query and use projection to return just the array you want.
The following shows this:
db.customFields.find(
{
"companyId": ObjectId("570269639caabe24e4e4043e"),
"options.descriptionId": ObjectId("5707b95b8415b224a48a0b2a")
},
{ "options": 1 }
);
Output
{
"_id" : ObjectId("5707b95b8415b224a48a0b2d"),
"options" : [
{
"descriptionId" : ObjectId("5707b95b8415b224a48a0b2a"),
"description" : "test"
},
{
"descriptionId" : ObjectId("5707b95b8415b224a48a0b2a"),
"description" : "test1"
}
]
}
Try this
db.CustomFields.find({companyId: ObjectId("570269639caabe24e4e4043e"),"options.descriptionId": ObjectId("5707b95b8415b224a48a0b2a")})

Join elasticsearch indices while matching fields in nested/inner objects

I am trying to join 2 elasticsearch indices by using terms filter lookup. I referred to http://www.elasticsearch.org/blog/terms-filter-lookup/ and http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-terms-filter.html. These Examples lookup on an array of fields like "followers" : ["1", "3"] and join works fine for similar data.
My requirement is to join with a field inside an array of objects. When I extend the above example to include an array of objects, my query fails.
Following is the sample data:
PUT /users/user/2 {
"followers" : [
{
"userId":"1",
"username":"abc",
"location":"xyz"
},
{
"userId":"3",
"username":"def",
"location":"xyz"
}
}
]
}
PUT /tweets/tweet/1 {
"user" : "2"
}
PUT /tweets/tweet/2 {
"user" : "1"
}
I am now trying to find tweets that are created by followers of user 2
POST /tweets/_search {
"query" : {
"filtered" : {
"filter" : {
"terms" : {
"user" : {
"index" : "users",
"type" : "user",
"id" : "2",
"path" : "followers.userId"
},
"_cache_key" : "user_2_friends"
}
}
}
}
}
My search results are 0 for above query. I tried 2 other approaches as well 1)declare the followers object as a nested object during mapping and use "nested" in the query, 2)tried to add a match query for followers.userId after giving path as "followers". None yielded results.
Does terms filter lookup support array of objects? Any pointers to solving my problem would be of great help
What you're trying to do worked for me, unless I'm missing something. What version of Elasticsearch are you using? I'm using 1.3.4.
So I created both indices and added the docs you have listed:
curl -XPUT "http://localhost:9200/users"
curl -XPUT "http://localhost:9200/users/user/2 " -d '
{
"followers" : [
{
"userId":"1",
"username":"abc",
"location":"xyz"
},
{
"userId":"3",
"username":"def",
"location":"xyz"
}
]
}'
curl -XPUT "http://localhost:9200/tweets"
curl -XPUT "http://localhost:9200/tweets/tweet/1 " -d'
{
"user" : "2"
}'
curl -XPUT "http://localhost:9200/tweets/tweet/2 " -d'
{
"user" : "1"
}'
then ran your search query:
curl -XPOST "http://localhost:9200/tweets/_search " -d'
{
"query": {
"filtered": {
"filter": {
"terms": {
"user": {
"index": "users",
"type": "user",
"id": "2",
"path": "followers.userId"
},
"_cache_key": "user_2_friends"
}
}
}
}
}'
and got back this result:
{
"took": 2,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 1,
"hits": [
{
"_index": "tweets",
"_type": "tweet",
"_id": "2",
"_score": 1,
"_source": {
"user": "1"
}
}
]
}
}
Here is the code I used:
http://sense.qbox.io/gist/4a2a2d77d0b6f4502ff6c5022b268acfa65ee6d2
Clear the indices if you have any
curl -XDELETE "http://example.com:9200/currencylookup/"
curl -XDELETE "http://example.com:9200/currency/"
Create the lookup table
curl -XPUT http://example.com:9200/currencylookup/type/2 -d '
{ "conv" : [
{ "currency":"usd","username":"abc", "location":"USA" },
{ "currency":"inr", "username":"def", "location":"India" },
{ "currency":"IDR", "username":"def", "location":"Indonesia" }]
}'
Lets put some dummy docs
curl -XPUT "http://example.com:9200/currency/type/USA" -d '{ "amount":"100", "currency":"usd", "location":"USA" }'
curl -XPUT "http://example.com:9200/currency/type/JPY" -d '{ "amount":"50", "currency":"JPY", "location":"JAPAN" }'
curl -XPUT "http://example.com:9200/currency/type/INR" -d '{ "amount":"50", "currency":"inr", "location":"INDIA" }'
curl -XPUT "http://example.com:9200/currency/type/IDR" -d '{ "amount":"30", "currency" : "IDR", "location": "Indonesia" }'
Time to check the output
curl http://example.com:9200/currency/_search?pretty -d '{
"query" : {
"filtered" : {
"filter" : {
"terms" : {
"currency" : {
"index" : "currencylookup",
"type" : "type",
"id" : "2",
"path" : "conv.currency"
},
"_cache_key" : "currencyexchange"
}
}
}
}
}'
Results
# curl http://example.com:9200/currency/_search?pretty -d '{
"query" : {
"filtered" : {
"filter" : {
"terms" : {
"currency" : {
"index" : "currencylookup",
"type" : "type",
"id" : "2",
"path" : "conv.currency"
},
"_cache_key" : "currencyexchange"
}
}
}
}
}'
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits" : {
"total" : 2,
"max_score" : 1.0,
"hits" : [ {
"_index" : "currency",
"_type" : "type",
"_id" : "INR",
"_score" : 1.0,
"_source":{ "amount":"50", "currency":"inr", "location":"INDIA" }
}, {
"_index" : "currency",
"_type" : "type",
"_id" : "USA",
"_score" : 1.0,
"_source":{ "amount":"100", "currency":"usd", "location":"USA" }
} ]
}
}
Conclusion
Capital letters are culprit here.
You can see 'IDR' is in caps so the match is failed for it and 'JPY' is not in look up even if it was there it would not have got matched because it is in caps.
cross matching values must be in small letters or numbers like
eg:
abc
1abc

MongoDB find the intersection of arrays

docs:
order1.filter = ['tag1','tag2']
order2.filter = ['tag1','tag2','tag3']
want to get:
query ['tag1','tag2'] -> (only order1)
query ['tag1','tag2','tag3'] -> (order1 and order2)
query ['tag1','tag2','tag3','tag4', etc ] -> (order1 and order2)
and
query ['tag1','tag3'] -> (null)
query ['tag2','tag3'] -> (null)
All values ​​order.filter should be necessarily in the query array
How to do it? Tried directives $all, $in :(
You can do this with aggregation framework (there is no way to do this with regular find that I know of).
I think this is basically a duplicate so adjusting that code for your fields you get something like:
//sample documents:
> db.docs.find({},{_id:0})
{ "order" : 1, "filter" : [ "t1", "t2" ] }
{ "order" : 2, "filter" : [ "t1", "t2", "t3" ] }
var tagArray = [ "t1", "t2" ]; // array to "match"
db.docs.aggregate( [
{
"$project" : {
"order" : 1,
"filter" : 1,
"killFlag" : {
"$const" : [
true,
false
]
}
}
},
{
"$unwind" : "$filter"
},
{
"$unwind" : "$killFlag"
},
{
"$match" : {
"$nor" : [
{
"filter" : {
"$in" : tagArray
},
"killFlag" : true
}
]
}
},
{
"$group" : {
"_id" : "$order",
"filter" : {
"$addToSet" : "$filter"
},
"killFlag" : {
"$max" : "$killFlag"
}
}
},
{
"$match" : {
"killFlag" : false
}
},
{
"$project" : {
"_id" : 1,
"filter" : 1
}
}
]);

Resources