org.alfresco.solr.query.DistributedAlfrescoSolrFingerPrintTest.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.solr.query.DistributedAlfrescoSolrFingerPrintTest.java

Source

/*
 * Copyright (C) 2005-2014 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */
package org.alfresco.solr.query;

import static java.util.Arrays.asList;
import static org.alfresco.solr.AlfrescoSolrUtils.getAcl;
import static org.alfresco.solr.AlfrescoSolrUtils.getAclChangeSet;
import static org.alfresco.solr.AlfrescoSolrUtils.getAclReaders;
import static org.alfresco.solr.AlfrescoSolrUtils.getNode;
import static org.alfresco.solr.AlfrescoSolrUtils.getNodeMetaData;
import static org.alfresco.solr.AlfrescoSolrUtils.getTransaction;
import static org.alfresco.solr.AlfrescoSolrUtils.indexAclChangeSet;
import static java.util.Collections.singletonList;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.alfresco.model.ContentModel;
import org.alfresco.repo.search.adaptor.lucene.QueryConstants;
import org.alfresco.solr.AbstractAlfrescoDistributedTest;
import org.alfresco.solr.client.Acl;
import org.alfresco.solr.client.AclChangeSet;
import org.alfresco.solr.client.AclReaders;
import org.alfresco.solr.client.Node;
import org.alfresco.solr.client.NodeMetaData;
import org.alfresco.solr.client.StringPropertyValue;
import org.alfresco.solr.client.Transaction;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.LegacyNumericRangeQuery;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.util.LuceneTestCase;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

/**
 * @author Joel
 */
@SolrTestCaseJ4.SuppressSSL
@LuceneTestCase.SuppressCodecs({ "Appending", "Lucene3x", "Lucene40", "Lucene41", "Lucene42", "Lucene43",
        "Lucene44", "Lucene45", "Lucene46", "Lucene47", "Lucene48", "Lucene49" })
public class DistributedAlfrescoSolrFingerPrintTest extends AbstractAlfrescoDistributedTest {
    private static long MAX_WAIT_TIME = 80000;

    private static Node[] NODES = new Node[4];
    private static NodeMetaData[] NODES_METADATA = new NodeMetaData[4];
    private static Acl ACL;

    @BeforeClass
    private static void initData() throws Throwable {
        initSolrServers(2, getClassName(), null);

        AclChangeSet aclChangeSet = getAclChangeSet(1);

        ACL = getAcl(aclChangeSet);
        Acl acl2 = getAcl(aclChangeSet);

        AclReaders aclReaders = getAclReaders(aclChangeSet, ACL, singletonList("joel"), singletonList("phil"),
                null);
        AclReaders aclReaders2 = getAclReaders(aclChangeSet, acl2, singletonList("jim"), singletonList("phil"),
                null);

        indexAclChangeSet(aclChangeSet, asList(ACL, acl2), asList(aclReaders, aclReaders2));

        //Check for the ACL state stamp.
        BooleanQuery.Builder builder = new BooleanQuery.Builder();
        builder.add(new BooleanClause(new TermQuery(new Term(QueryConstants.FIELD_SOLR4_ID, "TRACKER!STATE!ACLTX")),
                BooleanClause.Occur.MUST));
        builder.add(new BooleanClause(LegacyNumericRangeQuery.newLongRange(QueryConstants.FIELD_S_ACLTXID,
                aclChangeSet.getId(), aclChangeSet.getId() + 1, true, false), BooleanClause.Occur.MUST));
        BooleanQuery waitForQuery = builder.build();
        waitForDocCountAllCores(waitForQuery, 1, 80000);

        Transaction txn = getTransaction(0, 4);

        //Next create two NODES to update for the transaction
        NODES[0] = getNode(txn, ACL, Node.SolrApiNodeStatus.UPDATED);
        NODES[1] = getNode(txn, ACL, Node.SolrApiNodeStatus.UPDATED);
        NODES[2] = getNode(txn, ACL, Node.SolrApiNodeStatus.UPDATED);
        NODES[3] = getNode(txn, ACL, Node.SolrApiNodeStatus.UPDATED);

        //Next create the NodeMetaData for each node. TODO: Add more metadata
        NODES_METADATA[0] = getNodeMetaData(NODES[0], txn, ACL, "mike", null, false);
        NODES_METADATA[1] = getNodeMetaData(NODES[1], txn, ACL, "mike", null, false);
        NODES_METADATA[2] = getNodeMetaData(NODES[2], txn, ACL, "mike", null, false);
        NODES_METADATA[3] = getNodeMetaData(NODES[3], txn, ACL, "mike", null, false);

        List<String> content = new ArrayList<>();
        int[] sizes = { 2000, 1000, 1500, 750 };

        Random r = new Random(1);
        String token1 = Integer.toString(Math.abs(r.nextInt()));

        for (int i = 0; i < 4; i++) {
            Random rand = new Random(1);
            StringBuilder buf = new StringBuilder();
            int size = sizes[i];
            for (int s = 0; s < size; s++) {
                if (s > 0) {
                    buf.append(" ");
                }
                buf.append(Math.abs(rand.nextInt()));
            }
            content.add(buf.toString());
        }

        //Index the transaction, NODES, and nodeMetaDatas.
        //Note that the content is automatically created by the test framework.
        indexTransaction(txn, asList(NODES[0], NODES[1], NODES[2], NODES[3]),
                asList(NODES_METADATA[0], NODES_METADATA[1], NODES_METADATA[2], NODES_METADATA[3]), content);

        //Check for the TXN state stamp.
        builder = new BooleanQuery.Builder();
        builder.add(new BooleanClause(new TermQuery(new Term(QueryConstants.FIELD_SOLR4_ID, "TRACKER!STATE!TX")),
                BooleanClause.Occur.MUST));
        builder.add(new BooleanClause(LegacyNumericRangeQuery.newLongRange(QueryConstants.FIELD_S_TXID, txn.getId(),
                txn.getId() + 1, true, false), BooleanClause.Occur.MUST));
        waitForQuery = builder.build();

        waitForDocCountAllCores(waitForQuery, 1, 80000);

        /*
         * Query the index for the content
         */

        waitForDocCountAllCores(new TermQuery(new Term(QueryConstants.FIELD_READER, "jim")), 1, 80000);
        waitForDocCount(
                new TermQuery(new Term("content@s___t@{http://www.alfresco.org/model/content/1.0}content", token1)),
                4, 80000);
    }

    @AfterClass
    private static void destroyData() {
        dismissSolrServers();
    }

    @Test
    public void testFingerPrint() throws Exception {
        putHandleDefaults();
        QueryResponse response = query(getDefaultTestClient(), true,
                "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [\"joel\"], \"tenants\": []}",
                params("q", "FINGERPRINT:" + NODES[0].getId(), "qt", "/afts", "shards.qt", "/afts", "start", "0",
                        "fl", "DBID,score", "rows", "100"));

        SolrDocumentList docs = response.getResults();
        assertEquals(4, docs.getNumFound());
        SolrDocument doc0 = docs.get(0);
        long dbid0 = (long) doc0.getFieldValue("DBID");
        assertEquals(dbid0, NODES[0].getId());

        SolrDocument doc1 = docs.get(1);
        long dbid1 = (long) doc1.getFieldValue("DBID");
        assertEquals(dbid1, NODES[2].getId());

        SolrDocument doc2 = docs.get(2);
        long dbid2 = (long) doc2.getFieldValue("DBID");
        assertEquals(dbid2, NODES[1].getId());

        SolrDocument doc3 = docs.get(3);
        long dbid3 = (long) doc3.getFieldValue("DBID");
        assertEquals(dbid3, NODES[3].getId());
    }

    @Test
    public void testFingerPrint2() throws Exception {
        putHandleDefaults();
        QueryResponse response = query(getDefaultTestClient(), true,
                "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [\"joel\"], \"tenants\": []}",
                params("q", "FINGERPRINT:" + NODES[0].getId() + "_70", "qt", "/afts", "shards.qt", "/afts", "start",
                        "0", "fl", "DBID,score", "rows", "100"));

        SolrDocumentList docs = response.getResults();
        assertEquals(2, docs.getNumFound());
        SolrDocument doc0 = docs.get(0);
        long dbid0 = (long) doc0.getFieldValue("DBID");
        assertEquals(dbid0, NODES[0].getId());

        SolrDocument doc1 = docs.get(1);
        long dbid1 = (long) doc1.getFieldValue("DBID");
        assertEquals(dbid1, NODES[2].getId());
    }

    @Test
    public void testFingerPrint3() throws Exception {
        putHandleDefaults();
        QueryResponse response = query(getDefaultTestClient(), true,
                "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [\"joel\"], \"tenants\": []}",
                params("q", "FINGERPRINT:" + NODES[0].getId() + "_45", "qt", "/afts", "shards.qt", "/afts", "start",
                        "0", "fl", "DBID,score", "rows", "100"));

        SolrDocumentList docs = response.getResults();
        assertEquals(3, docs.getNumFound());
        SolrDocument doc0 = docs.get(0);
        long dbid0 = (long) doc0.getFieldValue("DBID");
        assertEquals(dbid0, NODES[0].getId());

        SolrDocument doc1 = docs.get(1);
        long dbid1 = (long) doc1.getFieldValue("DBID");
        assertEquals(dbid1, NODES[2].getId());

        SolrDocument doc2 = docs.get(2);
        long dbid2 = (long) doc2.getFieldValue("DBID");
        assertEquals(dbid2, NODES[1].getId());
    }

    @Test
    public void testFingerPrint4() throws Exception {
        putHandleDefaults();
        QueryResponse response = query(getDefaultTestClient(), true,
                "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [\"joel\"], \"tenants\": []}",
                params("q", "FINGERPRINT:" + NODES_METADATA[0].getNodeRef().getId(), "qt", "/afts", "shards.qt",
                        "/afts", "start", "0", "fl", "DBID,score", "rows", "100"));

        SolrDocumentList docs = response.getResults();
        assertEquals(4, docs.getNumFound());
        SolrDocument doc0 = docs.get(0);
        long dbid0 = (long) doc0.getFieldValue("DBID");
        assertEquals(dbid0, NODES[0].getId());

        SolrDocument doc1 = docs.get(1);
        long dbid1 = (long) doc1.getFieldValue("DBID");
        assertEquals(dbid1, NODES[2].getId());

        SolrDocument doc2 = docs.get(2);
        long dbid2 = (long) doc2.getFieldValue("DBID");
        assertEquals(dbid2, NODES[1].getId());

        SolrDocument doc3 = docs.get(3);
        long dbid3 = (long) doc3.getFieldValue("DBID");
        assertEquals(dbid3, NODES[3].getId());
    }

    @Test
    public void testFingerPrint5() throws Exception {
        putHandleDefaults();
        QueryResponse response = query(getDefaultTestClient(), true,
                "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [\"joel\"], \"tenants\": []}",
                params("q", "FINGERPRINT:" + NODES_METADATA[0].getNodeRef().getId() + "_70", "qt", "/afts",
                        "shards.qt", "/afts", "start", "0", "fl", "DBID,score", "rows", "100"));

        SolrDocumentList docs = response.getResults();
        assertEquals(2, docs.getNumFound());
        SolrDocument doc0 = docs.get(0);
        long dbid0 = (long) doc0.getFieldValue("DBID");
        assertEquals(dbid0, NODES[0].getId());

        SolrDocument doc1 = docs.get(1);
        long dbid1 = (long) doc1.getFieldValue("DBID");
        assertEquals(dbid1, NODES[2].getId());
    }

    @Test
    public void testFingerPrint6() throws Exception {
        putHandleDefaults();
        QueryResponse response = query(getDefaultTestClient(), true,
                "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [\"joel\"], \"tenants\": []}",
                params("q", "FINGERPRINT:" + NODES_METADATA[0].getNodeRef().getId() + "_45", "qt", "/afts",
                        "shards.qt", "/afts", "start", "0", "fl", "DBID,score", "rows", "100"));

        SolrDocumentList docs = response.getResults();
        assertEquals(3, docs.getNumFound());
        SolrDocument doc0 = docs.get(0);
        long dbid0 = (long) doc0.getFieldValue("DBID");
        assertEquals(dbid0, NODES[0].getId());

        SolrDocument doc1 = docs.get(1);
        long dbid1 = (long) doc1.getFieldValue("DBID");
        assertEquals(dbid1, NODES[2].getId());

        SolrDocument doc2 = docs.get(2);
        long dbid2 = (long) doc2.getFieldValue("DBID");
        assertEquals(dbid2, NODES[1].getId());
    }

    @Test
    public void testFingerprintStillExistsAfterNodeMetadataUpdate() throws Exception {
        putHandleDefaults();

        Transaction txn = getTransaction(0, 1);
        Node fileNode = getNode(txn, ACL, Node.SolrApiNodeStatus.UPDATED);
        NodeMetaData fileMetaData = getNodeMetaData(fileNode, txn, ACL, "mike", null, false);

        indexTransaction(txn, singletonList(fileNode), singletonList(fileMetaData),
                singletonList("This is a text content which is longer than the default hello world "
                        + fileNode.getId()
                        + " returned by the Mock SOLRAPIQueueClient. This is needed because the \"min_hash\" field type "
                        + "definition in Solr doesn't take in account fields which produce less than 5 tokens (see the "
                        + "ShingleFilter settings)."));

        makeSureContentNodeHasBeenIndexed(fileNode, "mike", "longer");

        QueryResponse response = query(getDefaultTestClient(), true,
                "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [\"joel\"], \"tenants\": []}",
                params("q", "FINGERPRINT:" + fileMetaData.getNodeRef().getId(), "qt", "/afts", "shards.qt", "/afts",
                        "start", "0", "fl", "DBID,score", "rows", "100"));

        SolrDocumentList docs = response.getResults();
        assertEquals(1, docs.getNumFound());
        assertEquals(fileNode.getId(), docs.iterator().next().getFieldValue("DBID"));

        // Let's update the test node
        fileMetaData.setOwner("Andrea");
        fileMetaData.getProperties().put(ContentModel.PROP_TITLE,
                new StringPropertyValue("This is the new file \"title\" metadata attribute."));

        txn = getTransaction(0, 1);

        indexTransaction(txn, singletonList(fileNode), singletonList(fileMetaData));

        makeSureContentNodeHasBeenIndexed(fileNode, "Andrea", "longer");

        response = query(getDefaultTestClient(), true,
                "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [\"joel\"], \"tenants\": []}",
                params("q", "FINGERPRINT:" + fileMetaData.getNodeRef().getId(), "qt", "/afts", "shards.qt", "/afts",
                        "start", "0", "fl", "DBID,score", "rows", "100"));
        docs = response.getResults();
        assertEquals(1, docs.getNumFound());
        assertEquals(fileNode.getId(), docs.iterator().next().getFieldValue("DBID"));
    }

    /**
     * Queries the index using a token from the (dummy) text produced by the test framework ("world", actually).
     * Once the query returns a positive result we are sure the ContentTracker
     *
     * <ol>
     *     <li>
     *         Fetched the text content associated with the current node, from Alfresco
     *     </li>
     *     <li>
     *         Computed a fingerprint (using the retrieved text) for the node
     *     </li>
     *     <li>
     *         Updated the node definition in the (Solr)ContentStore and in Solr
     *     </li>
     * </ol>
     *
     * Last but not least, we are also making sure that CommitTracker executed its cycle as well (otherwise document
     * wouldn't be searchable).
     *
     * @param node an addition term which will be appended as a required clause in the executed query.
     * @param testTerm a term which is supposed to be in the indexed content
     * @param owner the #FIELD_OWNER which will be used as an additional required query clause.
     * @throws Exception in case the MAX_WAIT_TIME is reached and the node is not in results.
     */
    private void makeSureContentNodeHasBeenIndexed(final Node node, final String owner, String testTerm)
            throws Exception {
        waitForDocCount(new TermQuery(new Term(QueryConstants.FIELD_READER, "jim")), 1, MAX_WAIT_TIME);
        waitForDocCount(
                new TermQuery(
                        new Term("content@s___t@{http://www.alfresco.org/model/content/1.0}content", testTerm)),
                1, MAX_WAIT_TIME);

        waitForDocCount(new TermQuery(new Term("content@s___t@{http://www.alfresco.org/model/content/1.0}content",
                Long.toString(node.getId()))), 1, MAX_WAIT_TIME);

        BooleanQuery.Builder builder = new BooleanQuery.Builder();
        builder.add(new BooleanClause(
                new TermQuery(
                        new Term("content@s___t@{http://www.alfresco.org/model/content/1.0}content", testTerm)),
                BooleanClause.Occur.MUST));
        builder.add(new BooleanClause(new TermQuery(new Term(QueryConstants.FIELD_OWNER, owner)),
                BooleanClause.Occur.MUST));
        waitForDocCount(builder.build(), 1, MAX_WAIT_TIME);
    }
}